diff --git a/.editorconfig b/.editorconfig index 1c514dae..5969a198 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ [*] -max_line_length = 140 +max_line_length = off [*.{kt, kts}] disabled_rules = import-ordering, no-wildcard-imports \ No newline at end of file diff --git a/.github/workflows/pr_builder.yml b/.github/workflows/pr_builder.yml index 6b446544..7b9fabf8 100644 --- a/.github/workflows/pr_builder.yml +++ b/.github/workflows/pr_builder.yml @@ -43,6 +43,36 @@ jobs: - name: Touch local properties run: touch local.properties + - name: Access KAKAO_NATIVE_KEY + env: + KAKAO_NATIVE_KEY: ${{ secrets.KAKAO_NATIVE_KEY }} + run: echo "KAKAO_NATIVE_KEY=\"$KAKAO_NATIVE_KEY\"" >> local.properties + + - name: Access NAVER_CLIENT_ID + env: + NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }} + run: echo "NAVER_CLIENT_ID=\"$NAVER_CLIENT_ID\"" >> local.properties + + - name: Access NAVER_CLIENT_SECRETE + env: + NAVER_CLIENT_SECRETE: ${{ secrets.NAVER_CLIENT_SECRETE }} + run: echo "NAVER_CLIENT_SECRETE=\"$NAVER_CLIENT_SECRETE\"" >> local.properties + + - name: Access KGE_BASE_URL + env: + HFM_BASE_URL: ${{ secrets.KGE_BASE_URL }} + run: echo "KGE_BASE_URL=\"$KGE_BASE_URL\"" >> local.properties + + - name: Access MIXPANEL_PROD_TOKEN + env: + HFM_BASE_URL: ${{ secrets.MIXPANEL_PROD_TOKEN }} + run: echo "MIXPANEL_PROD_TOKEN=\"$MIXPANEL_PROD_TOKEN\"" >> local.properties + + - name: Access MIXPANEL_DEV_TOKEN + env: + HFM_BASE_URL: ${{ secrets.MIXPANEL_DEV_TOKEN }} + run: echo "MIXPANEL_DEV_TOKEN=\"$MIXPANEL_DEV_TOKEN\"" >> local.properties + - name: Lint Check run: ./gradlew ktlintCheck diff --git a/README.md b/README.md index e95550d9..356e3398 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,37 @@ # *๐ŸŒย  Keep Go Eat - AOS ๐ŸŒ* + + IN SOPT 31st APPJAM ํ‚ต๊ณ ์ž‡ Android ํŒ€ ๋ ˆํฌ์ž…๋‹ˆ๋‹ค.
## ๐Ÿฅ—ย *****Project***** - **ํ‚ต๊ณ ์ž‡(Keep-Go-Eat)** -๊ณ„์† ๋จน์–ด๋„ ๊ดœ์ฐฎ์•„. -์ŠคํŠธ๋ ˆ์Šค ๋ฐ›์ง€ ์•Š๊ณ  ์Šค์Šค๋กœ ์กฐ์ ˆํ•˜๋Š” ๋‚˜๋งŒ์˜ ์‹์Šต๊ด€์„ ์ฐพ์•„๊ฐ€๋Š” ๊ธธ, ํ‚ต๊ณ ์ž‡ +image + + +์ข‹์€ ์Œ์‹์€ ๋” ๋จน๊ณ , ๋‚˜์œ ์Œ์‹์€ ๋œ ๋จน๋Š” ๋‚ ์„ ๋Š˜๋ ค๊ฐ€๋„๋ก ๋•๋Š” +์ฆ๊ฑฐ์šด ์‹์Šต๊ด€ ๊ธฐ๋ก ์„œ๋น„์Šค, ํ‚ต๊ณ ์ž‡ + +
+ +## ๐Ÿ“ท *****Screen***** +| | | | | +| :---: | :---: | :---: |:------------------------------------------------------------------------------------------------------------------------------:| +|`LoginView`|`HomeView`|`HomeView`| `GoalDetailView` | +| | | | +|`GoalDetailView`|`GoalSettingView`|`MyPageView`| |
## ๐Ÿ‘‹ *****Contributors***** -| [@YuBeen-Park](https://github.com/YuBeen-Park) | [@Dani43](https://github.com/Dan2dani) | [@youngjinc](https://github.com/youngjinc) | -| :---: | :---: | :---: | -|||| -|`HomeView`|`LoginView`, `OnBoardingView`|`GoalSettingView`, `GoalDetailView`| +| [@YuBeen-Park](https://github.com/YuBeen-Park) | [@Dani43](https://github.com/Dan2dani) | [@youngjinc](https://github.com/youngjinc) | +|:------------------------------------------------------------------------------------------------------------------------------:| :---: | :---: | +| ||| +| `HomeView`, `MyPageView` |`LoginView`, `OnBoardingView`|`GoalSettingView`, `GoalDetailView`|
@@ -29,7 +42,7 @@ IN SOPT 31st APPJAM ํ‚ต๊ณ ์ž‡ Android ํŒ€ ๋ ˆํฌ์ž…๋‹ˆ๋‹ค. | Jetpack Components | encryptedsharedpreferences, LiveData, Lifecycle, ViewModel, DataBinding | | Dependency Injection | Hilt | | Network | Retrofit, OkHttp | -| Asynchronous Processing | Coroutine | +| Asynchronous Processing | Coroutine(+ Flow) | | Third Party Library | Coil, Social Login, Firebase, Lottie, Timber, kotlinSerialization | | Strategy | Git Flow | | CI | GitHub Action(KtLint, Complie Check) | @@ -78,4 +91,4 @@ IN SOPT 31st APPJAM ํ‚ต๊ณ ์ž‡ Android ํŒ€ ๋ ˆํฌ์ž…๋‹ˆ๋‹ค. โ”ฃ ๐Ÿ“‚ util โ”ƒ โ”ฃ ๐Ÿ“‚ binding โ”ƒ โ”ฃ ๐Ÿ“‚ extension -``` \ No newline at end of file +``` diff --git a/app/build.gradle b/app/build.gradle index 365dd5fa..3d7c331a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,16 +21,28 @@ android { applicationId "org.keepgoeat" minSdk 28 targetSdk 33 - versionCode 1 - versionName "1.0" + versionCode 16 + versionName "1.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + buildConfigField "String", "KGE_BASE_URL", properties["KGE_BASE_URL"] + buildConfigField "String", "KAKAO_NATIVE_KEY", properties["KAKAO_NATIVE_KEY"] + buildConfigField "String", "NAVER_CLIENT_ID", properties["NAVER_CLIENT_ID"] + buildConfigField "String", "NAVER_CLIENT_SECRETE", properties["NAVER_CLIENT_SECRETE"] } buildTypes { - release { + debug { + manifestPlaceholders = [KAKAO_NATIVE_KEY: properties["KAKAO_NATIVE_KEY"]] minifyEnabled false + buildConfigField "String", "MIXPANEL_PROJECT_TOKEN", properties["MIXPANEL_DEV_TOKEN"] + } + release { + manifestPlaceholders = [KAKAO_NATIVE_KEY: properties["KAKAO_NATIVE_KEY"]] + minifyEnabled true + shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + buildConfigField "String", "MIXPANEL_PROJECT_TOKEN", properties["MIXPANEL_PROD_TOKEN"] } } compileOptions { @@ -52,6 +64,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'com.google.android.material:material:1.7.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.recyclerview:recyclerview:1.2.0-rc01' // Ktx implementation 'androidx.activity:activity-ktx:1.6.0' @@ -63,6 +76,7 @@ dependencies { implementation "com.google.dagger:hilt-android:$hilt_version" implementation 'androidx.hilt:hilt-navigation-fragment:1.0.0' kapt "com.google.dagger:hilt-compiler:$hilt_version" + kapt "com.google.dagger:dagger-compiler:$hilt_version" // Coil implementation 'io.coil-kt:coil:2.2.2' @@ -77,15 +91,37 @@ dependencies { implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' + implementation 'com.google.code.gson:gson:2.10' // Firebase implementation platform('com.google.firebase:firebase-bom:31.1.1') implementation 'com.google.firebase:firebase-crashlytics-ktx' - implementation 'com.google.firebase:firebase-analytics-ktx' + + // Mixpanel + implementation 'com.mixpanel.android:mixpanel-android:7.+' + + // KakaoSignIn + implementation "com.kakao.sdk:v2-user:2.12.0" + + // NaverSignIn + implementation 'com.navercorp.nid:oauth-jdk8:5.3.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + + // ViewPager2 + implementation 'androidx.viewpager2:viewpager2:1.0.0' + + // Splashscreen + implementation 'androidx.core:core-splashscreen:1.0.0' + + // Lottie + implementation 'com.airbnb.android:lottie:5.2.0' + + // In-App Review + implementation 'com.google.android.play:review:2.0.1' + implementation 'com.google.android.play:review-ktx:2.0.1' } ktlint { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..8ac59583 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,12 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-keep class com.kakao.sdk.**.model.* { ; } +-keep public class com.navercorp.nid.** { public *; } +-keep class * extends com.google.gson.TypeAdapter + +-keepnames class * extends android.os.Parcelable +-keepnames class * extends java.io.Serializable +-keep class org.keepgoeat.data.model.** { *; } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dcd7f02a..e11ca310 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,31 +3,102 @@ xmlns:tools="http://schemas.android.com/tools"> + + + android:name=".presentation.SplashActivity" + android:exported="true" + android:screenOrientation="portrait" + android:theme="@style/Theme.KeepGoEat.Splash" + tools:ignore="LockedOrientationActivity"> - - + android:name=".presentation.home.HomeActivity" + android:exported="true" + android:screenOrientation="portrait" + tools:ignore="LockedOrientationActivity" /> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_keepgoeat-playstore.png b/app/src/main/ic_keepgoeat-playstore.png new file mode 100644 index 00000000..0c5e588e Binary files /dev/null and b/app/src/main/ic_keepgoeat-playstore.png differ diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 00000000..ef839318 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/org/keepgoeat/App.kt b/app/src/main/java/org/keepgoeat/App.kt index 5bcab8fc..1086d8f7 100644 --- a/app/src/main/java/org/keepgoeat/App.kt +++ b/app/src/main/java/org/keepgoeat/App.kt @@ -1,7 +1,10 @@ package org.keepgoeat import android.app.Application +import androidx.appcompat.app.AppCompatDelegate +import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp +import org.keepgoeat.BuildConfig.KAKAO_NATIVE_KEY import org.keepgoeat.util.KGEDebugTree import timber.log.Timber @@ -9,6 +12,8 @@ import timber.log.Timber class App : Application() { override fun onCreate() { super.onCreate() + KakaoSdk.init(this, KAKAO_NATIVE_KEY) if (BuildConfig.DEBUG) Timber.plant(KGEDebugTree()) + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) } } diff --git a/app/src/main/java/org/keepgoeat/data/ApiResponse.kt b/app/src/main/java/org/keepgoeat/data/ApiResponse.kt deleted file mode 100644 index a96b8f2f..00000000 --- a/app/src/main/java/org/keepgoeat/data/ApiResponse.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.keepgoeat.data - -sealed class ApiResponse(val data: T?) { // TODO ์ˆ˜์ • - data class Idle(val datas: T?) : ApiResponse(null) - data class Loading(val datas: T?) : ApiResponse(datas) - data class Success(val datas: T) : ApiResponse(datas) - data class Failure(val throwable: Throwable?) : ApiResponse(null) -} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/local/KGEDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/local/KGEDataSource.kt index 2f9410b5..8b055882 100644 --- a/app/src/main/java/org/keepgoeat/data/datasource/local/KGEDataSource.kt +++ b/app/src/main/java/org/keepgoeat/data/datasource/local/KGEDataSource.kt @@ -1,6 +1,93 @@ package org.keepgoeat.data.datasource.local +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import dagger.hilt.android.qualifiers.ApplicationContext +import org.keepgoeat.BuildConfig +import org.keepgoeat.presentation.type.LoginPlatformType +import org.keepgoeat.util.safeValueOf +import javax.inject.Inject import javax.inject.Singleton @Singleton -class KGEDataSource +class KGEDataSource @Inject constructor(@ApplicationContext context: Context) { + + private val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + private val dataStore: SharedPreferences = + if (BuildConfig.DEBUG) context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE) + else EncryptedSharedPreferences.create( + context, + FILE_NAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + var accessToken: String + set(value) = dataStore.edit { putString(ACCESS_TOKEN, value) } + get() = dataStore.getString( + ACCESS_TOKEN, + "" + ) ?: "" + + var refreshToken: String + set(value) = dataStore.edit { putString(REFRESH_TOKEN, value) } + get() = dataStore.getString( + REFRESH_TOKEN, + "" + ) ?: "" + + var isLogin: Boolean + set(value) = dataStore.edit { putBoolean(IS_LOGIN, value) } + get() = dataStore.getBoolean(IS_LOGIN, false) + + var isClickedOnboardingButton: Boolean + set(value) = dataStore.edit { putBoolean(IS_CLICKED_ONBOARDING_BUTTON, value) } + get() = dataStore.getBoolean(IS_CLICKED_ONBOARDING_BUTTON, false) + + var loginPlatform: LoginPlatformType + set(value) = dataStore.edit { putString(LOGIN_PLATFORM, value.name) } + get() = safeValueOf(dataStore.getString(LOGIN_PLATFORM, "")) + ?: LoginPlatformType.NONE + + var userEmail: String + set(value) = dataStore.edit { putString(USER_EMAIL, value) } + get() = dataStore.getString(USER_EMAIL, "") ?: "" + + var userName: String + set(value) = dataStore.edit { putString(USER_NAME, value) } + get() = dataStore.getString(USER_NAME, "") ?: "" + + /** ๋กœ๊ทธ์•„์›ƒ ๋ฐ ํšŒ์› ํƒˆํ‡ด ์‹œ ์œ ์ €์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ•จ์ˆ˜ (๋‹จ, ๋กœ๊ทธ์•„์›ƒ์˜ ๊ฒฝ์šฐ IS_CLICKED_ONBOARDING_BUTTON์€ ์ œ์™ธํ•˜๊ณ  ์‚ญ์ œํ•œ๋‹ค. ์žฌ๋กœ๊ทธ์ธ ์‹œ ์˜จ๋ณด๋”ฉ ๋„์šฐ๊ธฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ)*/ + fun clear(isWithdrawal: Boolean = false) { + dataStore.edit { + if (isWithdrawal) { + clear() + } else { + remove(ACCESS_TOKEN) + remove(REFRESH_TOKEN) + remove(IS_LOGIN) + remove(LOGIN_PLATFORM) + remove(USER_NAME) + remove(USER_EMAIL) + } + } + } + + companion object { + const val FILE_NAME = "signSharedPreferences" + const val ACCESS_TOKEN = "accessToken" + const val REFRESH_TOKEN = "refreshToken" + const val IS_LOGIN = "isLogin" + const val IS_CLICKED_ONBOARDING_BUTTON = "isClickedOnboardingButton" + const val LOGIN_PLATFORM = "loginPlatform" + const val USER_EMAIL = "userEmail" + const val USER_NAME = "userName" + } +} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/remote/AuthDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/remote/AuthDataSource.kt new file mode 100644 index 00000000..39a66d25 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/datasource/remote/AuthDataSource.kt @@ -0,0 +1,21 @@ +package org.keepgoeat.data.datasource.remote + +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.data.model.request.RequestAuth +import org.keepgoeat.data.model.response.ResponseAuth +import org.keepgoeat.data.model.response.ResponseRefresh +import org.keepgoeat.data.model.response.ResponseWithdraw +import org.keepgoeat.data.service.AuthService +import javax.inject.Inject + +class AuthDataSource @Inject constructor( + private val authService: AuthService, + private val kgeDataSource: KGEDataSource, +) { + suspend fun login(requestAuth: RequestAuth): ResponseAuth = authService.login(requestAuth) + + suspend fun refresh(): ResponseRefresh = authService.refresh() + + suspend fun deleteAccount(): ResponseWithdraw = + authService.deleteAccount(kgeDataSource.loginPlatform.name) +} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/remote/DummyDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/remote/DummyDataSource.kt deleted file mode 100644 index 2eb4c1d5..00000000 --- a/app/src/main/java/org/keepgoeat/data/datasource/remote/DummyDataSource.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.keepgoeat.data.datasource.remote - -import org.keepgoeat.data.model.request.RequestData -import org.keepgoeat.data.model.response.ResponseResult -import org.keepgoeat.data.service.DummyService -import javax.inject.Inject - -class DummyDataSource @Inject constructor( - private val dummyService: DummyService, -) { - suspend fun uploadDummy(signInRequest: RequestData): ResponseResult { - // dummyService.uploadDummy(signInRequest) // TODO api ์š”์ฒญํ•˜๊ธฐ - return ResponseResult( - 1, - "success", - ResponseResult.ResponseData(1, "keepgoeat", "keepgoeat@gmamil.com") - ) - } -} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/remote/GoalDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/remote/GoalDataSource.kt new file mode 100644 index 00000000..996afbf3 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/datasource/remote/GoalDataSource.kt @@ -0,0 +1,40 @@ +package org.keepgoeat.data.datasource.remote + +import org.keepgoeat.data.model.request.RequestGoalAchievement +import org.keepgoeat.data.model.request.RequestGoalContent +import org.keepgoeat.data.model.request.RequestEditedGoalContent +import org.keepgoeat.data.model.response.* +import org.keepgoeat.data.service.GoalService +import javax.inject.Inject + +class GoalDataSource @Inject constructor( + private val goalService: GoalService, +) { + suspend fun fetchHomeContent(): ResponseHomeContent = + goalService.fetchHomeContent() + + suspend fun fetchGoalDetail(id: Int): ResponseGoalDetail = goalService.fetchGoalDetail(id) + + suspend fun fetchArchivedGoal(sortType: String): ResponseArchivedGoal = + goalService.fetchArchivedGoal(sortType) + + suspend fun uploadGoalContent(content: RequestGoalContent): ResponseGoalContent = + goalService.uploadGoalContent(content) + + suspend fun editGoalContent( + id: Int, + content: RequestEditedGoalContent, + ): ResponseGoalContent = goalService.editGoalContent(id, content) + + suspend fun keepGoal(id: Int): ResponseGoalKeep = goalService.keepGoal(id) + + suspend fun achieveGoal( + id: Int, + requestGoalAchievement: RequestGoalAchievement + ): ResponseGoalAchievement = + goalService.achieveGoal(id, requestGoalAchievement) + + suspend fun deleteGoal( + id: Int + ): ResponseGoalDeleted = goalService.deleteGoal(id) +} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/remote/UserDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/remote/UserDataSource.kt new file mode 100644 index 00000000..e4828382 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/datasource/remote/UserDataSource.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.data.datasource.remote + +import org.keepgoeat.data.model.response.ResponseUserInfo +import org.keepgoeat.data.service.UserService +import javax.inject.Inject + +class UserDataSource @Inject constructor( + private val userService: UserService, +) { + suspend fun fetchUserInfo(): ResponseUserInfo = + userService.fetchUserInfo() +} diff --git a/app/src/main/java/org/keepgoeat/data/datasource/remote/VersionDataSource.kt b/app/src/main/java/org/keepgoeat/data/datasource/remote/VersionDataSource.kt new file mode 100644 index 00000000..a7365d1f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/datasource/remote/VersionDataSource.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.data.datasource.remote + +import org.keepgoeat.data.model.response.ResponseVersion +import org.keepgoeat.data.service.VersionService +import javax.inject.Inject + +class VersionDataSource @Inject constructor( + private val versionService: VersionService, +) { + suspend fun getForcedUpdateVersion(clientType: String): ResponseVersion = + versionService.getForcedUpdateVersion(clientType) +} diff --git a/app/src/main/java/org/keepgoeat/data/interceptor/AuthInterceptor.kt b/app/src/main/java/org/keepgoeat/data/interceptor/AuthInterceptor.kt new file mode 100644 index 00000000..df80c936 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/interceptor/AuthInterceptor.kt @@ -0,0 +1,79 @@ +package org.keepgoeat.data.interceptor + +import android.app.Application +import android.content.Intent +import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import org.keepgoeat.BuildConfig +import org.keepgoeat.R +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.data.model.response.ResponseRefresh +import org.keepgoeat.util.extension.showToast +import javax.inject.Inject + +class AuthInterceptor @Inject constructor( + private val localStorage: KGEDataSource, + private val gson: Gson, + private val context: Application, +) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + val authRequest = originalRequest.newAuthBuilder().build() + val response = chain.proceed(authRequest) + + when (response.code) { + 401 -> { + val refreshTokenRequest = originalRequest.newBuilder().get() + .url("${BuildConfig.KGE_BASE_URL}auth/refresh") + .addHeader(ACCESS_TOKEN, localStorage.accessToken) + .addHeader(REFRESH_TOKEN, localStorage.refreshToken) + .build() + val refreshTokenResponse = chain.proceed(refreshTokenRequest) + + if (refreshTokenResponse.isSuccessful) { + val responseRefresh = gson.fromJson( + refreshTokenResponse.body?.string(), + ResponseRefresh::class.java + ) + with(localStorage) { + accessToken = responseRefresh.data.accessToken + refreshToken = responseRefresh.data.refreshToken + } + val newRequest = originalRequest.newAuthBuilder().build() + return chain.proceed(newRequest) + } else { + with(context) { + CoroutineScope(Dispatchers.Main).launch { + startActivity( + Intent.makeRestartActivityTask( + packageManager.getLaunchIntentForPackage(packageName)?.component + ) + ) + showToast(getString(R.string.auto_login_failure)) + localStorage.clear() + cancel() + } + } + } + } + } + return response + } + + private fun Request.newAuthBuilder() = + this.newBuilder() + .addHeader(ACCESS_TOKEN, localStorage.accessToken) + .addHeader(REFRESH_TOKEN, localStorage.refreshToken) + + companion object { + private const val ACCESS_TOKEN = "accesstoken" + private const val REFRESH_TOKEN = "refreshtoken" + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/request/RequestAuth.kt b/app/src/main/java/org/keepgoeat/data/model/request/RequestAuth.kt new file mode 100644 index 00000000..47936aac --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/request/RequestAuth.kt @@ -0,0 +1,9 @@ +package org.keepgoeat.data.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RequestAuth( + val platformAccessToken: String, + val platform: String, +) diff --git a/app/src/main/java/org/keepgoeat/data/model/request/RequestEditedGoalContent.kt b/app/src/main/java/org/keepgoeat/data/model/request/RequestEditedGoalContent.kt new file mode 100644 index 00000000..b8bb82a5 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/request/RequestEditedGoalContent.kt @@ -0,0 +1,9 @@ +package org.keepgoeat.data.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RequestEditedGoalContent( + val food: String, + val criterion: String +) diff --git a/app/src/main/java/org/keepgoeat/data/model/request/RequestData.kt b/app/src/main/java/org/keepgoeat/data/model/request/RequestGoalAchievement.kt similarity index 59% rename from app/src/main/java/org/keepgoeat/data/model/request/RequestData.kt rename to app/src/main/java/org/keepgoeat/data/model/request/RequestGoalAchievement.kt index 10f5cc49..74c704a5 100644 --- a/app/src/main/java/org/keepgoeat/data/model/request/RequestData.kt +++ b/app/src/main/java/org/keepgoeat/data/model/request/RequestGoalAchievement.kt @@ -3,7 +3,6 @@ package org.keepgoeat.data.model.request import kotlinx.serialization.Serializable @Serializable -data class RequestData( - val name: String, - val email: String, +data class RequestGoalAchievement( + val isAchieved: Boolean ) diff --git a/app/src/main/java/org/keepgoeat/data/model/request/RequestGoalContent.kt b/app/src/main/java/org/keepgoeat/data/model/request/RequestGoalContent.kt new file mode 100644 index 00000000..c6a8f44b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/request/RequestGoalContent.kt @@ -0,0 +1,10 @@ +package org.keepgoeat.data.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RequestGoalContent( + val food: String, + val criterion: String, + val isMore: Boolean, +) diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseArchivedGoal.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseArchivedGoal.kt new file mode 100644 index 00000000..7f40048b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseArchivedGoal.kt @@ -0,0 +1,44 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable +import org.keepgoeat.domain.model.ArchivedGoal +import org.keepgoeat.presentation.type.EatingType + +@Serializable +data class ResponseArchivedGoal( + val status: Int, + val success: Boolean, + val message: String, + val data: ResponseArchivedGoalData, +) { + @Serializable + data class ResponseArchivedGoalData( + val goals: List, + val goalCount: Int, + ) { + @Serializable + data class GoalInfo( + val goalId: Int, + val food: String, + val criterion: String, + val isMore: Boolean, + val isOngoing: Boolean, + val writerId: Int, + val totalCount: Int, + val startedAt: String, + val keptAt: String?, + val isAchieved: Boolean, + ) + + fun toMyGoal() = goals.map { goal -> + ArchivedGoal( + goal.goalId, + goal.food, + if (goal.isMore) EatingType.MORE else EatingType.LESS, + goal.startedAt, + goal.keptAt, + goal.totalCount + ) + } + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseAuth.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseAuth.kt new file mode 100644 index 00000000..6eee21ac --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseAuth.kt @@ -0,0 +1,29 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable +import org.keepgoeat.domain.model.AuthInfo +import org.keepgoeat.domain.type.SignType.SIGN_IN +import org.keepgoeat.domain.type.SignType.SIGN_UP + +@Serializable +data class ResponseAuth( + val data: ResponseAuthData?, + val message: String, + val status: Int, + val success: Boolean, +) { + @Serializable + data class ResponseAuthData( + val type: String, + val email: String, + val accessToken: String, + val refreshToken: String, + ) { + fun toAuthInfo() = AuthInfo( + if (type == SIGN_UP.typeStr) SIGN_UP else SIGN_IN, + email, + accessToken, + refreshToken + ) + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalAchievement.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalAchievement.kt new file mode 100644 index 00000000..ff8aeced --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalAchievement.kt @@ -0,0 +1,18 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGoalAchievement( + val data: ResponseGoalAchievementData, + val message: String, + val status: Int, + val success: Boolean +) { + @Serializable + data class ResponseGoalAchievementData( + val goalId: Int, + val thisMonthCount: Int, + val updatedIsAchieved: Boolean + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalContent.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalContent.kt new file mode 100644 index 00000000..d3ee7645 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalContent.kt @@ -0,0 +1,18 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGoalContent( + val status: Int, + val success: Boolean, + val message: String, + val data: ResponseGoalContentData, +) { + @Serializable + data class ResponseGoalContentData( + @SerialName("goalId") + val id: Int, + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDeleted.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDeleted.kt new file mode 100644 index 00000000..7b1359b1 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDeleted.kt @@ -0,0 +1,16 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGoalDeleted( + val data: ResponseGoalDeletedData, + val message: String, + val status: Int, + val success: Boolean +) { + @Serializable + data class ResponseGoalDeletedData( + val goalId: Int + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDetail.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDetail.kt new file mode 100644 index 00000000..89ae7c92 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalDetail.kt @@ -0,0 +1,36 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.keepgoeat.domain.model.GoalDetail +import org.keepgoeat.presentation.type.EatingType + +@Serializable +data class ResponseGoalDetail( + val status: Int, + val success: Boolean, + val message: String, + val data: ResponseGoalDetailData, +) { + @Serializable + data class ResponseGoalDetailData( + @SerialName("goalId") + val id: Int, + val isMore: Boolean, + val thisMonthCount: Int, + val lastMonthCount: Int, + val blankBoxCount: Int, + val emptyBoxCount: Int, + val food: String, + val criterion: String + ) { + fun toGoalDetail() = GoalDetail( + id = id, + eatingType = if (isMore) EatingType.MORE else EatingType.LESS, + thisMonthCount = thisMonthCount, + lastMonthCount = lastMonthCount, + food = food, + criterion = criterion + ) + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalKeep.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalKeep.kt new file mode 100644 index 00000000..3d9dddea --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseGoalKeep.kt @@ -0,0 +1,16 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGoalKeep( + val data: ResponseGoalKeepData, + val message: String, + val status: Int, + val success: Boolean +) { + @Serializable + data class ResponseGoalKeepData( + val goalId: Int + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseHomeContent.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseHomeContent.kt new file mode 100644 index 00000000..ba63f3dd --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseHomeContent.kt @@ -0,0 +1,52 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable +import org.keepgoeat.domain.model.HomeContent +import org.keepgoeat.domain.model.HomeGoal +import org.keepgoeat.presentation.type.HomeGoalViewType + +@Serializable +data class ResponseHomeContent( + val data: HomeData, + val message: String, + val status: Int, + val success: Boolean, +) { + @Serializable + data class HomeData( + val cheeringMessage: String, + val daytime: Int, + val goalCount: Int, + val goals: List, + ) { + @Serializable + data class Goal( + val food: String, + val criterion: String, + val goalId: Int, + val isAchieved: Boolean, + val isMore: Boolean, + val isOngoing: Boolean, + val keptAt: String, + val startedAt: String, + val thisMonthCount: Int, + val totalCount: Int, + val writerId: Int, + ) + + fun toHomeContent() = HomeContent( + cheeringMessage.replace("\\n", "\n"), + goals.map { goal -> + HomeGoal( + goal.goalId, + goal.food, + goal.criterion, + goal.isMore, + goal.isAchieved, + goal.thisMonthCount, + HomeGoalViewType.MY_GOAL_TYPE + ) + } + ) + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseRefresh.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseRefresh.kt new file mode 100644 index 00000000..4fd4f317 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseRefresh.kt @@ -0,0 +1,17 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseRefresh( + val status: Int, + val success: Boolean, + val message: String, + val data: ResponseToken, +) { + @Serializable + data class ResponseToken( + val accessToken: String, + val refreshToken: String + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseResult.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseResult.kt deleted file mode 100644 index 03764215..00000000 --- a/app/src/main/java/org/keepgoeat/data/model/response/ResponseResult.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.keepgoeat.data.model.response - -import kotlinx.serialization.Serializable -import org.keepgoeat.domain.model.DummyData - -@Serializable -data class ResponseResult( - val status: Int, - val message: String, - val data: ResponseData, -) { - @Serializable - data class ResponseData( // TODO ResponseUser๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ํŒŒ์ผ ๋ถ„๋ฆฌํ•˜๊ธฐ - val id: Int, - val name: String, - val email: String, - ) { - fun toDummyData() = DummyData( - id = id.toString(), - email = email, - name = name - ) - } -} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseUserInfo.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseUserInfo.kt new file mode 100644 index 00000000..682969e3 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseUserInfo.kt @@ -0,0 +1,21 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable +import org.keepgoeat.domain.model.UserInfo + +@Serializable +data class ResponseUserInfo( + val data: ResponseUserData?, + val message: String, + val status: Int, + val success: Boolean, +) { + @Serializable + data class ResponseUserData( + val name: String, + val email: String, + val keptGoalsCount: Int, + ) { + fun toUserInfo() = UserInfo(name, email, keptGoalsCount) + } +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseVersion.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseVersion.kt new file mode 100644 index 00000000..7d8ffb93 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseVersion.kt @@ -0,0 +1,16 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseVersion( + val status: Int, + val success: Boolean, + val message: String, + val data: VersionData +) { + @Serializable + data class VersionData( + val version: String + ) +} diff --git a/app/src/main/java/org/keepgoeat/data/model/response/ResponseWithdraw.kt b/app/src/main/java/org/keepgoeat/data/model/response/ResponseWithdraw.kt new file mode 100644 index 00000000..34186159 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/model/response/ResponseWithdraw.kt @@ -0,0 +1,10 @@ +package org.keepgoeat.data.model.response + +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseWithdraw( + val message: String, + val status: Int, + val success: Boolean, +) diff --git a/app/src/main/java/org/keepgoeat/data/repository/AuthRepositoryImpl.kt b/app/src/main/java/org/keepgoeat/data/repository/AuthRepositoryImpl.kt new file mode 100644 index 00000000..5abd041f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/repository/AuthRepositoryImpl.kt @@ -0,0 +1,50 @@ +package org.keepgoeat.data.repository + +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.data.datasource.remote.AuthDataSource +import org.keepgoeat.data.model.request.RequestAuth +import org.keepgoeat.data.model.response.ResponseRefresh +import org.keepgoeat.data.model.response.ResponseWithdraw +import org.keepgoeat.domain.model.AuthInfo +import org.keepgoeat.domain.repository.AuthRepository +import org.keepgoeat.domain.type.SignType +import timber.log.Timber +import javax.inject.Inject + +class AuthRepositoryImpl @Inject constructor( + private val authDataSource: AuthDataSource, + private val localStorage: KGEDataSource, +) : AuthRepository { + override suspend fun login(requestAuth: RequestAuth): Result = + runCatching { + authDataSource.login(requestAuth).data?.toAuthInfo() + }.onSuccess { + with(localStorage) { + isLogin = true + it?.let { + accessToken = it.accessToken + refreshToken = it.refreshToken + + if (it.signType == SignType.SIGN_IN) + isClickedOnboardingButton = true + } + } + }.onFailure { + Timber.e(it.message) + } + + override suspend fun deleteAccount(): Result = + runCatching { + authDataSource.deleteAccount() + }.onSuccess { + Timber.d("ํšŒ์› ํƒˆํ‡ด ์„ฑ๊ณต") + localStorage.clear(true) + }.onFailure { + Timber.e(it.message) + } + + override suspend fun refresh(): Result = + runCatching { + authDataSource.refresh().data + } +} diff --git a/app/src/main/java/org/keepgoeat/data/repository/DummyRepositoryImpl.kt b/app/src/main/java/org/keepgoeat/data/repository/DummyRepositoryImpl.kt deleted file mode 100644 index 3cb6bca3..00000000 --- a/app/src/main/java/org/keepgoeat/data/repository/DummyRepositoryImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.keepgoeat.data.repository - -import org.keepgoeat.data.datasource.remote.DummyDataSource -import org.keepgoeat.data.model.request.RequestData -import org.keepgoeat.domain.repository.DummyRepository -import javax.inject.Inject - -class DummyRepositoryImpl @Inject constructor( - private val dummyDataSource: DummyDataSource, -) : DummyRepository { - override suspend fun uploadDummy(name: String, email: String) { - dummyDataSource.uploadDummy(RequestData(name, email)) - } -} diff --git a/app/src/main/java/org/keepgoeat/data/repository/GoalRepositoryImpl.kt b/app/src/main/java/org/keepgoeat/data/repository/GoalRepositoryImpl.kt new file mode 100644 index 00000000..2d659384 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/repository/GoalRepositoryImpl.kt @@ -0,0 +1,64 @@ +package org.keepgoeat.data.repository + +import org.keepgoeat.data.datasource.remote.GoalDataSource +import org.keepgoeat.data.model.request.RequestGoalAchievement +import org.keepgoeat.data.model.request.RequestGoalContent +import org.keepgoeat.data.model.request.RequestEditedGoalContent +import org.keepgoeat.data.model.response.* +import org.keepgoeat.domain.model.GoalDetail +import org.keepgoeat.domain.model.ArchivedGoal +import org.keepgoeat.domain.model.HomeContent +import org.keepgoeat.domain.repository.GoalRepository +import javax.inject.Inject + +class GoalRepositoryImpl @Inject constructor( + private val goalDataSource: GoalDataSource, +) : GoalRepository { + override suspend fun fetchHomeContent(): Result = runCatching { + goalDataSource.fetchHomeContent().data.toHomeContent() + } + + override suspend fun achieveGoal( + goalId: Int, + isAchieved: Boolean, + ): Result = runCatching { + goalDataSource.achieveGoal(goalId, RequestGoalAchievement(isAchieved)).data + } + + override suspend fun uploadGoalContent( + food: String, + criterion: String, + isMore: Boolean, + ): Result = + runCatching { + goalDataSource.uploadGoalContent(RequestGoalContent(food, criterion, isMore)).data.id + } + + override suspend fun editGoalContent( + id: Int, + food: String, + criterion: String + ): Result = runCatching { + goalDataSource.editGoalContent(id, RequestEditedGoalContent(food, criterion)).data.id + } + + override suspend fun fetchGoalDetail(goalId: Int): Result = + runCatching { + goalDataSource.fetchGoalDetail(goalId).data.toGoalDetail() + } + + override suspend fun fetchArchivedGoal(sortType: String): Result> = + runCatching { + goalDataSource.fetchArchivedGoal(sortType).data.toMyGoal() + } + + override suspend fun keepGoal(id: Int): Result = + runCatching { + goalDataSource.keepGoal(id).data + } + + override suspend fun deleteGoal(id: Int): Result = + runCatching { + goalDataSource.deleteGoal(id).data + } +} diff --git a/app/src/main/java/org/keepgoeat/data/repository/UserRepositoryImpl.kt b/app/src/main/java/org/keepgoeat/data/repository/UserRepositoryImpl.kt new file mode 100644 index 00000000..16fa2259 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/repository/UserRepositoryImpl.kt @@ -0,0 +1,15 @@ +package org.keepgoeat.data.repository + +import org.keepgoeat.data.datasource.remote.UserDataSource +import org.keepgoeat.domain.model.UserInfo +import org.keepgoeat.domain.repository.UserRepository +import javax.inject.Inject + +class UserRepositoryImpl @Inject constructor( + private val userDataSource: UserDataSource, +) : UserRepository { + override suspend fun fetchUserInfo(): Result = + runCatching { + userDataSource.fetchUserInfo().data?.toUserInfo() + } +} diff --git a/app/src/main/java/org/keepgoeat/data/repository/VersionRepositoryImpl.kt b/app/src/main/java/org/keepgoeat/data/repository/VersionRepositoryImpl.kt new file mode 100644 index 00000000..4f816b5c --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/repository/VersionRepositoryImpl.kt @@ -0,0 +1,15 @@ +package org.keepgoeat.data.repository + +import org.keepgoeat.data.datasource.remote.VersionDataSource +import org.keepgoeat.data.model.response.ResponseVersion +import org.keepgoeat.domain.repository.VersionRepository +import javax.inject.Inject + +class VersionRepositoryImpl @Inject constructor( + private val versionDataSource: VersionDataSource +) : VersionRepository { + override suspend fun getForcedUpdateVersion(clientType: String): Result = + runCatching { + versionDataSource.getForcedUpdateVersion(clientType).data + } +} diff --git a/app/src/main/java/org/keepgoeat/data/service/AuthService.kt b/app/src/main/java/org/keepgoeat/data/service/AuthService.kt new file mode 100644 index 00000000..36da12f7 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/AuthService.kt @@ -0,0 +1,21 @@ +package org.keepgoeat.data.service + +import org.keepgoeat.data.model.request.RequestAuth +import org.keepgoeat.data.model.response.ResponseAuth +import org.keepgoeat.data.model.response.ResponseRefresh +import org.keepgoeat.data.model.response.ResponseWithdraw +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query + +interface AuthService { + @POST("auth") + suspend fun login(@Body authInfo: RequestAuth): ResponseAuth + + @GET("auth/refresh") + suspend fun refresh(): ResponseRefresh + + @GET("auth/withdraw") + suspend fun deleteAccount(@Query("code") platform: String): ResponseWithdraw +} diff --git a/app/src/main/java/org/keepgoeat/data/service/DummyService.kt b/app/src/main/java/org/keepgoeat/data/service/DummyService.kt deleted file mode 100644 index c5fa4d07..00000000 --- a/app/src/main/java/org/keepgoeat/data/service/DummyService.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.keepgoeat.data.service - -import org.keepgoeat.data.model.request.RequestData -import org.keepgoeat.data.model.response.ResponseResult -import retrofit2.http.Body -import retrofit2.http.POST - -interface DummyService { - @POST("api/dummy") - suspend fun uploadDummy(@Body request: RequestData): ResponseResult -} diff --git a/app/src/main/java/org/keepgoeat/data/service/GoalService.kt b/app/src/main/java/org/keepgoeat/data/service/GoalService.kt new file mode 100644 index 00000000..4755896e --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/GoalService.kt @@ -0,0 +1,43 @@ +package org.keepgoeat.data.service + +import org.keepgoeat.data.model.request.RequestGoalAchievement +import org.keepgoeat.data.model.request.RequestGoalContent +import org.keepgoeat.data.model.request.RequestEditedGoalContent +import org.keepgoeat.data.model.response.* +import retrofit2.http.* + +interface GoalService { + @GET("home") + suspend fun fetchHomeContent(): ResponseHomeContent + + @POST("goal") + suspend fun uploadGoalContent(@Body goalContent: RequestGoalContent): ResponseGoalContent + + @POST("goal/{goalId}") + suspend fun editGoalContent( + @Path("goalId") id: Int, + @Body goalContent: RequestEditedGoalContent + ): ResponseGoalContent + + @GET("history/{goalId}") + suspend fun fetchGoalDetail(@Path("goalId") id: Int): ResponseGoalDetail + + @GET("goal/kept") + suspend fun fetchArchivedGoal( + @Query("sort") sortType: String, + ): ResponseArchivedGoal + + @POST("goal/keep/{goalId}") + suspend fun keepGoal(@Path("goalId") id: Int): ResponseGoalKeep + + @POST("goal/achieve/{goalId}") + suspend fun achieveGoal( + @Path("goalId") id: Int, + @Body goalAchievement: RequestGoalAchievement + ): ResponseGoalAchievement + + @DELETE("goal/{goalId}") + suspend fun deleteGoal( + @Path("goalId") id: Int + ): ResponseGoalDeleted +} diff --git a/app/src/main/java/org/keepgoeat/data/service/KakaoAuthService.kt b/app/src/main/java/org/keepgoeat/data/service/KakaoAuthService.kt new file mode 100644 index 00000000..d04c3bc2 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/KakaoAuthService.kt @@ -0,0 +1,78 @@ +package org.keepgoeat.data.service + +import android.content.Context +import com.kakao.sdk.auth.model.OAuthToken +import com.kakao.sdk.user.UserApiClient +import dagger.hilt.android.qualifiers.ActivityContext +import org.keepgoeat.domain.model.AccountInfo +import org.keepgoeat.presentation.type.LoginPlatformType +import timber.log.Timber +import javax.inject.Inject + +class KakaoAuthService @Inject constructor( + @ActivityContext private val context: Context, + private val client: UserApiClient, +) { + private val isKakaoTalkLoginAvailable: Boolean + get() = client.isKakaoTalkLoginAvailable(context) + + fun loginKakao( + loginListener: ((LoginPlatformType, String) -> Unit), + accountListener: ((AccountInfo) -> Unit) + ) { + val callback: (OAuthToken?, Throwable?) -> Unit = { token, error -> + if (error != null) handleLoginError(error) + else if (token != null) handleLoginSuccess(token, loginListener, accountListener) + } + + if (isKakaoTalkLoginAvailable) client.loginWithKakaoTalk(context, callback = callback) + else client.loginWithKakaoAccount(context, callback = callback) + } + + private fun handleLoginError(throwable: Throwable) { + val kakaoType = if (isKakaoTalkLoginAvailable) "์นด์นด์˜คํ†ก" else "์นด์นด์˜ค ๊ณ„์ •" + Timber.d("${kakaoType}์œผ๋กœ ๋กœ๊ทธ์ธ ์‹คํŒจ (${throwable.message})") + } + + private fun handleLoginSuccess( + oAuthToken: OAuthToken, + loginListener: ((LoginPlatformType, String) -> Unit), + accountListener: ((AccountInfo) -> Unit), + ) { + client.me { user, error -> + Timber.d(oAuthToken.accessToken) + loginListener(LoginPlatformType.KAKAO, oAuthToken.accessToken) + if (error != null) Timber.e("kakao ์‚ฌ์šฉ์ž ์ •๋ณด ์š”์ฒญ ์‹คํŒจ $error") + else if (user != null) { + accountListener( + AccountInfo( + user.kakaoAccount?.profile?.nickname ?: "", + user.kakaoAccount?.email ?: "" + ) + ) + } + } + } + + fun logoutKakao(logoutListener: (() -> Unit)) { + client.logout { error -> + if (error == null) { + Timber.i("๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต. SDK์—์„œ ํ† ํฐ ์‚ญ์ œ๋จ") + logoutListener() + } else { + Timber.e("๋กœ๊ทธ์•„์›ƒ ์‹คํŒจ. SDK์—์„œ ํ† ํฐ ์‚ญ์ œ๋จ($error)") + } + } + } + + fun deleteAccountKakao(deleteAccountListener: (() -> Unit)) { + client.unlink { error -> + if (error == null) { + Timber.d("์—ฐ๊ฒฐ ๋Š๊ธฐ ์„ฑ๊ณต. SDK์—์„œ ํ† ํฐ ์‚ญ์ œ ๋จ") + deleteAccountListener() + } else { + Timber.e("์—ฐ๊ฒฐ ๋Š๊ธฐ ์‹คํŒจ($error)") + } + } + } +} diff --git a/app/src/main/java/org/keepgoeat/data/service/NaverAuthService.kt b/app/src/main/java/org/keepgoeat/data/service/NaverAuthService.kt new file mode 100644 index 00000000..aff9adbb --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/NaverAuthService.kt @@ -0,0 +1,102 @@ +package org.keepgoeat.data.service + +import android.content.Context +import com.navercorp.nid.NaverIdLoginSDK +import com.navercorp.nid.oauth.NidOAuthLogin +import com.navercorp.nid.oauth.OAuthLoginCallback +import com.navercorp.nid.profile.NidProfileCallback +import com.navercorp.nid.profile.data.NidProfileResponse +import dagger.hilt.android.qualifiers.ActivityContext +import org.keepgoeat.BuildConfig +import org.keepgoeat.domain.model.AccountInfo +import org.keepgoeat.presentation.type.LoginPlatformType +import timber.log.Timber +import javax.inject.Inject + +class NaverAuthService @Inject constructor( + @ActivityContext private val context: Context, +) { + init { + NaverIdLoginSDK.initialize( + context, BuildConfig.NAVER_CLIENT_ID, BuildConfig.NAVER_CLIENT_SECRETE, + CLIENT_NAME + ) + } + + fun loginNaver( + loginListener: ((LoginPlatformType, String) -> Unit), + accountListener: (AccountInfo) -> Unit + ) { + val oauthLoginCallback = object : OAuthLoginCallback { + override fun onSuccess() { + val accessToken = requireNotNull(NaverIdLoginSDK.getAccessToken()) + Timber.d(accessToken) + loginListener(LoginPlatformType.NAVER, accessToken) + getAccountInfo(accountListener) + } + + override fun onFailure(httpStatus: Int, message: String) { + Timber.i(NaverIdLoginSDK.getLastErrorCode().code) + Timber.i(NaverIdLoginSDK.getLastErrorDescription()) + } + + override fun onError(errorCode: Int, message: String) { + onFailure(errorCode, message) + } + } + NaverIdLoginSDK.authenticate( + context, oauthLoginCallback + ) + } + + fun getAccountInfo(accountListener: (AccountInfo) -> Unit) { + NidOAuthLogin().callProfileApi(object : NidProfileCallback { + override fun onSuccess(result: NidProfileResponse) { + accountListener( + AccountInfo( + result.profile?.name ?: "", + result.profile?.email ?: "" + ) + ) + } + + override fun onFailure(httpStatus: Int, message: String) { + Timber.i("Error : $httpStatus, message : $message") + } + + override fun onError(errorCode: Int, message: String) { + onFailure(errorCode, message) + } + }) + } + + fun logoutNaver(logoutListener: (() -> Unit)) { + NaverIdLoginSDK.logout() + logoutListener() + } + + fun deleteAccountNaver(deleteAccountListener: (() -> Unit)) { + NidOAuthLogin().callDeleteTokenApi( + context, + object : OAuthLoginCallback { + override fun onSuccess() { + Timber.d("์—ฐ๊ฒฐ ๋Š๊ธฐ ์„ฑ๊ณต. SDK์—์„œ ํ† ํฐ ์‚ญ์ œ ๋จ") + deleteAccountListener() + } + + override fun onFailure(httpStatus: Int, message: String) { + Timber.d("errorCode: ${NaverIdLoginSDK.getLastErrorCode().code}") + Timber.d("errorDesc: ${NaverIdLoginSDK.getLastErrorDescription()}") + } + + override fun onError(errorCode: Int, message: String) { + onFailure(errorCode, message) + } + } + ) + } + + companion object { + private const val CLIENT_NAME = "ํ‚ต๊ณ ์ž‡" + } +} diff --git a/app/src/main/java/org/keepgoeat/data/service/UserService.kt b/app/src/main/java/org/keepgoeat/data/service/UserService.kt new file mode 100644 index 00000000..1dce3989 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/UserService.kt @@ -0,0 +1,9 @@ +package org.keepgoeat.data.service + +import org.keepgoeat.data.model.response.ResponseUserInfo +import retrofit2.http.GET + +interface UserService { + @GET("mypage") + suspend fun fetchUserInfo(): ResponseUserInfo +} diff --git a/app/src/main/java/org/keepgoeat/data/service/VersionService.kt b/app/src/main/java/org/keepgoeat/data/service/VersionService.kt new file mode 100644 index 00000000..70e6159b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/data/service/VersionService.kt @@ -0,0 +1,13 @@ +package org.keepgoeat.data.service + +import org.keepgoeat.data.model.response.ResponseVersion +import retrofit2.http.GET +import retrofit2.http.Query + +interface VersionService { + /** ์ตœ์‹  ๊ฐ•์ œ ์—…๋ฐ์ดํŠธ ๋ฒ„์ „์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ•จ์ˆ˜ */ + @GET("ver") + suspend fun getForcedUpdateVersion( + @Query("client") client: String, + ): ResponseVersion +} diff --git a/app/src/main/java/org/keepgoeat/di/NetworkModule.kt b/app/src/main/java/org/keepgoeat/di/NetworkModule.kt index 823d5dc0..76419ce8 100644 --- a/app/src/main/java/org/keepgoeat/di/NetworkModule.kt +++ b/app/src/main/java/org/keepgoeat/di/NetworkModule.kt @@ -1,5 +1,7 @@ package org.keepgoeat.di +import com.google.gson.Gson +import com.google.gson.GsonBuilder import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module import dagger.Provides @@ -7,10 +9,13 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor +import org.keepgoeat.BuildConfig import org.keepgoeat.BuildConfig.DEBUG +import org.keepgoeat.data.interceptor.AuthInterceptor import retrofit2.Retrofit import java.util.concurrent.TimeUnit import javax.inject.Singleton @@ -28,27 +33,37 @@ object NetworkModule { ignoreUnknownKeys = true } + @Provides + @Singleton + fun provideGson(): Gson = GsonBuilder().setLenient().create() + @ExperimentalSerializationApi @Provides @Singleton fun provideRetrofit(client: OkHttpClient, json: Json): Retrofit = Retrofit.Builder() - .baseUrl("https://reqres.in/") // TODO BaseUrl ๋ณ€๊ฒฝ ๋ฐ BuildConfig ๋ณ€์ˆ˜ ์‚ฌ์šฉ + .baseUrl(BuildConfig.KGE_BASE_URL) .client(client) .addConverterFactory(json.asConverterFactory(requireNotNull("application/json".toMediaTypeOrNull()))) .build() @Provides @Singleton - fun provideOkHttpClientBuilder(): OkHttpClient = + fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor + + @Provides + @Singleton + fun provideOkHttpClientBuilder( + interceptor: AuthInterceptor, + ): OkHttpClient = OkHttpClient.Builder().apply { connectTimeout(10, TimeUnit.SECONDS) writeTimeout(10, TimeUnit.SECONDS) readTimeout(10, TimeUnit.SECONDS) + addInterceptor(interceptor) if (DEBUG) addInterceptor( HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } ) - // TODO AuthInterceptor ์ถ”๊ฐ€ }.build() } diff --git a/app/src/main/java/org/keepgoeat/di/RepositoryModule.kt b/app/src/main/java/org/keepgoeat/di/RepositoryModule.kt index d3d75e7f..67b8d879 100644 --- a/app/src/main/java/org/keepgoeat/di/RepositoryModule.kt +++ b/app/src/main/java/org/keepgoeat/di/RepositoryModule.kt @@ -4,8 +4,14 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import org.keepgoeat.data.repository.DummyRepositoryImpl -import org.keepgoeat.domain.repository.DummyRepository +import org.keepgoeat.data.repository.AuthRepositoryImpl +import org.keepgoeat.data.repository.GoalRepositoryImpl +import org.keepgoeat.data.repository.UserRepositoryImpl +import org.keepgoeat.data.repository.VersionRepositoryImpl +import org.keepgoeat.domain.repository.AuthRepository +import org.keepgoeat.domain.repository.GoalRepository +import org.keepgoeat.domain.repository.UserRepository +import org.keepgoeat.domain.repository.VersionRepository import javax.inject.Singleton @Module @@ -13,7 +19,25 @@ import javax.inject.Singleton interface RepositoryModule { @Binds @Singleton - fun bindDummyRepository( - dummyRepositoryImpl: DummyRepositoryImpl, - ): DummyRepository + fun bindGoalRepository( + goalRepositoryImpl: GoalRepositoryImpl, + ): GoalRepository + + @Binds + @Singleton + fun bindAuthRepository( + authRepositoryImpl: AuthRepositoryImpl, + ): AuthRepository + + @Binds + @Singleton + fun bindUserRepository( + userRepositoryImpl: UserRepositoryImpl, + ): UserRepository + + @Binds + @Singleton + fun bindVersionRepository( + versionRepositoryImpl: VersionRepositoryImpl + ): VersionRepository } diff --git a/app/src/main/java/org/keepgoeat/di/ServiceModule.kt b/app/src/main/java/org/keepgoeat/di/ServiceModule.kt index e1fdd008..489f197b 100644 --- a/app/src/main/java/org/keepgoeat/di/ServiceModule.kt +++ b/app/src/main/java/org/keepgoeat/di/ServiceModule.kt @@ -4,7 +4,10 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import org.keepgoeat.data.service.DummyService +import org.keepgoeat.data.service.AuthService +import org.keepgoeat.data.service.GoalService +import org.keepgoeat.data.service.UserService +import org.keepgoeat.data.service.VersionService import retrofit2.Retrofit import javax.inject.Singleton @@ -13,6 +16,21 @@ import javax.inject.Singleton object ServiceModule { @Singleton @Provides - fun provideDummyService(retrofit: Retrofit): DummyService = - retrofit.create(DummyService::class.java) + fun provideGoalService(retrofit: Retrofit): GoalService = + retrofit.create(GoalService::class.java) + + @Singleton + @Provides + fun provideAuthService(retrofit: Retrofit): AuthService = + retrofit.create(AuthService::class.java) + + @Singleton + @Provides + fun provideUserService(retrofit: Retrofit): UserService = + retrofit.create(UserService::class.java) + + @Singleton + @Provides + fun provideVersionService(retrofit: Retrofit): VersionService = + retrofit.create(VersionService::class.java) } diff --git a/app/src/main/java/org/keepgoeat/di/SignModule.kt b/app/src/main/java/org/keepgoeat/di/SignModule.kt new file mode 100644 index 00000000..dd87c9a6 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/di/SignModule.kt @@ -0,0 +1,31 @@ +package org.keepgoeat.di + +import android.content.Context +import com.kakao.sdk.user.UserApiClient +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.scopes.ActivityScoped +import org.keepgoeat.data.service.KakaoAuthService +import org.keepgoeat.data.service.NaverAuthService + +@Module +@InstallIn(ActivityComponent::class) +object SignModule { + @Provides + @ActivityScoped + fun provideUserApiClient(): UserApiClient = UserApiClient.instance + + @Provides + fun provideKakaoSignService( + @ActivityContext context: Context, + client: UserApiClient, + ) = KakaoAuthService(context, client) + + @Provides + fun provideNaverSignService( + @ActivityContext context: Context, + ) = NaverAuthService(context) +} diff --git a/app/src/main/java/org/keepgoeat/domain/model/DummyData.kt b/app/src/main/java/org/keepgoeat/domain/model/AccountInfo.kt similarity index 66% rename from app/src/main/java/org/keepgoeat/domain/model/DummyData.kt rename to app/src/main/java/org/keepgoeat/domain/model/AccountInfo.kt index 828500d1..692d59c0 100644 --- a/app/src/main/java/org/keepgoeat/domain/model/DummyData.kt +++ b/app/src/main/java/org/keepgoeat/domain/model/AccountInfo.kt @@ -1,7 +1,6 @@ package org.keepgoeat.domain.model -data class DummyData( - val email: String, - val id: String, +data class AccountInfo( val name: String, + val email: String, ) diff --git a/app/src/main/java/org/keepgoeat/domain/model/ArchivedGoal.kt b/app/src/main/java/org/keepgoeat/domain/model/ArchivedGoal.kt new file mode 100644 index 00000000..3559293f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/ArchivedGoal.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.domain.model + +import org.keepgoeat.presentation.type.EatingType + +data class ArchivedGoal( + val id: Int, + val goalContent: String, + val eatingType: EatingType, + val startedAt: String, + val archivedAt: String?, + val totalCount: Int, +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/AuthInfo.kt b/app/src/main/java/org/keepgoeat/domain/model/AuthInfo.kt new file mode 100644 index 00000000..66745241 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/AuthInfo.kt @@ -0,0 +1,10 @@ +package org.keepgoeat.domain.model + +import org.keepgoeat.domain.type.SignType + +data class AuthInfo( + val signType: SignType, + val email: String, + val accessToken: String, + val refreshToken: String, +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/GoalDetail.kt b/app/src/main/java/org/keepgoeat/domain/model/GoalDetail.kt new file mode 100644 index 00000000..2007fe39 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/GoalDetail.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.domain.model + +import org.keepgoeat.presentation.type.EatingType + +data class GoalDetail( + val id: Int, + val eatingType: EatingType, + val thisMonthCount: Int, + val lastMonthCount: Int, + val food: String, + val criterion: String +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/GoalSticker.kt b/app/src/main/java/org/keepgoeat/domain/model/GoalSticker.kt new file mode 100644 index 00000000..29d585ae --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/GoalSticker.kt @@ -0,0 +1,6 @@ +package org.keepgoeat.domain.model + +data class GoalSticker( + val id: Int, + val isDone: Boolean, +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/HomeContent.kt b/app/src/main/java/org/keepgoeat/domain/model/HomeContent.kt new file mode 100644 index 00000000..7ef03c2a --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/HomeContent.kt @@ -0,0 +1,6 @@ +package org.keepgoeat.domain.model + +data class HomeContent( + val cheeringMessage: String, + val goals: List +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/HomeGoal.kt b/app/src/main/java/org/keepgoeat/domain/model/HomeGoal.kt new file mode 100644 index 00000000..4205788a --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/HomeGoal.kt @@ -0,0 +1,13 @@ +package org.keepgoeat.domain.model + +import org.keepgoeat.presentation.type.HomeGoalViewType + +data class HomeGoal( + val id: Int, + val goalTitle: String, + val goalCriterion: String, + val isMore: Boolean, + val isAchieved: Boolean, + val thisMonthCount: Int, + val type: HomeGoalViewType +) diff --git a/app/src/main/java/org/keepgoeat/domain/model/UserInfo.kt b/app/src/main/java/org/keepgoeat/domain/model/UserInfo.kt new file mode 100644 index 00000000..6762a75d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/model/UserInfo.kt @@ -0,0 +1,11 @@ +package org.keepgoeat.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class UserInfo( + val name: String, + val email: String, + val archivedGoalCount: Int, +) : Parcelable diff --git a/app/src/main/java/org/keepgoeat/domain/repository/AuthRepository.kt b/app/src/main/java/org/keepgoeat/domain/repository/AuthRepository.kt new file mode 100644 index 00000000..ce9c5c44 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/repository/AuthRepository.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.domain.repository + +import org.keepgoeat.data.model.request.RequestAuth +import org.keepgoeat.data.model.response.ResponseRefresh +import org.keepgoeat.data.model.response.ResponseWithdraw +import org.keepgoeat.domain.model.AuthInfo + +interface AuthRepository { + suspend fun login(requestAuth: RequestAuth): Result + suspend fun refresh(): Result + suspend fun deleteAccount(): Result +} diff --git a/app/src/main/java/org/keepgoeat/domain/repository/DummyRepository.kt b/app/src/main/java/org/keepgoeat/domain/repository/DummyRepository.kt deleted file mode 100644 index 80a2d465..00000000 --- a/app/src/main/java/org/keepgoeat/domain/repository/DummyRepository.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.keepgoeat.domain.repository - -interface DummyRepository { - suspend fun uploadDummy(name: String, email: String) -} diff --git a/app/src/main/java/org/keepgoeat/domain/repository/GoalRepository.kt b/app/src/main/java/org/keepgoeat/domain/repository/GoalRepository.kt new file mode 100644 index 00000000..68aea78f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/repository/GoalRepository.kt @@ -0,0 +1,31 @@ +package org.keepgoeat.domain.repository + +import org.keepgoeat.data.model.response.* +import org.keepgoeat.domain.model.GoalDetail +import org.keepgoeat.domain.model.ArchivedGoal +import org.keepgoeat.domain.model.HomeContent + +interface GoalRepository { + suspend fun achieveGoal( + goalId: Int, + isAchieved: Boolean + ): Result + + suspend fun uploadGoalContent( + food: String, + criterion: String, + isMore: Boolean + ): Result + + suspend fun editGoalContent( + id: Int, + food: String, + criterion: String + ): Result + + suspend fun fetchHomeContent(): Result + suspend fun fetchGoalDetail(goalId: Int): Result + suspend fun fetchArchivedGoal(sortType: String): Result> + suspend fun keepGoal(id: Int): Result + suspend fun deleteGoal(id: Int): Result +} diff --git a/app/src/main/java/org/keepgoeat/domain/repository/UserRepository.kt b/app/src/main/java/org/keepgoeat/domain/repository/UserRepository.kt new file mode 100644 index 00000000..f32ca764 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/repository/UserRepository.kt @@ -0,0 +1,7 @@ +package org.keepgoeat.domain.repository + +import org.keepgoeat.domain.model.UserInfo + +interface UserRepository { + suspend fun fetchUserInfo(): Result +} diff --git a/app/src/main/java/org/keepgoeat/domain/repository/VersionRepository.kt b/app/src/main/java/org/keepgoeat/domain/repository/VersionRepository.kt new file mode 100644 index 00000000..c413cfbc --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/repository/VersionRepository.kt @@ -0,0 +1,7 @@ +package org.keepgoeat.domain.repository + +import org.keepgoeat.data.model.response.ResponseVersion + +interface VersionRepository { + suspend fun getForcedUpdateVersion(clientType: String): Result +} diff --git a/app/src/main/java/org/keepgoeat/domain/type/SignType.kt b/app/src/main/java/org/keepgoeat/domain/type/SignType.kt new file mode 100644 index 00000000..92292bb3 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/domain/type/SignType.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.domain.type + +enum class SignType(val typeStr: String) { + SIGN_UP("signup"), SIGN_IN("signin") +} diff --git a/app/src/main/java/org/keepgoeat/presentation/MainActivity.kt b/app/src/main/java/org/keepgoeat/presentation/MainActivity.kt deleted file mode 100644 index 24767676..00000000 --- a/app/src/main/java/org/keepgoeat/presentation/MainActivity.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.keepgoeat.presentation - -import org.keepgoeat.R -import org.keepgoeat.databinding.ActivityMainBinding -import org.keepgoeat.util.binding.BindingActivity - -class MainActivity : BindingActivity(R.layout.activity_main) diff --git a/app/src/main/java/org/keepgoeat/presentation/SplashActivity.kt b/app/src/main/java/org/keepgoeat/presentation/SplashActivity.kt new file mode 100644 index 00000000..8ea98721 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/SplashActivity.kt @@ -0,0 +1,45 @@ +package org.keepgoeat.presentation + +import android.content.Intent +import android.os.Bundle +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.keepgoeat.R +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.databinding.ActivitySplashBinding +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.onboarding.OnboardingActivity +import org.keepgoeat.presentation.sign.SignActivity +import org.keepgoeat.presentation.base.screen.BindingActivity + +@AndroidEntryPoint +class SplashActivity : BindingActivity(R.layout.activity_splash) { + override fun onCreate(savedInstanceState: Bundle?) { + installSplashScreen() + super.onCreate(savedInstanceState) + loadSplashScreen() + } + + private fun loadSplashScreen() { + lifecycleScope.launch { + delay(1000L) + moveToNext() + } + } + + private fun moveToNext() { + val storage = KGEDataSource(this) + val nextScreen = + if (storage.isLogin) { + if (storage.isClickedOnboardingButton) HomeActivity::class.java + else OnboardingActivity::class.java + } else { + SignActivity::class.java + } + startActivity(Intent(this, nextScreen)) + finish() + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingActivity.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingActivity.kt new file mode 100644 index 00000000..189a0635 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingActivity.kt @@ -0,0 +1,46 @@ +package org.keepgoeat.presentation.base.screen + +import android.os.Bundle +import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatActivity +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.util.KGESnackbar +import org.keepgoeat.util.NetworkMonitor + +abstract class BindingActivity(@LayoutRes private val layoutRes: Int) : + AppCompatActivity() { + lateinit var binding: B + private var snackbar: KGESnackbar? = null + private val _isConnectedNetwork = MutableStateFlow(false) + val isConnectedNetwork get() = _isConnectedNetwork.asStateFlow() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DataBindingUtil.setContentView(this, layoutRes) + snackbar = KGESnackbar( + binding.root, + getString(R.string.network_error_snackbar_message), + Snackbar.LENGTH_INDEFINITE, + true + ) + collectNetworkState() + } + + private fun collectNetworkState() { + NetworkMonitor(this, lifecycleScope).isConnected.flowWithLifecycle(lifecycle) + .onEach { isConnected -> + if (isConnected) snackbar?.dismiss() + else snackbar?.show() + _isConnectedNetwork.value = isConnected + }.launchIn(lifecycleScope) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingBottomSheetDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingBottomSheetDialogFragment.kt new file mode 100644 index 00000000..fe8bec21 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingBottomSheetDialogFragment.kt @@ -0,0 +1,30 @@ +package org.keepgoeat.presentation.base.screen + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +abstract class BindingBottomSheetDialogFragment(@LayoutRes private val layoutRes: Int) : + BottomSheetDialogFragment() { + private var _binding: B? = null + val binding get() = requireNotNull(_binding!!) { "${this::class.java.simpleName}์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingDialogFragment.kt new file mode 100644 index 00000000..46e3f2e9 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingDialogFragment.kt @@ -0,0 +1,40 @@ +package org.keepgoeat.presentation.base.screen + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.annotation.LayoutRes +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.fragment.app.DialogFragment + +abstract class BindingDialogFragment(@LayoutRes private val layoutRes: Int) : + DialogFragment() { + private var _binding: B? = null + val binding get() = requireNotNull(_binding!!) { "${this::class.java.simpleName}์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." } + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.WRAP_CONTENT + ) + dialog?.window?.setBackgroundDrawableResource(android.R.color.transparent) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/org/keepgoeat/util/binding/BindingFragment.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingFragment.kt similarity index 83% rename from app/src/main/java/org/keepgoeat/util/binding/BindingFragment.kt rename to app/src/main/java/org/keepgoeat/presentation/base/screen/BindingFragment.kt index d0553dbb..08326826 100644 --- a/app/src/main/java/org/keepgoeat/util/binding/BindingFragment.kt +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/BindingFragment.kt @@ -1,4 +1,4 @@ -package org.keepgoeat.util.binding +package org.keepgoeat.presentation.base.screen import android.os.Bundle import android.view.LayoutInflater @@ -9,7 +9,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment -abstract class BindingFragment(@LayoutRes private val layoutResId: Int) : +abstract class BindingFragment(@LayoutRes private val layoutRes: Int) : Fragment() { private var _binding: B? = null val binding get() = requireNotNull(_binding!!) { "${this::class.java.simpleName}์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." } @@ -19,7 +19,7 @@ abstract class BindingFragment(@LayoutRes private val layou container: ViewGroup?, savedInstanceState: Bundle?, ): View { - _binding = DataBindingUtil.inflate(inflater, layoutResId, container, false) + _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) return binding.root } diff --git a/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelActivity.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelActivity.kt new file mode 100644 index 00000000..b591898a --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelActivity.kt @@ -0,0 +1,22 @@ +package org.keepgoeat.presentation.base.screen + +import androidx.annotation.LayoutRes +import androidx.databinding.ViewDataBinding +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel + +abstract class MixpanelActivity( + @LayoutRes private val layoutRes: Int, + private val screenName: String, +) : BindingActivity(layoutRes) { + abstract val viewModel: MixpanelViewModel + + override fun onStart() { + super.onStart() + viewModel.startRecodingScreenTime() + } + + override fun onStop() { + super.onStop() + viewModel.stopRecodingScreenTime(screenName) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelBottomSheetDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelBottomSheetDialogFragment.kt new file mode 100644 index 00000000..df2caf92 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/screen/MixpanelBottomSheetDialogFragment.kt @@ -0,0 +1,22 @@ +package org.keepgoeat.presentation.base.screen + +import androidx.annotation.LayoutRes +import androidx.databinding.ViewDataBinding +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel + +abstract class MixpanelBottomSheetDialogFragment( + @LayoutRes private val layoutRes: Int, + private val screenName: String, +) : BindingBottomSheetDialogFragment(layoutRes) { + abstract val viewModel: MixpanelViewModel + + override fun onStart() { + super.onStart() + viewModel.startRecodingScreenTime() + } + + override fun onStop() { + super.onStop() + viewModel.stopRecodingScreenTime(screenName) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/BaseViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/BaseViewModel.kt new file mode 100644 index 00000000..58dec14d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/BaseViewModel.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.presentation.base.viewmodel + +import androidx.lifecycle.ViewModel + +abstract class BaseViewModel : ViewModel() diff --git a/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/MixpanelViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/MixpanelViewModel.kt new file mode 100644 index 00000000..0cf2ca28 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/base/viewmodel/MixpanelViewModel.kt @@ -0,0 +1,17 @@ +package org.keepgoeat.presentation.base.viewmodel + +import org.keepgoeat.util.mixpanel.MixpanelProvider +import javax.inject.Inject + +abstract class MixpanelViewModel : BaseViewModel() { + @Inject + lateinit var mixpanelProvider: MixpanelProvider + + fun startRecodingScreenTime() { + mixpanelProvider.startRecodingScreenTime() + } + + fun stopRecodingScreenTime(screenName: String) { + mixpanelProvider.stopRecodingScreenTime(screenName) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/common/WebViewActivity.kt b/app/src/main/java/org/keepgoeat/presentation/common/WebViewActivity.kt new file mode 100644 index 00000000..113131ec --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/common/WebViewActivity.kt @@ -0,0 +1,45 @@ +package org.keepgoeat.presentation.common + +import android.annotation.SuppressLint +import android.os.Bundle +import android.webkit.WebChromeClient +import android.webkit.WebViewClient +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityWebViewBinding +import org.keepgoeat.presentation.base.screen.BindingActivity + +class WebViewActivity : BindingActivity(R.layout.activity_web_view) { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + initView() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun initView() { + binding.webView.apply { + webViewClient = WebViewClient() + webChromeClient = WebChromeClient() + + settings.apply { + javaScriptEnabled = true + javaScriptCanOpenWindowsAutomatically = true + loadWithOverviewMode = true + useWideViewPort = true + domStorageEnabled = true + setSupportZoom(true) + } + + intent.getStringExtra(ARG_WEB_VIEW_LINK)?.let { loadUrl(it) } + } + } + + override fun onPause() { + super.onPause() + overridePendingTransition(0, 0) + } + + companion object { + const val ARG_WEB_VIEW_LINK = "link" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/detail/GoalDeleteBottomDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDeleteBottomDialogFragment.kt new file mode 100644 index 00000000..1fc6408b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDeleteBottomDialogFragment.kt @@ -0,0 +1,34 @@ +package org.keepgoeat.presentation.detail + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.R +import org.keepgoeat.databinding.DialogBottomGoalDeleteBinding +import org.keepgoeat.presentation.base.screen.MixpanelBottomSheetDialogFragment + +@AndroidEntryPoint +class GoalDeleteBottomDialogFragment : + MixpanelBottomSheetDialogFragment( + R.layout.dialog_bottom_goal_delete, SCREEN_NAME + ) { + override val viewModel: GoalDetailViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = viewModel + + addListeners() + } + + private fun addListeners() { + binding.btnNo.setOnClickListener { + dismiss() + } + } + + companion object { + private const val SCREEN_NAME = "goal delete" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailActivity.kt b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailActivity.kt new file mode 100644 index 00000000..2b33f3f7 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailActivity.kt @@ -0,0 +1,152 @@ +package org.keepgoeat.presentation.detail + +import android.content.Intent +import android.os.Bundle +import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityGoalDetailBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.detail.GoalDetailViewModel.Companion.CELL_COUNT +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.model.GoalContent +import org.keepgoeat.presentation.my.archive.ArchivedGoalActivity +import org.keepgoeat.presentation.my.archive.ArchivedGoalActivity.Companion.ARG_IS_ENTERED_FROM_KEEP +import org.keepgoeat.presentation.setting.GoalSettingActivity +import org.keepgoeat.presentation.setting.GoalSettingActivity.Companion.ARG_GOAL_CONTENT +import org.keepgoeat.presentation.setting.GoalSettingActivity.Companion.ARG_IS_UPDATED +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.presentation.type.RecyclerLayoutType +import org.keepgoeat.util.ItemDecorationUtil +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.showToast +import org.keepgoeat.util.safeValueOf + +@AndroidEntryPoint +class GoalDetailActivity : + MixpanelActivity(R.layout.activity_goal_detail, SCREEN_NAME) { + override val viewModel: GoalDetailViewModel by viewModels() + private lateinit var adapter: GoalStickerListAdapter + private var isUpdated = false + private val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + moveToHome() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + intent.let { + val eatingType = safeValueOf(it.getStringExtra(ARG_EATING_TYPE)) ?: return + binding.eatingType = eatingType + adapter = GoalStickerListAdapter(eatingType, CELL_COUNT) + val goalId = it.getIntExtra(ARG_GOAL_ID, -1) + viewModel.fetchGoalDetailInfo(goalId) + isUpdated = it.getBooleanExtra(ARG_IS_UPDATED, false) + if (isUpdated) this.onBackPressedDispatcher.addCallback(this, callback) + } + + initLayout() + addListeners() + collectData() + } + + private fun initLayout() { + binding.rvGoalCard.apply { + addItemDecoration( + ItemDecorationUtil( + CARD_ITEM_SPACE, + Pair(CARD_MATRIX_ROW, CARD_MATRIX_COL), + RecyclerLayoutType.GRID + ) + ) + clipToOutline = true + adapter = this@GoalDetailActivity.adapter + } + } + + private fun addListeners() { + binding.ivBack.setOnClickListener { + if (isUpdated) moveToHome() + else finish() + } + binding.ivKeep.setOnClickListener { + showGoalKeepDialog() + } + binding.ivEdit.setOnClickListener { + viewModel.goalDetail.value?.let { detail -> + val content = GoalContent(detail.id, detail.food, detail.criterion) + Intent(this, GoalSettingActivity::class.java).apply { + putExtra(ARG_GOAL_CONTENT, content) + putExtra(ARG_EATING_TYPE, detail.eatingType.name) + }.also { + startActivity(it) + } + } + } + } + + private fun collectData() { + viewModel.goalStickers.flowWithLifecycle(lifecycle).onEach { stickers -> + adapter.submitList(stickers) + }.launchIn(lifecycleScope) + viewModel.keepState.flowWithLifecycle(lifecycle).onEach { keepState -> + when (keepState) { + is UiState.Success -> { + showToast(getString(R.string.goal_detail_success_goal_keep_toast_message)) + Intent(this, ArchivedGoalActivity::class.java).apply { + putExtra(ARG_IS_ENTERED_FROM_KEEP, true) + putExtra( + ARG_HOME_GOAL_COUNT, + intent.getIntExtra(ARG_HOME_GOAL_COUNT, -1) - 1 + ) + }.also { + startActivity(it) + finish() + } + } + else -> {} + } + }.launchIn(lifecycleScope) + viewModel.deleteState.flowWithLifecycle(lifecycle).onEach { deleteState -> + when (deleteState) { + is UiState.Success -> { + showToast(getString(R.string.goal_detail_success_goal_delete_toast_message)) + startActivity(Intent(this, HomeActivity::class.java)) + finish() + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun showGoalKeepDialog() { + intent?.let { + GoalKeepBottomDialogFragment().show(supportFragmentManager, "goalKeepDialog") + } + } + + private fun moveToHome() { + val intent = Intent(this, HomeActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + } + + companion object { + private const val CARD_ITEM_SPACE = 2 + private const val CARD_MATRIX_ROW = 5 + private const val CARD_MATRIX_COL = 7 + const val ARG_EATING_TYPE = "eatingType" + const val ARG_GOAL_ID = "goalId" + const val ARG_HOME_GOAL_COUNT = "homeGoalCount" + private const val SCREEN_NAME = "goal detail" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailViewModel.kt new file mode 100644 index 00000000..3d2e21d2 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/detail/GoalDetailViewModel.kt @@ -0,0 +1,84 @@ +package org.keepgoeat.presentation.detail + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.keepgoeat.domain.model.GoalDetail +import org.keepgoeat.domain.model.GoalSticker +import org.keepgoeat.domain.repository.GoalRepository +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.util.UiState +import org.keepgoeat.util.mixpanel.GoalEvent +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class GoalDetailViewModel @Inject constructor( + private val goalRepository: GoalRepository, +) : MixpanelViewModel() { + private val _goalStickers = MutableStateFlow>(emptyList()) + val goalStickers get() = _goalStickers.asStateFlow() + private val _goalDetail = MutableStateFlow(null) + val goalDetail get() = _goalDetail.asStateFlow() + private val _goalId = MutableStateFlow(-1) + val goalId get() = _goalId.asStateFlow() + private val _keepState = MutableStateFlow>(UiState.Loading) + val keepState get() = _keepState.asStateFlow() + private val _deleteState = MutableStateFlow>(UiState.Loading) + val deleteState get() = _deleteState.asStateFlow() + + fun fetchGoalDetailInfo(goalId: Int) { + _goalId.value = goalId + viewModelScope.launch { + goalRepository.fetchGoalDetail(goalId).onSuccess { detail -> + _goalDetail.value = detail + _goalStickers.value = Array(CELL_COUNT) { idx -> + GoalSticker(idx, idx < detail.thisMonthCount) + }.toList() + }.onFailure { + Timber.e(it.message) + } + } + } + + fun keepGoal() { + viewModelScope.launch { + goalId.value.let { id -> + goalRepository.keepGoal(id).onSuccess { goalData -> + _keepState.value = UiState.Success(goalData.goalId) + mixpanelProvider.sendEvent( + GoalEvent.archiveGoal( + goalDetail.value?.food ?: "", + goalDetail.value?.criterion ?: "" + ) + ) + }.onFailure { + Timber.e(it.message) + } + } + } + } + + fun deleteGoal() { + viewModelScope.launch { + goalId.value.let { id -> + goalRepository.deleteGoal(id) + .onSuccess { deletedData -> + _deleteState.value = UiState.Success(deletedData.goalId) + with(mixpanelProvider) { + deleteGoal() + sendEvent(GoalEvent.deleteGoal(), false) + } + }.onFailure { + Timber.e(it.message) + } + } + } + } + + companion object { + const val CELL_COUNT = 35 + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/detail/GoalKeepBottomDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/detail/GoalKeepBottomDialogFragment.kt new file mode 100644 index 00000000..9d598d49 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/detail/GoalKeepBottomDialogFragment.kt @@ -0,0 +1,56 @@ +package org.keepgoeat.presentation.detail + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.DialogBottomGoalKeepBinding +import org.keepgoeat.presentation.base.screen.MixpanelBottomSheetDialogFragment +import org.keepgoeat.util.UiState + +@AndroidEntryPoint +class GoalKeepBottomDialogFragment : // TODO ๋„ค์ด๋ฐ ์ˆ˜์ • keep -> archive + MixpanelBottomSheetDialogFragment( + R.layout.dialog_bottom_goal_keep, SCREEN_NAME + ) { + override val viewModel: GoalDetailViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = viewModel + + addListeners() + collectData() + } + + private fun addListeners() { + binding.tvDelete.setOnClickListener { + showGoalDeleteDialog() + } + } + + private fun collectData() { + viewModel.keepState.flowWithLifecycle(lifecycle).onEach { keepState -> + when (keepState) { + is UiState.Success -> { + dismiss() + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun showGoalDeleteDialog() { + GoalDeleteBottomDialogFragment().show(parentFragmentManager, "goalDeleteDialog") + dismiss() + } + + companion object { + private const val SCREEN_NAME = "goal archive" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/detail/GoalStickerListAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/detail/GoalStickerListAdapter.kt new file mode 100644 index 00000000..6946d921 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/detail/GoalStickerListAdapter.kt @@ -0,0 +1,46 @@ +package org.keepgoeat.presentation.detail + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.databinding.ItemGoalStickerBinding +import org.keepgoeat.domain.model.GoalSticker +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.util.ItemDiffCallback +import java.time.LocalDate + +class GoalStickerListAdapter(private val eatingType: EatingType, private val cellCount: Int) : + ListAdapter( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.id == new.id } + ) + ) { + + /** ์ „์ฒด ์…€ ๊ฐœ์ˆ˜์—์„œ ์›”๋ณ„ ์ผ ์ˆ˜๋ฅผ ๋บ€ ๋นˆ์นธ ์ˆ˜ */ + private val blankCellCount: Int = cellCount - LocalDate.now().lengthOfMonth() + private lateinit var inflater: LayoutInflater + + class GoalStickerViewHolder(private val binding: ItemGoalStickerBinding) : + RecyclerView.ViewHolder(binding.root) { + fun onBind(data: GoalSticker, eatingType: EatingType, isDefault: Boolean) { + binding.layout.setBackgroundColor(eatingType.cardBackgroundColor) + if (isDefault) + binding.ivSticker.setBackgroundResource(eatingType.defaultStickerRes) + else if (data.isDone) + binding.ivSticker.setBackgroundResource(eatingType.snailStickerRes) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GoalStickerViewHolder { + if (!::inflater.isInitialized) + inflater = LayoutInflater.from(parent.context) + + return GoalStickerViewHolder(ItemGoalStickerBinding.inflate(inflater, parent, false)) + } + + override fun onBindViewHolder(holder: GoalStickerViewHolder, position: Int) { + holder.onBind(currentList[position], eatingType, position >= itemCount - blankCellCount) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/dummy/DummyActivity.kt b/app/src/main/java/org/keepgoeat/presentation/dummy/DummyActivity.kt deleted file mode 100644 index b706e581..00000000 --- a/app/src/main/java/org/keepgoeat/presentation/dummy/DummyActivity.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.keepgoeat.presentation.dummy - -import android.os.Bundle -import androidx.activity.viewModels -import dagger.hilt.android.AndroidEntryPoint -import org.keepgoeat.R -import org.keepgoeat.databinding.ActivityDummyBinding -import org.keepgoeat.util.binding.BindingActivity - -@AndroidEntryPoint -class DummyActivity : BindingActivity(R.layout.activity_dummy) { - private val viewModel: DummyViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding.viewModel = viewModel -// binding.lifecycleOwner = this // binding์—์„œ LiveData๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ํ•ด๋‹น ์ฝ”๋“œ ํ•„์š” - } - - private fun initLayout() { - TODO("Not yet implemented") - } - - private fun addListeners() { - TODO("Not yet implemented") - } - - private fun addObservers() { - TODO("Not yet implemented") - } -} diff --git a/app/src/main/java/org/keepgoeat/presentation/dummy/DummyViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/dummy/DummyViewModel.kt deleted file mode 100644 index b1595523..00000000 --- a/app/src/main/java/org/keepgoeat/presentation/dummy/DummyViewModel.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.keepgoeat.presentation.dummy - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import org.keepgoeat.domain.repository.DummyRepository -import javax.inject.Inject - -@HiltViewModel -class DummyViewModel @Inject constructor(private val dummyRepository: DummyRepository) : - ViewModel() { - fun uploadDummy() { - viewModelScope.launch { - dummyRepository.uploadDummy("keepgoeat", "keepgoeat@gmail.com") - } - } -} diff --git a/app/src/main/java/org/keepgoeat/presentation/home/HomeActivity.kt b/app/src/main/java/org/keepgoeat/presentation/home/HomeActivity.kt new file mode 100644 index 00000000..1111409c --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/home/HomeActivity.kt @@ -0,0 +1,141 @@ +package org.keepgoeat.presentation.home + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityHomeBinding +import org.keepgoeat.domain.model.HomeGoal +import org.keepgoeat.presentation.detail.GoalDetailActivity +import org.keepgoeat.presentation.my.MyActivity +import org.keepgoeat.presentation.sign.SignActivity +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.presentation.type.ProcessState +import org.keepgoeat.util.UiState +import org.keepgoeat.presentation.base.screen.BindingActivity + +@AndroidEntryPoint +class HomeActivity : BindingActivity(R.layout.activity_home) { + private val viewModel: HomeViewModel by viewModels() + private lateinit var goalAdapter: HomeGoalAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + if (intent.getBooleanExtra(ARG_KILL_HOME_AND_GO_TO_SIGN, false)) moveToSign() + + viewModel.getForcedUpdateVersion() + initLayout() + addListeners() + collectData() + } + + override fun onStart() { + super.onStart() + viewModel.startRecodingScreenTime() + } + + override fun onStop() { + super.onStop() + viewModel.stopRecodingScreenTime("$SCREEN_NAME${viewModel.goalCount.value}") + } + + private fun addListeners() { + with(binding) { + btnNoGoal.setOnClickListener { + showMakeGoalDialog() + } + ivMyPage.setOnClickListener { + moveToMy() + } + } + } + + private fun initLayout() { + goalAdapter = + HomeGoalAdapter(::changeGoalItemBtnColor, ::moveToDetail, ::showMakeGoalDialog) + binding.rvMyGoals.apply { + itemAnimator = null + adapter = goalAdapter + } + } + + private fun collectData() { + viewModel.goalList.flowWithLifecycle(lifecycle).onEach { goalList -> + goalAdapter.submitList(goalList.toMutableList()) + }.launchIn(lifecycleScope) + viewModel.goalCount.flowWithLifecycle(lifecycle).onEach { goalCount -> + if (goalCount > 0) + binding.ivHomeSnail.setImageResource(R.drawable.ic_snail_orange_cheer_right) + }.launchIn(lifecycleScope) + viewModel.lottieState.flowWithLifecycle(lifecycle).onEach { lottieState -> + when (lottieState) { + ProcessState.IN_PROGRESS -> { + binding.lottieSnail.playAnimation() + binding.lottieBackground.playAnimation() + viewModel.changeLottieState(ProcessState.DONE) + } + ProcessState.IDLE -> {} + ProcessState.DONE -> {} + } + }.launchIn(lifecycleScope) + combine(viewModel.homeDataFetchState, isConnectedNetwork) { fetchState, isConnected -> + fetchState !is UiState.Success && isConnected + }.flowWithLifecycle(lifecycle).onEach { shouldFetch -> + if (shouldFetch) viewModel.fetchHomeContent() + }.launchIn(lifecycleScope) + viewModel.updateVersion.flowWithLifecycle(lifecycle).onEach { version -> + if (viewModel.compareVersion(version)) showForceUpdateDialog(version) + }.launchIn(lifecycleScope) + } + + private fun showMakeGoalDialog() { + HomeBottomDialogFragment().show(supportFragmentManager, "homeDialog") + } + + private fun showForceUpdateDialog(updateVersion: String) { + HomeForceUpdateDialogFragment().apply { + arguments = Bundle().apply { + putString(ARG_UPDATE_VERSION, updateVersion) + } + }.show(supportFragmentManager, "forceUpdateDialog") + } + + private fun moveToDetail(eatingType: EatingType, goalId: Int) { + val intent = Intent(this@HomeActivity, GoalDetailActivity::class.java) + intent.putExtra(GoalDetailActivity.ARG_EATING_TYPE, eatingType.name) + intent.putExtra(GoalDetailActivity.ARG_GOAL_ID, goalId) + intent.putExtra(ARG_HOME_GOAL_COUNT, viewModel.goalCount.value) + startActivity(intent) + } + + private fun moveToMy() { + val intent = Intent(this@HomeActivity, MyActivity::class.java) + intent.putExtra(ARG_HOME_GOAL_COUNT, viewModel.goalCount.value) + startActivity(intent) + } + + private fun changeGoalItemBtnColor(myGoal: HomeGoal) { + viewModel.changeGoalAchieved(myGoal) + } + + private fun moveToSign() { + startActivity(Intent(this, SignActivity::class.java)) + finish() + } + + companion object { + const val ARG_KILL_HOME_AND_GO_TO_SIGN = "killHomeAndGoToSign" + const val ARG_HOME_GOAL_COUNT = "homeGoalCount" + const val ARG_UPDATE_VERSION = "updateVersion" + const val SCREEN_NAME = "main_goalnum" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/home/HomeBottomDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/home/HomeBottomDialogFragment.kt new file mode 100644 index 00000000..ff74393d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/home/HomeBottomDialogFragment.kt @@ -0,0 +1,47 @@ +package org.keepgoeat.presentation.home + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.R +import org.keepgoeat.databinding.DialogBottomHomeBinding +import org.keepgoeat.presentation.base.screen.MixpanelBottomSheetDialogFragment +import org.keepgoeat.presentation.setting.GoalSettingActivity +import org.keepgoeat.presentation.type.EatingType + +@AndroidEntryPoint +class HomeBottomDialogFragment : + MixpanelBottomSheetDialogFragment( + R.layout.dialog_bottom_home, SCREEN_NAME + ) { + override val viewModel: HomeViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + addListeners() + } + + private fun addListeners() { + binding.layoutHomeBottomMore.setOnClickListener { + moveToSetting(EatingType.MORE) + viewModel.sendGoalAddEvent(EatingType.MORE) + } + binding.layoutHomeBottomLess.setOnClickListener { + moveToSetting(EatingType.LESS) + viewModel.sendGoalAddEvent(EatingType.LESS) + } + } + + private fun moveToSetting(eatingType: EatingType) { + val intent = Intent(requireActivity(), GoalSettingActivity::class.java) + intent.putExtra(GoalSettingActivity.ARG_EATING_TYPE, eatingType.name) + startActivity(intent) + dismiss() + } + + companion object { + const val SCREEN_NAME = "main_add" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/home/HomeForceUpdateDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/home/HomeForceUpdateDialogFragment.kt new file mode 100644 index 00000000..d012ec20 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/home/HomeForceUpdateDialogFragment.kt @@ -0,0 +1,47 @@ +package org.keepgoeat.presentation.home + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.View +import org.keepgoeat.BuildConfig +import org.keepgoeat.R +import org.keepgoeat.databinding.DialogForceUpdateBinding +import org.keepgoeat.presentation.base.screen.BindingDialogFragment + +class HomeForceUpdateDialogFragment : + BindingDialogFragment(R.layout.dialog_force_update) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + isCancelable = false + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.updateVersion = arguments?.getString(ARG_UPDATE_VERSION) + binding.currentVersion = BuildConfig.VERSION_NAME + + addListeners() + } + + private fun addListeners() { + binding.btnForceUpdate.setOnClickListener { + moveToPlayStore() + } + } + + private fun moveToPlayStore() { + Intent(Intent.ACTION_VIEW).apply { + data = + Uri.parse(getString(R.string.play_store_detail_url) + requireContext().packageName) + }.also { + startActivity(it) + } + } + + companion object { + const val ARG_UPDATE_VERSION = "updateVersion" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/home/HomeGoalAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/home/HomeGoalAdapter.kt new file mode 100644 index 00000000..b8f9258f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/home/HomeGoalAdapter.kt @@ -0,0 +1,153 @@ +package org.keepgoeat.presentation.home + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.R +import org.keepgoeat.databinding.ItemAddGoalBinding +import org.keepgoeat.databinding.ItemHomeGoalBinding +import org.keepgoeat.domain.model.HomeGoal +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.presentation.type.HomeBtnType +import org.keepgoeat.presentation.type.HomeGoalViewType +import org.keepgoeat.util.ItemDiffCallback +import org.keepgoeat.util.extension.setOnSingleClickListener +import org.keepgoeat.util.setVisibility + +class HomeGoalAdapter( + private val changeBtnColor: (HomeGoal) -> Unit, + private val moveToDetail: (EatingType, Int) -> Unit, + private val showMakeGoalDialog: () -> Unit, +) : ListAdapter( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.id == new.id } + ) +) { + private lateinit var inflater: LayoutInflater + + class MyGoalViewHolder( + private val binding: ItemHomeGoalBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun bind( + myGoal: HomeGoal, + eatingType: EatingType, + changeBtnColor: (HomeGoal) -> Unit, + moveToDetail: (EatingType, Int) -> Unit, + ) { + val btnType: HomeBtnType = if (eatingType == EatingType.MORE) { // ๋” ๋จน๊ธฐ์ธ ๊ฒฝ์šฐ + if (myGoal.isAchieved) { + HomeBtnType.PLUS_ACHIEVED + } else { + HomeBtnType.PLUS_NOT_ACHIEVED + } + } else { // ๋œ ๋จน๊ธฐ์ธ ๊ฒฝ์šฐ + if (myGoal.isAchieved) { + HomeBtnType.MINUS_ACHIEVED + } else { + HomeBtnType.MINUS_NOT_ACHIEVED + } + } + binding.goal = myGoal + binding.goalType = eatingType + binding.goalBtn = btnType + + with(binding) { + btnGoalAchieved.setOnSingleClickListener { + changeBtnColor(myGoal) + } + layoutHomeGoal.setOnClickListener { + moveToDetail(eatingType, myGoal.id) + } + } + } + } + + class AddGoalViewHolder( + private val binding: ItemAddGoalBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(goalCount: Int, showMakeGoalDialog: () -> Unit) { + when (goalCount) { + 0 -> binding.layoutGoalInfo.visibility = View.GONE + 1 -> binding.tvAddMoreGoal.setText(R.string.home_two_more_goal) + 2 -> binding.tvAddMoreGoal.setText(R.string.home_one_more_goal) + 3 -> { + binding.tvAddMoreGoal.setText(R.string.home_no_more_goal) + binding.btnMakeGoal.setVisibility(false) + } + } + if (goalCount < 3) { + binding.root.setOnClickListener { + showMakeGoalDialog() + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + if (!::inflater.isInitialized) + inflater = LayoutInflater.from(parent.context) + return when (viewType) { + HomeGoalViewType.MY_GOAL_TYPE.ordinal -> { + MyGoalViewHolder(ItemHomeGoalBinding.inflate(inflater, parent, false)) + } + HomeGoalViewType.ADD_GOAL_TYPE.ordinal -> { + AddGoalViewHolder(ItemAddGoalBinding.inflate(inflater, parent, false)) + } + else -> { + throw java.lang.ClassCastException("Unknown ViewType Error") + } + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is MyGoalViewHolder -> { + if (currentList[position].isMore) { + holder.bind( + currentList[position], + EatingType.MORE, + changeBtnColor, + moveToDetail + ) + } else { + holder.bind( + currentList[position], + EatingType.LESS, + changeBtnColor, + moveToDetail + ) + } + } + is AddGoalViewHolder -> holder.bind(currentList.size - 1, showMakeGoalDialog) + } + } + + override fun getItemViewType(position: Int): Int = currentList[position].type.ordinal + + override fun submitList(list: MutableList?) { + if (list != null && list.isNotEmpty()) { + if (list.last().type == HomeGoalViewType.ADD_GOAL_TYPE) { + super.submitList(list) + } else { + super.submitList( + list.plus( + mutableListOf( + HomeGoal( + 0, + "", + "", + false, + false, + 0, + HomeGoalViewType.ADD_GOAL_TYPE + ) + ) + ) + ) + } + } + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/home/HomeViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/home/HomeViewModel.kt new file mode 100644 index 00000000..482f9288 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/home/HomeViewModel.kt @@ -0,0 +1,137 @@ +package org.keepgoeat.presentation.home + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.keepgoeat.BuildConfig +import org.keepgoeat.domain.model.HomeContent +import org.keepgoeat.domain.model.HomeGoal +import org.keepgoeat.domain.repository.GoalRepository +import org.keepgoeat.domain.repository.VersionRepository +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.presentation.type.ProcessState +import org.keepgoeat.util.UiState +import org.keepgoeat.util.mixpanel.GoalEvent +import timber.log.Timber +import java.io.IOException +import java.time.LocalDateTime +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + private val goalRepository: GoalRepository, + private val versionRepository: VersionRepository, +) : MixpanelViewModel() { + private val _homeDataFetchState = MutableStateFlow>(UiState.Loading) + val homeDataFetchState get() = _homeDataFetchState.asStateFlow() + private var _goalList = MutableStateFlow>(mutableListOf()) + val goalList get() = _goalList.asStateFlow() + private val _goalCount = MutableStateFlow(0) + val goalCount = _goalCount.asStateFlow() + private val _hour = MutableStateFlow(LocalDateTime.now().hour) + val hour = _hour.asStateFlow() + private val _cheeringMessage = MutableStateFlow("") + val cheeringMessage = _cheeringMessage.asStateFlow() + private val _lottieState = MutableStateFlow(ProcessState.IDLE) + val lottieState get() = _lottieState.asStateFlow() + private val _updateVersion = MutableStateFlow("") + val updateVersion get() = _updateVersion.asStateFlow() + + fun fetchHomeContent() { + viewModelScope.launch { + goalRepository.fetchHomeContent() + .onSuccess { homeContent -> + _homeDataFetchState.value = UiState.Success(homeContent) + _goalList.value = homeContent.goals.toMutableList() + _cheeringMessage.value = homeContent.cheeringMessage + _lottieState.value = ProcessState.IDLE + _goalCount.value = homeContent.goals.size + }.onFailure { throwable -> + when (throwable) { + is IOException -> + _homeDataFetchState.value = UiState.Error(throwable.message) + } + Timber.e(throwable.message) + } + } + } + + fun changeGoalAchieved(goal: HomeGoal) { + val position = goalList.value.indexOf(goal) + viewModelScope.launch { + goalRepository.achieveGoal(goal.id, !goal.isAchieved) + .onSuccess { goalData -> + val list = _goalList.value.toMutableList() + with(goal) { + list.set( + position, + HomeGoal( + id, + goalTitle, + goalCriterion, + isMore, + goalData.updatedIsAchieved, + goalData.thisMonthCount, + type + ) + ) + } + if (goalData.updatedIsAchieved) { + _lottieState.value = ProcessState.IN_PROGRESS + mixpanelProvider.sendEvent( + GoalEvent.completeGoal( + goal.goalTitle, + goal.goalCriterion + ), + true + ) + } + _goalList.value = + list.toMutableList() + } + .onFailure { + Timber.e(it.message) + } + } + } + + fun changeLottieState(state: ProcessState) { + _lottieState.value = state + } + + fun sendGoalAddEvent(eatingType: EatingType) { + val goalType = if (eatingType == EatingType.MORE) "๋”๋จน๊ธฐ" else "๋œ๋จน๊ธฐ" + mixpanelProvider.sendEvent(GoalEvent.addGoal(goalType), false) + } + + fun getForcedUpdateVersion() { + viewModelScope.launch { + versionRepository.getForcedUpdateVersion(CLIENT_TYPE) + .onSuccess { + _updateVersion.value = it.version + } + .onFailure { + Timber.e(it.message) + } + } + } + + fun compareVersion(updateVersion: String): Boolean { + if (updateVersion.isBlank()) return false + + val splitCurrent = BuildConfig.VERSION_NAME.split(".") + val splitUpdate = updateVersion.split(".") + if (splitCurrent.size > 2 && splitUpdate.size > 2) { + if (splitCurrent[2] != splitUpdate[2]) return true + } + + return false + } + + companion object { + private const val CLIENT_TYPE = "AOS" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/model/GoalContent.kt b/app/src/main/java/org/keepgoeat/presentation/model/GoalContent.kt new file mode 100644 index 00000000..d83670c8 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/model/GoalContent.kt @@ -0,0 +1,11 @@ +package org.keepgoeat.presentation.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GoalContent( + val id: Int, + val food: String, + val criterion: String +) : Parcelable diff --git a/app/src/main/java/org/keepgoeat/presentation/model/MixPanelEvent.kt b/app/src/main/java/org/keepgoeat/presentation/model/MixPanelEvent.kt new file mode 100644 index 00000000..3c08cc1c --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/model/MixPanelEvent.kt @@ -0,0 +1,6 @@ +package org.keepgoeat.presentation.model + +data class MixPanelEvent( + val name: String, + val params: Map? +) diff --git a/app/src/main/java/org/keepgoeat/presentation/my/AccountInfoActivity.kt b/app/src/main/java/org/keepgoeat/presentation/my/AccountInfoActivity.kt new file mode 100644 index 00000000..500e4998 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/AccountInfoActivity.kt @@ -0,0 +1,91 @@ +package org.keepgoeat.presentation.my + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.data.service.KakaoAuthService +import org.keepgoeat.data.service.NaverAuthService +import org.keepgoeat.databinding.ActivityAccountInfoBinding +import org.keepgoeat.domain.model.UserInfo +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.home.HomeActivity.Companion.ARG_KILL_HOME_AND_GO_TO_SIGN +import org.keepgoeat.presentation.my.MyActivity.Companion.ARG_USER_INFO +import org.keepgoeat.presentation.my.withdraw.WithdrawActivity +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.getParcelable +import org.keepgoeat.util.extension.showToast +import javax.inject.Inject + +@AndroidEntryPoint +class AccountInfoActivity : + MixpanelActivity(R.layout.activity_account_info, SCREEN_NAME) { + @Inject + lateinit var kakaoSignService: KakaoAuthService + + @Inject + lateinit var naverSignService: NaverAuthService + override val viewModel: MyViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + intent.getParcelable(ARG_USER_INFO, UserInfo::class.java)?.let { // TODO need refactoring + binding.tvUserName.text = it.name + binding.tvUserEmail.text = it.email + } + + addListeners() + collectData() + } + + private fun addListeners() { + binding.viewToolbar.ivBack.setOnClickListener { + finish() + } + binding.tvLogout.setOnClickListener { + LogoutDialogFragment().show(supportFragmentManager, "logoutDialog") + } + binding.tvDeleteAccount.setOnClickListener { + val intent = Intent(this, WithdrawActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + startActivity(intent) + } + } + + private fun collectData() { + viewModel.logoutUiState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + showToast(getString(R.string.my_logout_success_toast_message)) + moveToSign() + } + is UiState.Error -> { + showToast(getString(R.string.my_logout_failure_toast_message)) + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun moveToSign() { + Intent(this, HomeActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + putExtra(ARG_KILL_HOME_AND_GO_TO_SIGN, true) + }.also { + startActivity(it) + } + } + + companion object { + private const val SCREEN_NAME = "account" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/LogoutDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/my/LogoutDialogFragment.kt new file mode 100644 index 00000000..25791330 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/LogoutDialogFragment.kt @@ -0,0 +1,47 @@ +package org.keepgoeat.presentation.my + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.R +import org.keepgoeat.data.service.KakaoAuthService +import org.keepgoeat.data.service.NaverAuthService +import org.keepgoeat.databinding.DialogLogoutBinding +import org.keepgoeat.presentation.type.LoginPlatformType +import org.keepgoeat.presentation.base.screen.BindingDialogFragment +import javax.inject.Inject + +@AndroidEntryPoint +class LogoutDialogFragment : BindingDialogFragment(R.layout.dialog_logout) { + @Inject + lateinit var kakaoSignService: KakaoAuthService + + @Inject + lateinit var naverSignService: NaverAuthService + + private val viewModel: MyViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + + addListeners() + addListeners() + } + + private fun addListeners() { + binding.yes.setOnClickListener { + when (viewModel.loginPlatForm) { + LoginPlatformType.NAVER -> naverSignService.logoutNaver(viewModel::logout) + LoginPlatformType.KAKAO -> kakaoSignService.logoutKakao(viewModel::logout) + else -> {} + } + dismiss() + } + binding.no.setOnClickListener { + dismiss() + } + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/MyActivity.kt b/app/src/main/java/org/keepgoeat/presentation/my/MyActivity.kt new file mode 100644 index 00000000..8e3b98d9 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/MyActivity.kt @@ -0,0 +1,125 @@ +package org.keepgoeat.presentation.my + +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import androidx.activity.viewModels +import com.google.android.play.core.review.ReviewManagerFactory +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.BuildConfig +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityMyBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.common.WebViewActivity +import org.keepgoeat.presentation.my.archive.ArchivedGoalActivity +import org.keepgoeat.util.extension.showToast + +@AndroidEntryPoint +class MyActivity : MixpanelActivity(R.layout.activity_my, SCREEN_NAME) { + override val viewModel: MyViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + viewModel.fetchUserInfo() + addListeners() + } + + private fun addListeners() { + binding.viewToolbar.ivBack.setOnClickListener { + finish() + } + binding.tvUserName.setOnClickListener { + Intent(this, AccountInfoActivity::class.java).apply { + putExtra(ARG_USER_INFO, viewModel.userInfo.value) + }.also { + startActivity(it) + } + } + binding.layoutArchivedGoal.setOnClickListener { + moveToArchivedGoalDetail() + } + binding.tvContactUs.setOnClickListener { + sendMail( + getString(R.string.my_contact_us_mail_title), + String.format( + getString(R.string.my_contact_us_mail_content), + Build.BRAND, + Build.DEVICE, + BuildConfig.VERSION_NAME, + Build.VERSION.SDK_INT, + Build.VERSION.RELEASE + ) + ) + } + binding.tvFeedback.setOnClickListener { + moveToPlayStore() + } + binding.tvAboutService.setOnClickListener { + startActivity(Intent(this, ServiceIntroActivity::class.java)) + } + binding.tvTerms.setOnClickListener { + moveToWebPage(TERMS_LINK) + } + binding.tvPolicy.setOnClickListener { + moveToWebPage(POLICY_LINK) + } + } + + private fun moveToArchivedGoalDetail() { + val homeGoalCount = intent.getIntExtra(ARG_HOME_GOAL_COUNT, -1) + val intent = Intent(this, ArchivedGoalActivity::class.java) + intent.putExtra(ARG_HOME_GOAL_COUNT, homeGoalCount) + startActivity(intent) + } + + private fun sendMail(title: String, content: String) { + Intent(Intent.ACTION_SEND).apply { + type = "plain/text" + putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.keep_go_eat_mail))) + putExtra(Intent.EXTRA_SUBJECT, title) + putExtra(Intent.EXTRA_TEXT, content) + }.also { startActivity(it) } + } + + private fun showReviewDialog() { + val manager = ReviewManagerFactory.create(this) + manager.requestReviewFlow().addOnCompleteListener { task -> + if (task.isSuccessful) { + manager.launchReviewFlow(this, task.result) + .addOnCompleteListener { + showToast(getString(R.string.my_feedback_success_toast_message)) + } + } else { + moveToPlayStore() + } + } + } + + private fun moveToWebPage(link: String) { + Intent(this, WebViewActivity::class.java).apply { + putExtra(WebViewActivity.ARG_WEB_VIEW_LINK, link) + }.also { startActivity(it) } + } + + private fun moveToPlayStore() { + Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(getString(R.string.play_store_detail_url) + packageName) + }.also { + startActivity(it) + } + } + + companion object { + private const val TERMS_LINK = + "https://68space.notion.site/7d49b1a8912440cb9ec262392e5583e2" + private const val POLICY_LINK = + "https://68space.notion.site/9083a018baab42958103596378417c13" + const val ARG_HOME_GOAL_COUNT = "homeGoalCount" + const val ARG_USER_INFO = "userInfo" + private const val SCREEN_NAME = "mypage" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/MyViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/my/MyViewModel.kt new file mode 100644 index 00000000..5bef7ff3 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/MyViewModel.kt @@ -0,0 +1,154 @@ +package org.keepgoeat.presentation.my + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.domain.model.ArchivedGoal +import org.keepgoeat.domain.model.UserInfo +import org.keepgoeat.domain.repository.AuthRepository +import org.keepgoeat.domain.repository.GoalRepository +import org.keepgoeat.domain.repository.UserRepository +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.presentation.type.WithdrawReason +import org.keepgoeat.presentation.type.SortType +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.toStateFlow +import org.keepgoeat.util.mixpanel.SignEvent +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class MyViewModel @Inject constructor( + // TODO need refactoring + private val authRepository: AuthRepository, + private val goalRepository: GoalRepository, + private val userRepository: UserRepository, + private val localStorage: KGEDataSource, +) : MixpanelViewModel() { + private val _userInfo = MutableStateFlow(UserInfo("", "", 0)) + val userInfo get() = _userInfo.asStateFlow() + + private val _archivedGoalFetchUiState = + MutableStateFlow>>(UiState.Loading) + val archivedGoalFetchUiState get() = _archivedGoalFetchUiState.asStateFlow() + private val _archivedGoalCount = MutableStateFlow(0) + private val _allArchivedGoalCount = MutableStateFlow(0) + + private val _logoutUiState = MutableStateFlow>(UiState.Loading) + val logoutUiState get() = _logoutUiState.asStateFlow() + + private val _goalDeleteState = MutableStateFlow>(UiState.Loading) + val goalDeleteState get() = _goalDeleteState.asStateFlow() + private var _deletedGoalCount = 0 + val deletedGoalCount get() = _deletedGoalCount + + val archivedGoalCount get() = _archivedGoalCount.asStateFlow() + val allArchivedGoalCount get() = _allArchivedGoalCount.asStateFlow() + + private val _deleteAccountUiState = + MutableStateFlow>(UiState.Loading) + val deleteAccountUiState get() = _deleteAccountUiState.asStateFlow() + val otherReason = MutableStateFlow("") + val isValidOtherReason: StateFlow + get() = otherReason.map { reason -> + reason.isNotBlank() + }.toStateFlow(viewModelScope, false) + private val _isKeyboardVisible = MutableStateFlow(false) + val isKeyboardVisible get() = _isKeyboardVisible.asStateFlow() + private val _isOtherReasonSelected = MutableStateFlow(false) + val isOtherReasonSelected get() = _isOtherReasonSelected.asStateFlow() + private val _selectedReasons: MutableStateFlow> = + MutableStateFlow(arrayListOf()) + private val selectedReasons get() = _selectedReasons.asStateFlow() + val loginPlatForm = localStorage.loginPlatform + + fun fetchUserInfo() { + viewModelScope.launch { + userRepository.fetchUserInfo().onSuccess { userInfo -> + userInfo?.let { + _userInfo.value = userInfo + } + } + } + } + + fun fetchArchivedGoalBySort(sortType: SortType) { + viewModelScope.launch { + goalRepository.fetchArchivedGoal(sortType.name.lowercase()) + .onSuccess { + _archivedGoalFetchUiState.value = UiState.Success(it) + _archivedGoalCount.value = it.size + if (sortType == SortType.ALL) + _allArchivedGoalCount.value = it.size + }.onFailure { + _archivedGoalFetchUiState.value = UiState.Error(null) + } + } + } + + fun deleteArchivedGoal(id: Int) { + viewModelScope.launch { + goalRepository.deleteGoal(id).onSuccess { deletedData -> + _goalDeleteState.value = UiState.Success(deletedData.goalId) + _archivedGoalCount.value -= 1 + _allArchivedGoalCount.value -= 1 + _deletedGoalCount += 1 // TODO need refactoring + }.onFailure { + Timber.e(it.message) + } + } + } + + fun logout() { + localStorage.clear() + _logoutUiState.value = UiState.Success(true) + } + + fun deleteAccount() { + viewModelScope.launch { + authRepository.deleteAccount() + .onSuccess { + _deleteAccountUiState.value = UiState.Success(true) + }.onFailure { + _deleteAccountUiState.value = UiState.Error(it.message) + } + } + } + + fun setKeyboardVisibility(visible: Boolean) { + _isKeyboardVisible.value = visible + } + + fun onCheckBoxClick() { + _isOtherReasonSelected.value = !isOtherReasonSelected.value + } + + fun changeCheckboxSelected(isSelected: Boolean) { + _isOtherReasonSelected.value = isSelected + } + + fun selectReasons(isSelected: WithdrawReason) { + if (_selectedReasons.value.contains(isSelected)) { + _selectedReasons.value.remove(isSelected) + return + } + _selectedReasons.value.add(isSelected) + } + + fun sendDeleteAccountEvent(reasons: Map?) { + mixpanelProvider.sendEvent(SignEvent.deleteAccount(reasons)) + } + + fun getWithdrawReasons(): MutableMap { + val reasons: MutableMap = + selectedReasons.value.associate { it.name to it.reason }.toMutableMap() + if (isOtherReasonSelected.value) + reasons["SUBJECTIVE_ISSUE"] = otherReason.value + return reasons + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/ServiceIntroActivity.kt b/app/src/main/java/org/keepgoeat/presentation/my/ServiceIntroActivity.kt new file mode 100644 index 00000000..356d1268 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/ServiceIntroActivity.kt @@ -0,0 +1,44 @@ +package org.keepgoeat.presentation.my + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityServiceIntroBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.common.WebViewActivity +import org.keepgoeat.presentation.common.WebViewActivity.Companion.ARG_WEB_VIEW_LINK + +@AndroidEntryPoint +class ServiceIntroActivity : + MixpanelActivity(R.layout.activity_service_intro, SCREEN_NAME) { + override val viewModel: MyViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + addListeners() + } + + private fun addListeners() { + binding.viewToolbar.ivBack.setOnClickListener { + finish() + } + binding.tvOpenSource.setOnClickListener { + moveToOpenSourcePage() + } + } + + private fun moveToOpenSourcePage() { + Intent(this, WebViewActivity::class.java).apply { + putExtra(ARG_WEB_VIEW_LINK, OPEN_SOURCE_LINK) + }.also { startActivity(it) } + } + + companion object { + private const val OPEN_SOURCE_LINK = + "https://68space.notion.site/cd16c9399dfe4c7a867deb59851282e3" + private const val SCREEN_NAME = "service intro" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalActivity.kt b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalActivity.kt new file mode 100644 index 00000000..1372d61b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalActivity.kt @@ -0,0 +1,154 @@ +package org.keepgoeat.presentation.my.archive + +import android.content.Intent +import android.os.Bundle +import android.view.MotionEvent +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.ConcatAdapter +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityArchivedGoalBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.my.MyActivity +import org.keepgoeat.presentation.my.MyViewModel +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.presentation.type.SortType +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.showToast + +@AndroidEntryPoint +class ArchivedGoalActivity : + MixpanelActivity(R.layout.activity_archived_goal, SCREEN_NAME) { + override val viewModel: MyViewModel by viewModels() + lateinit var goalAdapter: ArchivedGoalAdapter + private val headerAdapter = ArchivedGoalHeaderAdapter(::getFilteredGoalWithEatingType) + lateinit var goalConcatAdapter: ConcatAdapter + private var isEnteredFromKeep: Boolean = false + + private val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + moveToPrevious() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + isEnteredFromKeep = intent.getBooleanExtra(ARG_IS_ENTERED_FROM_KEEP, false) + + viewModel.fetchArchivedGoalBySort(SortType.ALL) + goalAdapter = ArchivedGoalAdapter(::showKeepDeleteDialog) + goalConcatAdapter = ConcatAdapter(headerAdapter, goalAdapter) + + initLayout() + addListeners() + collectData() + } + + override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { + goalAdapter.checkForVisibleDeleteButton() + return super.dispatchTouchEvent(ev) + } + + private fun initLayout() { + binding.rvGoalList.apply { + adapter = goalConcatAdapter + itemAnimator = null + } + this.onBackPressedDispatcher.addCallback(this, callback) + } + + private fun getFilteredGoalWithEatingType(eatingType: EatingType?) { + when (eatingType) { + null -> viewModel.fetchArchivedGoalBySort(SortType.ALL) + EatingType.MORE -> viewModel.fetchArchivedGoalBySort(SortType.MORE) + EatingType.LESS -> viewModel.fetchArchivedGoalBySort(SortType.LESS) + } + } + + private fun addListeners() { + binding.viewToolbar.ivBack.setOnClickListener { + moveToPrevious() + } + binding.btnMoreKeep.setOnClickListener { + moveToHome() + } + } + + private fun collectData() { + viewModel.archivedGoalFetchUiState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + goalAdapter.setGoalList(it.data.toMutableList()) + } + is UiState.Error -> {} + is UiState.Loading -> {} + else -> {} + } + }.launchIn(lifecycleScope) + + viewModel.goalDeleteState.flowWithLifecycle(lifecycle).onEach { deleteState -> + when (deleteState) { + is UiState.Success -> { + goalAdapter.removeGoal(deleteState.data) + showToast(getString(R.string.goal_detail_success_goal_delete_toast_message)) + } + else -> {} + } + }.launchIn(lifecycleScope) + + viewModel.allArchivedGoalCount.flowWithLifecycle(lifecycle).onEach { allAchievedGoalCount -> + val homeGoalCount = intent.getIntExtra(ARG_HOME_GOAL_COUNT, -1) + if (homeGoalCount == 0 && allAchievedGoalCount == 0) { + binding.btnMoreKeep.visibility = View.VISIBLE + } else { + binding.btnMoreKeep.visibility = View.INVISIBLE + } + }.launchIn(lifecycleScope) + } + + private fun moveToHome() { + val intent = Intent(this, HomeActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + } + + private fun moveToMy() { + if (viewModel.deletedGoalCount > 0) + Intent(this, MyActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + }.also { + startActivity(it) + } + else + finish() + } + + private fun moveToPrevious() { + if (isEnteredFromKeep) moveToHome() + else moveToMy() + } + + private fun showKeepDeleteDialog(goalId: Int) { + GoalDeleteDialogFragment().apply { + arguments = Bundle().apply { + putInt(ARG_GOAL_ID, goalId) + } + }.show(supportFragmentManager, "archivedGoalDeleteDialog") + } + + companion object { + const val ARG_IS_ENTERED_FROM_KEEP = "isEnteredFromKeep" + const val ARG_GOAL_ID = "goalId" + const val ARG_HOME_GOAL_COUNT = "homeGoalCount" + const val SCREEN_NAME = "archived goal" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalAdapter.kt new file mode 100644 index 00000000..010506f2 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalAdapter.kt @@ -0,0 +1,84 @@ +package org.keepgoeat.presentation.my.archive + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.databinding.ItemArchivedGoalBinding +import org.keepgoeat.domain.model.ArchivedGoal + +class ArchivedGoalAdapter( + private val showKeepDeleteDialog: (Int) -> Unit, +) : RecyclerView.Adapter() { + private lateinit var inflater: LayoutInflater + private val goalLists: MutableList = mutableListOf() + private var goalWithDeleteViewVisible: ArchivedGoal? = null + + class ArchivedGoalViewHolder(private val binding: ItemArchivedGoalBinding) : + RecyclerView.ViewHolder(binding.root) { + fun onBind( + data: ArchivedGoal, + showKeepDeleteDialog: (Int) -> Unit, + showDeleteButton: (Button, ArchivedGoal) -> Unit, + hideDeleteButton: (Button) -> Unit, + ) { + binding.goal = data + + if (binding.btnArchivedGoalDelete.visibility == View.VISIBLE) + hideDeleteButton(binding.btnArchivedGoalDelete) + + binding.ivArchivedGoalDetail.setOnClickListener { + if (binding.btnArchivedGoalDelete.visibility == View.INVISIBLE) + showDeleteButton(binding.btnArchivedGoalDelete, data) + } + binding.btnArchivedGoalDelete.setOnClickListener { + showKeepDeleteDialog(data.id) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArchivedGoalViewHolder { + if (!::inflater.isInitialized) + inflater = LayoutInflater.from(parent.context) + + return ArchivedGoalViewHolder(ItemArchivedGoalBinding.inflate(inflater, parent, false)) + } + + override fun onBindViewHolder(holder: ArchivedGoalViewHolder, position: Int) { + holder.onBind( + goalLists[position], + showKeepDeleteDialog, + ::showDeleteButton, + ::hideDeleteButton + ) + } + + override fun getItemCount() = goalLists.size + + fun setGoalList(goals: MutableList) { + goalLists.clear() + goalLists.addAll(goals) + notifyDataSetChanged() + } + + private fun hideDeleteButton(deleteButton: Button) { + deleteButton.visibility = View.INVISIBLE + goalWithDeleteViewVisible = null + } + + private fun showDeleteButton(deleteButton: Button, goal: ArchivedGoal) { + deleteButton.visibility = View.VISIBLE + goalWithDeleteViewVisible = goal + } + + fun checkForVisibleDeleteButton() { + if (goalWithDeleteViewVisible == null) return + else notifyItemChanged(goalLists.indexOf(goalWithDeleteViewVisible)) + } + + fun removeGoal(goalId: Int) { + val goal = goalLists.find { it.id == goalId } + notifyItemRemoved(goalLists.indexOf(goal)) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalHeaderAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalHeaderAdapter.kt new file mode 100644 index 00000000..8bb48a7f --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/archive/ArchivedGoalHeaderAdapter.kt @@ -0,0 +1,69 @@ +package org.keepgoeat.presentation.my.archive + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.R +import org.keepgoeat.databinding.LayoutArchivedGoalHeaderBinding +import org.keepgoeat.presentation.type.EatingType + +class ArchivedGoalHeaderAdapter(private val eatingTypeClickListener: ((EatingType?) -> Unit)) : + RecyclerView.Adapter() { + private lateinit var inflater: LayoutInflater + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ArchivedGoalHeaderViewHolder { + if (!::inflater.isInitialized) + inflater = LayoutInflater.from(parent.context) + + return ArchivedGoalHeaderViewHolder( + LayoutArchivedGoalHeaderBinding.inflate(inflater, parent, false) + ) + } + + override fun onBindViewHolder(holder: ArchivedGoalHeaderViewHolder, position: Int) { + holder.onBind(eatingTypeClickListener, ::changeTextAppearance) + } + + override fun getItemCount() = 1 + + private fun changeTextAppearance( + clickedView: TextView, + unclickedView1: TextView, + unclickedView2: TextView, + ) { + clickedView.setTextAppearance(R.style.TextAppearance_System5_Bold) + unclickedView1.setTextAppearance(R.style.TextAppearance_System5) + unclickedView2.setTextAppearance(R.style.TextAppearance_System5) + clickedView.setTextColor(clickedView.context.getColor(R.color.gray_800)) + unclickedView1.setTextColor(clickedView.context.getColor(R.color.gray_400)) + unclickedView2.setTextColor(clickedView.context.getColor(R.color.gray_400)) + } + + class ArchivedGoalHeaderViewHolder( + private val binding: LayoutArchivedGoalHeaderBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun onBind( + eatingTypeClickListener: ((EatingType?) -> Unit), + changeTextAppearance: (TextView, TextView, TextView) -> Unit, + ) { + with(binding) { + tvAll.setOnClickListener { + eatingTypeClickListener(null) + changeTextAppearance(tvAll, tvMore, tvLess) + } + tvMore.setOnClickListener { + eatingTypeClickListener(EatingType.MORE) + changeTextAppearance(tvMore, tvAll, tvLess) + } + tvLess.setOnClickListener { + eatingTypeClickListener(EatingType.LESS) + changeTextAppearance(tvLess, tvAll, tvMore) + } + } + } + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/archive/GoalDeleteDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/my/archive/GoalDeleteDialogFragment.kt new file mode 100644 index 00000000..55df0eff --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/archive/GoalDeleteDialogFragment.kt @@ -0,0 +1,42 @@ +package org.keepgoeat.presentation.my.archive + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import org.keepgoeat.R +import org.keepgoeat.databinding.DialogGoalDeleteBinding +import org.keepgoeat.presentation.my.MyViewModel +import org.keepgoeat.presentation.base.screen.BindingDialogFragment + +class GoalDeleteDialogFragment : + BindingDialogFragment(R.layout.dialog_goal_delete) { + private val viewModel: MyViewModel by activityViewModels() + private var goalId: Int? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + goalId = arguments?.getInt(ARG_GOAL_ID) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.viewModel = viewModel + addListeners() + } + + private fun addListeners() { + binding.no.setOnClickListener { + dismiss() + } + binding.yes.setOnClickListener { + goalId?.let { goalId -> + viewModel.deleteArchivedGoal(goalId) + } + dismiss() + } + } + + companion object { + const val ARG_GOAL_ID = "goalId" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawActivity.kt b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawActivity.kt new file mode 100644 index 00000000..45e01145 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawActivity.kt @@ -0,0 +1,143 @@ +package org.keepgoeat.presentation.my.withdraw + +import android.graphics.Rect +import android.os.Bundle +import android.view.View +import android.view.ViewTreeObserver +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityWithdrawBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.my.MyViewModel +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.showKeyboard +import org.keepgoeat.util.setVisibility + +@AndroidEntryPoint +class WithdrawActivity : + MixpanelActivity(R.layout.activity_withdraw, SCREEN_NAME) { + override val viewModel: MyViewModel by viewModels() + lateinit var onGlobalListener: ViewTreeObserver.OnGlobalLayoutListener + lateinit var rootView: View + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + rootView = binding.root + + initLayout() + addListeners() + collectData() + } + + override fun onStart() { + super.onStart() + rootView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener) + } + + override fun onStop() { + super.onStop() + rootView.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) + } + + private fun initLayout() { + binding.rvWithdraw.apply { + itemAnimator = null + adapter = WithdrawReasonAdapter(viewModel::selectReasons) + } + + initGlobalListener() + } + + private fun initGlobalListener() { + onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener { + val rect = Rect() + rootView.getWindowVisibleDisplayFrame(rect) + val screenHeight = rootView.rootView.height + val keypadHeight = screenHeight - rect.bottom + viewModel.setKeyboardVisibility(keypadHeight > screenHeight * 0.15) + } + } + + private fun addListeners() { + binding.etOtherReason.setOnFocusChangeListener { _, focused -> + if (focused) { // '์ง์ ‘ ์ž…๋ ฅ' editText๊ฐ€ focus ์ƒํƒœ์ผ ๋•Œ + requestFocus() + viewModel.changeCheckboxSelected(true) + } else { + clearFocus() + } + } + binding.layoutWithdraw.setOnClickListener { // ์™ธ๋ถ€ ์˜์—ญ ํด๋ฆญ ํ–ˆ์„ ๋•Œ + clearFocus() + } + binding.btnWithdraw.setOnClickListener { + if (binding.etOtherReason.text.isNullOrBlank() && viewModel.isOtherReasonSelected.value) + binding.tvOtherReasonErrorMsg.setVisibility(true) + else + WithdrawDialogFragment().show( + supportFragmentManager, + "withdrawDialog" + ) + } + binding.viewWithdrawToolbar.ivBack.setOnClickListener { + finish() + } + } + + private fun collectData() { + viewModel.isOtherReasonSelected.flowWithLifecycle(lifecycle).onEach { isSelected -> + if (isSelected) { + requestFocus() + } else { + clearFocus() + } + }.launchIn(lifecycleScope) + viewModel.isValidOtherReason.flowWithLifecycle(lifecycle).onEach { isValid -> + if (isValid) + binding.tvOtherReasonErrorMsg.setVisibility(false) + }.launchIn(lifecycleScope) + viewModel.isKeyboardVisible.flowWithLifecycle(lifecycle).onEach { isVisible -> + binding.rvWithdraw.setVisibility(!isVisible) + }.launchIn(lifecycleScope) + viewModel.deleteAccountUiState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> sendDeleteAccountEvent() + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun sendDeleteAccountEvent() { + val reasons: MutableMap = mutableMapOf() + viewModel.getWithdrawReasons().map { + if (it.key == SUBJECTIVE_ISSUE) { + reasons[it.key] = it.value + } else { + reasons[it.key] = (it.value as? Int)?.let { value -> getString(value) } ?: "" + } + } + viewModel.sendDeleteAccountEvent(reasons) + } + + private fun clearFocus() { + binding.etOtherReason.clearFocus() + showKeyboard(binding.etOtherReason, false) + } + + private fun requestFocus() { + binding.etOtherReason.requestFocus() + showKeyboard(binding.etOtherReason, true) + } + + companion object { + private const val SUBJECTIVE_ISSUE = "SUBJECTIVE_ISSUE" + private const val SCREEN_NAME = "account_delete" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawDialogFragment.kt b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawDialogFragment.kt new file mode 100644 index 00000000..717700c9 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawDialogFragment.kt @@ -0,0 +1,78 @@ +package org.keepgoeat.presentation.my.withdraw + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.data.service.KakaoAuthService +import org.keepgoeat.data.service.NaverAuthService +import org.keepgoeat.databinding.DialogWithdrawBinding +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.my.MyViewModel +import org.keepgoeat.presentation.type.LoginPlatformType +import org.keepgoeat.util.UiState +import org.keepgoeat.presentation.base.screen.BindingDialogFragment +import org.keepgoeat.util.extension.showToast +import javax.inject.Inject + +@AndroidEntryPoint +class WithdrawDialogFragment : + BindingDialogFragment(R.layout.dialog_withdraw) { + @Inject + lateinit var kakaoSignService: KakaoAuthService + + @Inject + lateinit var naverSignService: NaverAuthService + private val viewModel: MyViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + addListeners() + collectData() + } + + private fun addListeners() { + binding.tvCancel.setOnClickListener { + dismiss() + } + binding.tvWithdraw.setOnClickListener { + when (viewModel.loginPlatForm) { + LoginPlatformType.NAVER -> naverSignService.deleteAccountNaver(viewModel::deleteAccount) + LoginPlatformType.KAKAO -> kakaoSignService.deleteAccountKakao(viewModel::deleteAccount) + else -> {} + } + } + } + + private fun collectData() { + viewModel.deleteAccountUiState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + requireActivity().showToast(getString(R.string.withdraw_success)) + moveToSign() + dismiss() + } + is UiState.Error -> { + requireActivity().showToast(getString(R.string.withdraw_fail)) + dismiss() + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun moveToSign() { + Intent(context, HomeActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + putExtra(HomeActivity.ARG_KILL_HOME_AND_GO_TO_SIGN, true) + }.also { + startActivity(it) + } + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawReasonAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawReasonAdapter.kt new file mode 100644 index 00000000..5032dda8 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/my/withdraw/WithdrawReasonAdapter.kt @@ -0,0 +1,52 @@ +package org.keepgoeat.presentation.my.withdraw + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.databinding.ItemWithdrawBinding +import org.keepgoeat.presentation.type.WithdrawReason +import org.keepgoeat.util.ItemDiffCallback + +class WithdrawReasonAdapter( + private val selectReasons: (WithdrawReason) -> Unit, +) : + ListAdapter( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.reason == new.reason } + ) + ) { + private lateinit var inflater: LayoutInflater + + init { + submitList(WithdrawReason.values().toMutableList()) + } + + class WithdrawViewHolder( + private val binding: ItemWithdrawBinding, + ) : RecyclerView.ViewHolder(binding.root) { + var isClicked: Boolean = false + fun bind( + reason: WithdrawReason, + selectReasons: (WithdrawReason) -> Unit, + ) { + binding.reason = reason.reason + binding.layoutWithdrawReason.setOnClickListener { + isClicked = !isClicked + binding.isClicked = isClicked + selectReasons(reason) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WithdrawViewHolder { + if (!::inflater.isInitialized) + inflater = LayoutInflater.from(parent.context) + return WithdrawViewHolder(ItemWithdrawBinding.inflate(inflater, parent, false)) + } + + override fun onBindViewHolder(holder: WithdrawViewHolder, position: Int) { + holder.bind(currentList[position], selectReasons) + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingActivity.kt b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingActivity.kt new file mode 100644 index 00000000..06a844af --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingActivity.kt @@ -0,0 +1,86 @@ +package org.keepgoeat.presentation.onboarding + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityOnboardingBinding +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.base.screen.BindingActivity + +@AndroidEntryPoint +class OnboardingActivity : + BindingActivity(R.layout.activity_onboarding) { + private val viewModel: OnboardingViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + initLayout() + addListeners() + } + + override fun onStop() { + super.onStop() + stopRecodingScreenTime(viewModel.onboardingType.value.ordinal) + } + + private fun initLayout() { + with(binding) { + vpViewPager.adapter = OnboardingAdapter(this@OnboardingActivity) + vpViewPager.registerOnPageChangeCallback(getPageChangeCallback()) + TabLayoutMediator(indicator, vpViewPager) { _, _ -> }.attach() + } + } + + private fun getPageChangeCallback() = + object : ViewPager2.OnPageChangeCallback() { + var prevPos = viewModel.onboardingType.value.ordinal + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + + viewModel.setOnboardingType(position) + recodeScreenTime(prevPos, position) + prevPos = position + } + } + + private fun addListeners() { + binding.btnNext.setOnClickListener { + if (binding.vpViewPager.currentItem == viewModel.onboardingLastPos) { + viewModel.setClickedOnboardingButton() + moveToHome() + } + binding.vpViewPager.currentItem++ + } + binding.tvSkip.setOnClickListener { + viewModel.setClickedOnboardingButton() + moveToHome() + } + } + + private fun moveToHome() { + startActivity(Intent(this, HomeActivity::class.java)) + finish() + } + + /** ์ด์ „ ํฌ์ง€์…˜ ์˜จ๋ณด๋”ฉ ๋ทฐ์˜ ์Šคํฌ๋ฆฐํƒ€์ž„ ๊ธฐ๋ก์„ ์ค‘๋‹จ, ํ˜„์žฌ ํฌ์ง€์…˜ ์˜จ๋ณด๋”ฉ ๋ทฐ์˜ ์Šคํฌ๋ฆฐํƒ€์ž„์„์„ ๊ธฐ๋กํ•˜๋Š” ํ•จ์ˆ˜ (๋‹จ, ์ดˆ๊ธฐ ํฌ์ง€์…˜์ด 0์ธ ๊ฒฝ์šฐ, if๋ฌธ์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์Œ.) */ + private fun recodeScreenTime(prevPos: Int, curPos: Int) { + if (prevPos != curPos) stopRecodingScreenTime(prevPos) + viewModel.startRecodingScreenTime() + } + + private fun stopRecodingScreenTime(pos: Int) { + viewModel.stopRecodingScreenTime("$SCREEN_NAME ${pos + 1}") + } + + companion object { + private const val SCREEN_NAME = "onboarding" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingAdapter.kt b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingAdapter.kt new file mode 100644 index 00000000..e5730695 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingAdapter.kt @@ -0,0 +1,34 @@ +package org.keepgoeat.presentation.onboarding + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.databinding.ItemOnboardingBinding +import org.keepgoeat.presentation.type.OnBoardingViewType + +class OnboardingAdapter(context: Context) : + RecyclerView.Adapter() { + private val inflater by lazy { LayoutInflater.from(context) } + private val onBoardingList = OnBoardingViewType.values() + + init { + notifyDataSetChanged() + } + + class OnBoardingViewHolder(private val binding: ItemOnboardingBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(onboarding: OnBoardingViewType) { + binding.item = onboarding + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OnBoardingViewHolder = + OnBoardingViewHolder(ItemOnboardingBinding.inflate(inflater, parent, false)) + + override fun onBindViewHolder(holder: OnBoardingViewHolder, position: Int) { + holder.bind(onBoardingList[position]) + } + + override fun getItemCount(): Int = onBoardingList.size +} diff --git a/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingViewModel.kt new file mode 100644 index 00000000..d89f5cb5 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/onboarding/OnboardingViewModel.kt @@ -0,0 +1,29 @@ +package org.keepgoeat.presentation.onboarding + +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.presentation.type.OnBoardingViewType +import javax.inject.Inject + +@HiltViewModel +class OnboardingViewModel @Inject constructor(private val localStorage: KGEDataSource) : + MixpanelViewModel() { + private val _onboardingType = MutableStateFlow(OnBoardingViewType.FIRST) + val onboardingType = _onboardingType.asStateFlow() + val onboardingLastPos = OnBoardingViewType.values().size - 1 + + fun setOnboardingType(position: Int) { + _onboardingType.value = when (position) { + OnBoardingViewType.FIRST.ordinal -> OnBoardingViewType.FIRST + OnBoardingViewType.SECOND.ordinal -> OnBoardingViewType.SECOND + else -> OnBoardingViewType.THIRD + } + } + + fun setClickedOnboardingButton() { + localStorage.isClickedOnboardingButton = true + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingActivity.kt b/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingActivity.kt new file mode 100644 index 00000000..efb510c0 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingActivity.kt @@ -0,0 +1,114 @@ +package org.keepgoeat.presentation.setting + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.databinding.ActivityGoalSettingBinding +import org.keepgoeat.presentation.detail.GoalDetailActivity +import org.keepgoeat.presentation.detail.GoalDetailActivity.Companion.ARG_GOAL_ID +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.model.GoalContent +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.util.UiState +import org.keepgoeat.presentation.base.screen.BindingActivity +import org.keepgoeat.util.extension.* +import org.keepgoeat.util.safeValueOf + +@AndroidEntryPoint +class GoalSettingActivity : + BindingActivity(R.layout.activity_goal_setting) { + private val viewModel: GoalSettingViewModel by viewModels() + private var isEditMode: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + binding.lifecycleOwner = this + + intent.let { + it.getStringExtra(ARG_EATING_TYPE)?.let { strExtra -> + val eatingType = safeValueOf(strExtra) ?: return@let + viewModel.setEatingType(eatingType) + } + + it.getParcelable(ARG_GOAL_CONTENT, GoalContent::class.java)?.let { goal -> + isEditMode = true + viewModel.setGoalContent(goal) + } + } + + addListeners() + collectData() + } + + override fun onStart() { + super.onStart() + viewModel.startRecodingScreenTime() + } + + override fun onStop() { + super.onStop() + viewModel.stopRecodingScreenTime(viewModel.eatingType.value?.name?.lowercase() ?: "") + } + + private fun collectData() { + viewModel.uploadState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + if (isEditMode) { + showToast(getString(R.string.goal_setting_edit_success_toast_message)) + moveToDetail() + } else { + showToast(getString(R.string.goal_setting_add_success_toast_message)) + moveToHome() + } + } + is UiState.Error -> {} + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun addListeners() { + binding.root.setOnClickListener { + showKeyboard(it, false) + binding.etGoal.clearFocus() + binding.etGoalCriterion.clearFocus() + } + binding.viewToolbar.ivBack.setOnClickListener { + finish() + } + binding.btnComplete.setOnSingleClickListener { + showKeyboard(it, false) + viewModel.uploadGoal() + } + } + + private fun moveToHome() { + val intent = Intent(this, HomeActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + } + + private fun moveToDetail() { + val intent = Intent(this, GoalDetailActivity::class.java).apply { + putExtra(ARG_GOAL_ID, viewModel.goalId) + putExtra(ARG_IS_UPDATED, true) + putExtra(ARG_EATING_TYPE, viewModel.eatingType.value?.name) + } + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + } + + companion object { + const val ARG_EATING_TYPE = "eatingType" + const val ARG_GOAL_CONTENT = "goalContent" + const val ARG_IS_UPDATED = "isUpdated" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingViewModel.kt new file mode 100644 index 00000000..3cf36f83 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/setting/GoalSettingViewModel.kt @@ -0,0 +1,82 @@ +package org.keepgoeat.presentation.setting + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.keepgoeat.domain.repository.GoalRepository +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.presentation.model.GoalContent +import org.keepgoeat.presentation.type.EatingType +import org.keepgoeat.util.UiState +import org.keepgoeat.util.mixpanel.GoalEvent +import org.keepgoeat.util.safeLet +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class GoalSettingViewModel @Inject constructor( + private val goalRepository: GoalRepository, +) : MixpanelViewModel() { + val goalFood = MutableStateFlow(null) + val goalCriterion = MutableStateFlow("") + private val _eatingType = MutableStateFlow(null) + val eatingType get() = _eatingType.asStateFlow() + var goalId: Int? = null + + private val _uploadState = MutableStateFlow>(UiState.Loading) + val uploadState: StateFlow> get() = _uploadState + + fun uploadGoal() { + if (goalId == null) addGoal() + else editGoal() + } + + private fun addGoal() { + val title = goalFood.value?.trim() ?: return + val criterion = goalCriterion.value.trim() + + viewModelScope.launch { + goalRepository.uploadGoalContent( + title, + criterion, + eatingType.value == EatingType.MORE + ).onSuccess { + _uploadState.value = UiState.Success(it) + with(mixpanelProvider) { + createGoal() + sendEvent(GoalEvent.createGoal(title, criterion)) + } + }.onFailure { + _uploadState.value = UiState.Error(it.message) + Timber.e(it.message) + } + } + } + + private fun editGoal() { + viewModelScope.launch { + safeLet(goalId, goalFood.value, goalCriterion.value) { id, food, criterion -> + goalRepository.editGoalContent(id, food, criterion) + .onSuccess { + _uploadState.value = UiState.Success(it) + }.onFailure { + _uploadState.value = UiState.Error(it.message) + Timber.e(it.message) + } + } + } + } + + fun setEatingType(eatingType: EatingType) { + _eatingType.value = eatingType + } + + fun setGoalContent(goal: GoalContent) { + goalId = goal.id + goalFood.value = goal.food + goalCriterion.value = goal.criterion + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/sign/SignActivity.kt b/app/src/main/java/org/keepgoeat/presentation/sign/SignActivity.kt new file mode 100644 index 00000000..8a9385da --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/sign/SignActivity.kt @@ -0,0 +1,75 @@ +package org.keepgoeat.presentation.sign + +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.keepgoeat.R +import org.keepgoeat.data.service.KakaoAuthService +import org.keepgoeat.data.service.NaverAuthService +import org.keepgoeat.databinding.ActivitySignBinding +import org.keepgoeat.presentation.base.screen.MixpanelActivity +import org.keepgoeat.presentation.home.HomeActivity +import org.keepgoeat.presentation.onboarding.OnboardingActivity +import org.keepgoeat.util.UiState +import org.keepgoeat.util.extension.showToast +import javax.inject.Inject + +@AndroidEntryPoint +class SignActivity : MixpanelActivity(R.layout.activity_sign, SCREEN_NAME) { + @Inject + lateinit var kakaoSignService: KakaoAuthService + + @Inject + lateinit var naverSignService: NaverAuthService + override val viewModel: SignViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + addListeners() + collectData() + } + + private fun addListeners() { + binding.layoutKakaoSignIn.setOnClickListener { + kakaoSignService.loginKakao(viewModel::login, viewModel::saveAccount) + } + binding.layoutNaverSignIn.setOnClickListener { + naverSignService.loginNaver(viewModel::login, viewModel::saveAccount) + } + } + + private fun collectData() { + viewModel.loginUiState.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + showToast(getString(R.string.signin_success_toast_message)) + moveToNext(it.data) + } + is UiState.Error -> { + showToast(getString(R.string.signin_failure_toast_message)) + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + /** ๊ฐ€์ž…์ž๊ฐ€ ์˜จ๋ณด๋”ฉ ๋ชจ๋‘ ํ™•์ธ(์ƒ๋žต ํฌํ•จ)ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์˜จ๋ณด๋”ฉ ํ™”๋ฉด์œผ๋กœ ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ(๊ฐ€์ž…์ž๊ฐ€ ์˜จ๋ณด๋”ฉ์—์„œ ์ค‘๊ฐ„ ์ดํƒˆ + ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž)๋Š” ํ™ˆํ™”๋ฉด์œผ๋กœ ์ด๋™ */ + /** @param onboardingFlag : first๋Š” ๊ฐ€์ž…์ž ์—ฌ๋ถ€(true: ๊ฐ€์ž…ํ•œ ์‚ฌ์šฉ์ž, false: ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž), second๋Š” ์˜จ๋ณด๋”ฉ ์™„๋ฃŒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ */ + private fun moveToNext(onboardingFlag: Pair) { + val nextScreen = + if (onboardingFlag.first && !onboardingFlag.second) OnboardingActivity::class.java + else HomeActivity::class.java + startActivity(Intent(this, nextScreen)) + finish() + } + + companion object { + private const val SCREEN_NAME = "signup" + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/sign/SignViewModel.kt b/app/src/main/java/org/keepgoeat/presentation/sign/SignViewModel.kt new file mode 100644 index 00000000..c677dce0 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/sign/SignViewModel.kt @@ -0,0 +1,67 @@ +package org.keepgoeat.presentation.sign + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.data.model.request.RequestAuth +import org.keepgoeat.domain.model.AccountInfo +import org.keepgoeat.domain.repository.AuthRepository +import org.keepgoeat.domain.type.SignType +import org.keepgoeat.presentation.base.viewmodel.MixpanelViewModel +import org.keepgoeat.presentation.type.LoginPlatformType +import org.keepgoeat.util.UiState +import org.keepgoeat.util.mixpanel.SignEvent +import javax.inject.Inject + +@HiltViewModel +class SignViewModel @Inject constructor( + private val authRepository: AuthRepository, + private val localStorage: KGEDataSource, +) : MixpanelViewModel() { + private val _loginUiState = MutableStateFlow>>(UiState.Loading) + val loginUiState get() = _loginUiState.asStateFlow() + + fun login(loginPlatForm: LoginPlatformType, accessToken: String) { + localStorage.loginPlatform = loginPlatForm + viewModelScope.launch { + authRepository.login( + RequestAuth(accessToken, loginPlatForm.name) + ).onSuccess { + val isSignUp = it?.signType == SignType.SIGN_UP + _loginUiState.value = UiState.Success( + Pair(isSignUp, localStorage.isClickedOnboardingButton) + ) + initMixpanelGoalNumber(isSignUp) + sendSignEventLog(it?.signType, loginPlatForm.label) + }.onFailure { + _loginUiState.value = UiState.Error(it.message) + } + } + } + + fun saveAccount(accountInfo: AccountInfo) { + localStorage.userName = accountInfo.name + localStorage.userEmail = accountInfo.email + mixpanelProvider.setUserProfile() + } + + /** ํšŒ์›๊ฐ€์ž… ์‹œ์—๋งŒ ์œ ์ € ํ”„๋กœํผํ‹ฐ > ๋ชฉํ‘œ์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜ */ + private fun initMixpanelGoalNumber(isSignUp: Boolean) { + if (isSignUp) mixpanelProvider.initUserGoalNumber() + } + + private fun sendSignEventLog(signType: SignType?, platform: String) { + when (signType) { + SignType.SIGN_UP -> { + mixpanelProvider.sendEvent(SignEvent.completeSignUp(platform)) + } + SignType.SIGN_IN -> { + mixpanelProvider.sendEvent(SignEvent.completeLogin()) + } + else -> {} + } + } +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/EatingType.kt b/app/src/main/java/org/keepgoeat/presentation/type/EatingType.kt new file mode 100644 index 00000000..d1231d2b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/EatingType.kt @@ -0,0 +1,61 @@ +package org.keepgoeat.presentation.type + +import android.graphics.Color +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.keepgoeat.R + +enum class EatingType( + @StringRes val strRes: Int, + @StringRes val titleStrRes: Int, + @StringRes val achievementCountTitle: Int, + @DrawableRes val snailStickerRes: Int, + @DrawableRes val defaultStickerRes: Int, + @DrawableRes val tagIconRes: Int, + @DrawableRes val snailCheerInKeepRes: Int, + @ColorRes val tagTextColor: Int, + @ColorRes val tagBackgroundColor: Int, + @ColorRes val thisMonthTextColor: Int, + @ColorRes val buttonBackgroundColor: Int, + @ColorRes val buttonTextColor: Int, + @ColorRes val buttonRippleColor: Int, + @ColorRes val achievedGoalDayTextColor: Int, + @ColorInt val cardBackgroundColor: Int, +) { + LESS( + R.string.eating_type_less, + R.string.title_less, + R.string.goal_detail_hold, + R.drawable.ic_snail_green_sticker, + R.drawable.ic_default_green_sticker, + R.drawable.ic_eating_less_tag_minus, + R.drawable.ic_snail_green_cheer_left, + R.color.green_600, + R.color.green_200, + R.color.green_700, + R.color.green_500, + R.color.white, + R.color.green_700, + R.color.green_600, + Color.parseColor("#EAFBF4"), + ), + MORE( + R.string.eating_type_more, + R.string.title_more, + R.string.goal_detail_eat, + R.drawable.ic_snail_orange_sticker, + R.drawable.ic_default_orange_sticker, + R.drawable.ic_eating_more_tag_plus, + R.drawable.ic_snail_orange_cheer_left, + R.color.orange_600, + R.color.orange_100, + R.color.orange_600, + R.color.orange_600, + R.color.gray_50, + R.color.orange_700, + R.color.orange_500, + Color.parseColor("#FFF0EB"), + ) +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/HomeBackgroundType.kt b/app/src/main/java/org/keepgoeat/presentation/type/HomeBackgroundType.kt new file mode 100644 index 00000000..3e00a75d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/HomeBackgroundType.kt @@ -0,0 +1,12 @@ +package org.keepgoeat.presentation.type + +import androidx.annotation.DrawableRes +import org.keepgoeat.R + +enum class HomeBackgroundType( + @DrawableRes val sky: Int +) { + MORNING(R.drawable.img_home_background_morning), + EVENING(R.drawable.img_home_background_evening), + NIGHT(R.drawable.img_home_background_night) +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/HomeBtnType.kt b/app/src/main/java/org/keepgoeat/presentation/type/HomeBtnType.kt new file mode 100644 index 00000000..c51edb08 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/HomeBtnType.kt @@ -0,0 +1,38 @@ +package org.keepgoeat.presentation.type + +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.keepgoeat.R + +enum class HomeBtnType( + @ColorRes val btnColorInt: Int, + @DrawableRes val btnDrawableRes: Int, + @StringRes val btnStringRes: Int, + @ColorRes val btnTextColorInt: Int +) { + MINUS_ACHIEVED( + R.color.green_100, + R.drawable.ic_check_green, + R.string.home_goal_less_achieved, + R.color.green_600 + ), + MINUS_NOT_ACHIEVED( + R.color.green_500, + R.drawable.ic_check, + R.string.home_goal_less_btn, + R.color.gray_50 + ), + PLUS_ACHIEVED( + R.color.orange_50, + R.drawable.ic_check_orange, + R.string.home_goal_more_achieved, + R.color.orange_600 + ), + PLUS_NOT_ACHIEVED( + R.color.orange_600, + R.drawable.ic_check, + R.string.home_goal_more_btn, + R.color.gray_50 + ) +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/HomeGoalViewType.kt b/app/src/main/java/org/keepgoeat/presentation/type/HomeGoalViewType.kt new file mode 100644 index 00000000..d5b0dd2d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/HomeGoalViewType.kt @@ -0,0 +1,6 @@ +package org.keepgoeat.presentation.type + +enum class HomeGoalViewType { + MY_GOAL_TYPE, + ADD_GOAL_TYPE +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/LoginPlatformType.kt b/app/src/main/java/org/keepgoeat/presentation/type/LoginPlatformType.kt new file mode 100644 index 00000000..8912dd8b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/LoginPlatformType.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.presentation.type + +enum class LoginPlatformType(val label: String) { + NAVER("Naver"), KAKAO("Kakao"), NONE("None") +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/OnBoardingViewType.kt b/app/src/main/java/org/keepgoeat/presentation/type/OnBoardingViewType.kt new file mode 100644 index 00000000..9b8f3680 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/OnBoardingViewType.kt @@ -0,0 +1,31 @@ +package org.keepgoeat.presentation.type + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.keepgoeat.R + +enum class OnBoardingViewType( + @StringRes val titleStrRes: Int, + @StringRes val desStrRes: Int, + @StringRes val btnTextRes: Int, + @DrawableRes val imageRes: Int, +) { + FIRST( + R.string.onboarding1_title, + R.string.onboarding1_des, + R.string.onboarding1_button, + R.drawable.img_onboarding_1 + ), + SECOND( + R.string.onboarding2_title, + R.string.onboarding2_des, + R.string.onboarding2_button, + R.drawable.img_onboarding_2 + ), + THIRD( + R.string.onboarding3_title, + R.string.onboarding3_des, + R.string.onboarding3_button, + R.drawable.img_onboarding_3 + ) +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/ProcessState.kt b/app/src/main/java/org/keepgoeat/presentation/type/ProcessState.kt new file mode 100644 index 00000000..bd4fad56 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/ProcessState.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.presentation.type + +enum class ProcessState { + IDLE, IN_PROGRESS, DONE +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/RecyclerLayoutType.kt b/app/src/main/java/org/keepgoeat/presentation/type/RecyclerLayoutType.kt new file mode 100644 index 00000000..cc6889db --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/RecyclerLayoutType.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.presentation.type + +enum class RecyclerLayoutType { + HORIZONTAL, VERTICAL, GRID +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/SortType.kt b/app/src/main/java/org/keepgoeat/presentation/type/SortType.kt new file mode 100644 index 00000000..11da00bb --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/SortType.kt @@ -0,0 +1,5 @@ +package org.keepgoeat.presentation.type + +enum class SortType { + MORE, LESS, ALL +} diff --git a/app/src/main/java/org/keepgoeat/presentation/type/WithdrawReason.kt b/app/src/main/java/org/keepgoeat/presentation/type/WithdrawReason.kt new file mode 100644 index 00000000..b1ff1321 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/presentation/type/WithdrawReason.kt @@ -0,0 +1,13 @@ +package org.keepgoeat.presentation.type + +import androidx.annotation.StringRes +import org.keepgoeat.R + +enum class WithdrawReason( + @StringRes val reason: Int, +) { + QUIT_ISSUE(R.string.withdraw_reason1), + FREQUENCY_ISSUE(R.string.withdraw_reason2), + ERROR_ISSUE(R.string.withdraw_reason3), + CONTENT_ISSUE(R.string.withdraw_reason4), +} diff --git a/app/src/main/java/org/keepgoeat/util/BindingAdatper.kt b/app/src/main/java/org/keepgoeat/util/BindingAdatper.kt index 20af0143..fa8f23bf 100644 --- a/app/src/main/java/org/keepgoeat/util/BindingAdatper.kt +++ b/app/src/main/java/org/keepgoeat/util/BindingAdatper.kt @@ -1,10 +1,16 @@ package org.keepgoeat.util +import android.text.InputFilter import android.view.View +import android.widget.EditText import android.widget.ImageView +import android.widget.TextView import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener import androidx.databinding.BindingAdapter import coil.load +import org.keepgoeat.presentation.type.HomeBackgroundType +import org.keepgoeat.util.extension.getStringLength @BindingAdapter("image") fun ImageView.setImage(imageUrl: String) { @@ -16,3 +22,52 @@ fun View.setVisibility(isVisible: Boolean?) { if (isVisible == null) return this.isVisible = isVisible } + +/* +@BindingAdapter(value = ["startDate", "endDate"], requireAll = false) +fun TextView.setDuration(startDate: LocalDate?, endDate: LocalDate?) { + safeLet(startDate, endDate) { s, e -> + text = String.format( + context.getString(R.string.my_goal_duration_format), + s.year, + s.monthValue.padZero(2), + s.dayOfMonth.padZero(2), + e.year, + e.monthValue.padZero(2), + e.dayOfMonth.padZero(2) + ) + } +} +*/ + +@BindingAdapter(value = ["startDate", "endDate"], requireAll = false) +fun TextView.setDurationText(startDate: String?, endDate: String?) { + safeLet(startDate, endDate) { s, e -> + text = "$s ~ $e" + } +} + +@BindingAdapter("homeBackground") +fun ImageView.setBackground(hour: Int) { + val imgRes = when (hour) { + in 7..16 -> HomeBackgroundType.MORNING.sky + in 18..24, in 0..5 -> HomeBackgroundType.NIGHT.sky + in 5..7, in 16..18 -> HomeBackgroundType.EVENING.sky + else -> { + HomeBackgroundType.MORNING.sky + } + } + setBackgroundResource(imgRes) +} + +/** ์‹œ์Šคํ…œ์ด ์ธ์‹ํ•˜๋Š” ๊ธ€์ž ์ˆ˜๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์‹ํ•˜๋Š” ๊ธ€์ž ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๊ธ€์ž ์ˆ˜ ๋ฒ”์œ„์— ๋”ฐ๋ผ maxLength ๋ฅผ ์„ค์ • */ +@BindingAdapter("maxLen") +fun EditText.cutTextToMaxLength(maxLength: Int) { + val filterArray = arrayOfNulls(1) + addTextChangedListener { + val str = text.toString() + val lengthFilter = + if (str.getStringLength() == maxLength) str.length else Int.MAX_VALUE + filters = filterArray.apply { this[0] = InputFilter.LengthFilter(lengthFilter) } + } +} diff --git a/app/src/main/java/org/keepgoeat/util/Event.kt b/app/src/main/java/org/keepgoeat/util/Event.kt deleted file mode 100644 index 4a55ef3e..00000000 --- a/app/src/main/java/org/keepgoeat/util/Event.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.keepgoeat.util - -open class Event(private val content: T) { - var hasBeenHandled = false - private set - - fun getContentIfNotHandled(): T? { - return if (hasBeenHandled) { - null - } else { - hasBeenHandled = true - content - } - } - - fun peekContent(): T = content -} diff --git a/app/src/main/java/org/keepgoeat/util/EventObserver.kt b/app/src/main/java/org/keepgoeat/util/EventObserver.kt deleted file mode 100644 index 92c4d279..00000000 --- a/app/src/main/java/org/keepgoeat/util/EventObserver.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.keepgoeat.util - -import androidx.lifecycle.Observer - -class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> { - override fun onChanged(event: Event?) { - event?.getContentIfNotHandled()?.let { value -> - onEventUnhandledContent(value) - } - } -} diff --git a/app/src/main/java/org/keepgoeat/util/ItemDecorationUtil.kt b/app/src/main/java/org/keepgoeat/util/ItemDecorationUtil.kt new file mode 100644 index 00000000..0fd59c06 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/ItemDecorationUtil.kt @@ -0,0 +1,40 @@ +package org.keepgoeat.util + +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import org.keepgoeat.presentation.type.RecyclerLayoutType +import org.keepgoeat.util.extension.dp + +/** @param space : ์—ฌ๋ฐฑ ์‚ฌ์ด์ฆˆ + * @param gridMatrix : ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ๋ฉ”๋‹ˆ์ € ์ ์šฉ ์‹œ row, colume ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Pair + * @param layoutType : ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ๋ ˆ์ด์•„์›ƒ ๋ฐฐ์น˜ ๋ฐฉ์‹ */ +class ItemDecorationUtil( + private val space: Int, + private val gridMatrix: Pair? = null, + private val layoutType: RecyclerLayoutType, +) : RecyclerView.ItemDecoration() { + private val spaceDp = space.dp + private val zeroDp = 0.dp + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + super.getItemOffsets(outRect, view, parent, state) + val pos = parent.getChildAdapterPosition(view) + + when (layoutType) { + RecyclerLayoutType.GRID -> { + if (gridMatrix == null) return + + with(gridMatrix) { + outRect.bottom = if (pos / second + 1 < first) spaceDp else zeroDp + outRect.right = if (pos % second + 1 < second) spaceDp else zeroDp + } + } + RecyclerLayoutType.VERTICAL -> { + val isLastItem = pos == parent.adapter?.itemCount?.minus(1) + outRect.bottom = if (!isLastItem) spaceDp else zeroDp + } + else -> {} + } + } +} diff --git a/app/src/main/java/org/keepgoeat/util/KGESnackbar.kt b/app/src/main/java/org/keepgoeat/util/KGESnackbar.kt new file mode 100644 index 00000000..601385e7 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/KGESnackbar.kt @@ -0,0 +1,60 @@ +package org.keepgoeat.util + +import android.annotation.SuppressLint +import android.graphics.Color +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import androidx.databinding.DataBindingUtil +import com.google.android.material.snackbar.BaseTransientBottomBar.ANIMATION_MODE_FADE +import com.google.android.material.snackbar.Snackbar +import org.keepgoeat.R +import org.keepgoeat.databinding.ViewKgeSnackbarBinding + +class KGESnackbar( + private val view: View, + private val message: String, + private val duration: Int, + private val isTop: Boolean, +) { + private val snackbar = Snackbar.make(view, "", duration) + private val snackbarLayout = snackbar.view as Snackbar.SnackbarLayout + private val snackbarBinding: ViewKgeSnackbarBinding = + DataBindingUtil.inflate( + LayoutInflater.from(view.context), + R.layout.view_kge_snackbar, + null, + false + ).apply { + tvMessage.text = message + } + private val layoutParams = LinearLayout.LayoutParams(snackbar.view.layoutParams) + + init { + initializeView() + } + + @SuppressLint("RestrictedApi") + private fun initializeView() { + with(snackbarLayout) { + removeAllViews() + if (isTop) { + this@KGESnackbar.layoutParams.gravity = Gravity.TOP + layoutParams = this@KGESnackbar.layoutParams + snackbar.animationMode = ANIMATION_MODE_FADE + } + setBackgroundColor(Color.TRANSPARENT) + setPadding(0, 0, 0, 0) + addView(snackbarBinding.root, 0) + } + } + + fun show() { + snackbar.show() + } + + fun dismiss() { + if (duration == Snackbar.LENGTH_INDEFINITE) snackbar.dismiss() + } +} diff --git a/app/src/main/java/org/keepgoeat/util/NetworkMonitor.kt b/app/src/main/java/org/keepgoeat/util/NetworkMonitor.kt new file mode 100644 index 00000000..a721e5f1 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/NetworkMonitor.kt @@ -0,0 +1,39 @@ +package org.keepgoeat.util + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import org.keepgoeat.util.extension.toStateFlow + +/* ref : +https://developer.android.com/training/basics/network-ops/reading-network-state +https://medium.com/@bazzairvine/observing-your-network-connection-with-flow-1cdedf31780c */ + +class NetworkMonitor(private val context: Context, private val coroutineScope: CoroutineScope) { + private val connectivityManager: ConnectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val isConnected: StateFlow = callbackFlow { + val callback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + super.onAvailable(network) + trySend(true) + } + + override fun onLost(network: Network) { + super.onLost(network) + trySend(false) + } + } + + connectivityManager.registerDefaultNetworkCallback(callback) + + awaitClose { + connectivityManager.unregisterNetworkCallback(callback) + } + }.toStateFlow(coroutineScope, false) +} diff --git a/app/src/main/java/org/keepgoeat/util/UiState.kt b/app/src/main/java/org/keepgoeat/util/UiState.kt new file mode 100644 index 00000000..23afc793 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/UiState.kt @@ -0,0 +1,8 @@ +package org.keepgoeat.util + +sealed class UiState { + object Loading : UiState() + object Empty : UiState() + data class Success(val data: T) : UiState() + data class Error(val message: String?) : UiState() +} diff --git a/app/src/main/java/org/keepgoeat/util/binding/BindingActivity.kt b/app/src/main/java/org/keepgoeat/util/binding/BindingActivity.kt deleted file mode 100644 index e22a0d83..00000000 --- a/app/src/main/java/org/keepgoeat/util/binding/BindingActivity.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.keepgoeat.util.binding - -import android.os.Bundle -import androidx.annotation.LayoutRes -import androidx.appcompat.app.AppCompatActivity -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding - -abstract class BindingActivity(@LayoutRes private val layoutRes: Int) : - AppCompatActivity() { - lateinit var binding: B - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = DataBindingUtil.setContentView(this, layoutRes) - } -} diff --git a/app/src/main/java/org/keepgoeat/util/extension/ActivityExt.kt b/app/src/main/java/org/keepgoeat/util/extension/ActivityExt.kt index bb386067..6cc418ec 100644 --- a/app/src/main/java/org/keepgoeat/util/extension/ActivityExt.kt +++ b/app/src/main/java/org/keepgoeat/util/extension/ActivityExt.kt @@ -1,3 +1,5 @@ +package org.keepgoeat.util.extension + import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment diff --git a/app/src/main/java/org/keepgoeat/util/extension/ContextExt.kt b/app/src/main/java/org/keepgoeat/util/extension/ContextExt.kt index 57b9f33d..963065a9 100644 --- a/app/src/main/java/org/keepgoeat/util/extension/ContextExt.kt +++ b/app/src/main/java/org/keepgoeat/util/extension/ContextExt.kt @@ -11,9 +11,9 @@ fun Context.showToast(message: String, isShort: Boolean = true) { Toast.makeText(this, message, duration).show() } -fun Context.showKeyboard(view: View, toShow: Boolean) { +fun Context.showKeyboard(view: View, isShown: Boolean = true) { (getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager).run { - if (toShow) showSoftInput(view, 0) + if (isShown) showSoftInput(view, 0) else hideSoftInputFromWindow(view.windowToken, 0) } } diff --git a/app/src/main/java/org/keepgoeat/util/extension/FlowExt.kt b/app/src/main/java/org/keepgoeat/util/extension/FlowExt.kt new file mode 100644 index 00000000..11b9a03b --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/extension/FlowExt.kt @@ -0,0 +1,9 @@ +package org.keepgoeat.util.extension + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn + +fun Flow.toStateFlow(coroutineScope: CoroutineScope, initialValue: T) = + stateIn(coroutineScope, SharingStarted.Eagerly, initialValue) diff --git a/app/src/main/java/org/keepgoeat/util/extension/IntExt.kt b/app/src/main/java/org/keepgoeat/util/extension/IntExt.kt new file mode 100644 index 00000000..67289c7d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/extension/IntExt.kt @@ -0,0 +1,9 @@ +package org.keepgoeat.util.extension + +import android.content.res.Resources +import kotlin.math.roundToInt + +val Int.dp: Int + get() = (this * Resources.getSystem().displayMetrics.density).roundToInt() + +fun Int.padZero(length: Int) = toString().padStart(length, '0') diff --git a/app/src/main/java/org/keepgoeat/util/extension/LivedataExt.kt b/app/src/main/java/org/keepgoeat/util/extension/LivedataExt.kt deleted file mode 100644 index d5b19ce0..00000000 --- a/app/src/main/java/org/keepgoeat/util/extension/LivedataExt.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.keepgoeat.util.extension - -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.MutableLiveData - -fun MediatorLiveData.addSourceList( - vararg liveDataArgument: MutableLiveData<*>, - onChanged: () -> T, -) { - liveDataArgument.forEach { - this.addSource(it) { value = onChanged() } - } -} diff --git a/app/src/main/java/org/keepgoeat/util/extension/StringExt.kt b/app/src/main/java/org/keepgoeat/util/extension/StringExt.kt new file mode 100644 index 00000000..66dad3a6 --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/extension/StringExt.kt @@ -0,0 +1,15 @@ +package org.keepgoeat.util.extension + +import java.text.BreakIterator + +/** ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์‹ํ•˜๋Š” ๋ฌธ์ž์—ด์˜ ๊ธ€์ž ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ */ +fun String?.getStringLength(): Int { + if (this == null) return 0 + val it: BreakIterator = BreakIterator.getCharacterInstance() + it.setText(this) + var count = 0 + while (it.next() != BreakIterator.DONE) { + count++ + } + return count +} diff --git a/app/src/main/java/org/keepgoeat/util/extension/ViewExt.kt b/app/src/main/java/org/keepgoeat/util/extension/ViewExt.kt index 9c7c07ee..a4a6ede9 100644 --- a/app/src/main/java/org/keepgoeat/util/extension/ViewExt.kt +++ b/app/src/main/java/org/keepgoeat/util/extension/ViewExt.kt @@ -7,3 +7,17 @@ fun View.showSnackbar(message: String, isShort: Boolean = true) { val duration = if (isShort) Snackbar.LENGTH_SHORT else Snackbar.LENGTH_LONG Snackbar.make(this, message, duration).show() } + +inline fun View.setOnSingleClickListener( + delay: Long = 500L, + crossinline block: (View) -> Unit, +) { + var previousClickedTime = 0L + setOnClickListener { view -> + val clickedTime = System.currentTimeMillis() + if (clickedTime - previousClickedTime >= delay) { + block(view) + previousClickedTime = clickedTime + } + } +} diff --git a/app/src/main/java/org/keepgoeat/util/mixpanel/GoalEvent.kt b/app/src/main/java/org/keepgoeat/util/mixpanel/GoalEvent.kt new file mode 100644 index 00000000..d444443d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/mixpanel/GoalEvent.kt @@ -0,0 +1,27 @@ +package org.keepgoeat.util.mixpanel + +import org.keepgoeat.presentation.model.MixPanelEvent + +object GoalEvent { + fun completeGoal(goalName: String, goalStandard: String) = MixPanelEvent( + "Complete Goal", + mapOf("Goal Name" to goalName, "Goal Standard" to goalStandard) + ) + + fun addGoal(eatingType: String) = MixPanelEvent( + "Add Goal", + mapOf("Goal Type" to eatingType) + ) + + fun createGoal(title: String, criterion: String) = MixPanelEvent( + "Create Goal", + mapOf("Goal Name" to title, "Goal Standard" to criterion) + ) + + fun deleteGoal() = MixPanelEvent("Delete Goal", null) + + fun archiveGoal(goalName: String, goalStandard: String) = MixPanelEvent( + "Archive", + mapOf("Goal Name" to goalName, "Goal Standard" to goalStandard) + ) +} diff --git a/app/src/main/java/org/keepgoeat/util/mixpanel/MixpanelProvider.kt b/app/src/main/java/org/keepgoeat/util/mixpanel/MixpanelProvider.kt new file mode 100644 index 00000000..4449be8d --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/mixpanel/MixpanelProvider.kt @@ -0,0 +1,90 @@ +package org.keepgoeat.util.mixpanel + +import android.content.Context +import com.mixpanel.android.mpmetrics.MixpanelAPI +import dagger.hilt.android.qualifiers.ApplicationContext +import org.json.JSONObject +import org.keepgoeat.BuildConfig +import org.keepgoeat.data.datasource.local.KGEDataSource +import org.keepgoeat.presentation.model.MixPanelEvent +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MixpanelProvider @Inject constructor( + @ApplicationContext private val context: Context, + private val localStorage: KGEDataSource, +) { + private val instance: MixpanelAPI = + MixpanelAPI.getInstance(context, BuildConfig.MIXPANEL_PROJECT_TOKEN, true) + + fun setUserProfile() { + if (BuildConfig.DEBUG) return + instance.identify("${localStorage.loginPlatform.label} ${localStorage.userEmail}") + + val props = JSONObject().apply { + put(PROPERTY_NICKNAME, localStorage.userName) + put(PROPERTY_EMAIL, localStorage.userEmail) + put(PROPERTY_PLATFORM, localStorage.loginPlatform.label) + } + + instance.people.set(props) + } + + fun initUserGoalNumber() { + instance.people.set(PROPERTY_GOAL_NUMBER, 0) + } + + private fun getUserProperties() = JSONObject().apply { + put(PROPERTY_NICKNAME, localStorage.userName) + put(PROPERTY_EMAIL, localStorage.userEmail) + } + + fun createGoal() { + if (BuildConfig.DEBUG) return + instance.people.increment(PROPERTY_GOAL_NUMBER, 1.0) + } + + fun deleteGoal() { + if (BuildConfig.DEBUG) return + instance.people.increment(PROPERTY_GOAL_NUMBER, -1.0) + } + + /** ๋ฏน์ŠคํŒจ๋„์— ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•˜๋Š” ํ•จ์ˆ˜. ์œ ์ € ํ”„๋กœํผํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ, isRequiredUserProps๋ฅผ false๋กœ ์„ค์ • ํ•„์š” */ + fun sendEvent(event: MixPanelEvent, isRequiredUserProps: Boolean = true) { + if (BuildConfig.DEBUG) return + val props = if (isRequiredUserProps) getUserProperties() else JSONObject() + + event.params?.let { + for ((key, value) in it) + props.put(key, value) + } + + instance.track(event.name, props) + } + + fun startRecodingScreenTime() { + if (BuildConfig.DEBUG) return + instance.timeEvent(EVENT_VIEW_PAGE) + } + + fun stopRecodingScreenTime(screenName: String) { + if (BuildConfig.DEBUG) return + val props = JSONObject().apply { + put(PROPERTY_PAGE_NAME, screenName) + } + instance.track(EVENT_VIEW_PAGE, props) + } + + companion object { + // User Property + private const val PROPERTY_NICKNAME = "\$name" + private const val PROPERTY_EMAIL = "\$email" + private const val PROPERTY_PLATFORM = "Platform" + private const val PROPERTY_GOAL_NUMBER = "Goal Number" + + // Event Property + private const val EVENT_VIEW_PAGE = "View Page" + private const val PROPERTY_PAGE_NAME = "Page Name" + } +} diff --git a/app/src/main/java/org/keepgoeat/util/mixpanel/SignEvent.kt b/app/src/main/java/org/keepgoeat/util/mixpanel/SignEvent.kt new file mode 100644 index 00000000..76c6c93c --- /dev/null +++ b/app/src/main/java/org/keepgoeat/util/mixpanel/SignEvent.kt @@ -0,0 +1,11 @@ +package org.keepgoeat.util.mixpanel + +import org.keepgoeat.presentation.model.MixPanelEvent + +object SignEvent { + fun completeSignUp(platForm: String) = MixPanelEvent("Sign Up", mapOf("Platform" to platForm)) + + fun completeLogin() = MixPanelEvent("Login", null) + + fun deleteAccount(reasons: Map?) = MixPanelEvent("Delete Account", reasons) +} diff --git a/app/src/main/res/color/selector_complete_button_text_color.xml b/app/src/main/res/color/selector_complete_button_text_color.xml new file mode 100644 index 00000000..d5112a5c --- /dev/null +++ b/app/src/main/res/color/selector_complete_button_text_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_less_complete_button_color.xml b/app/src/main/res/color/selector_less_complete_button_color.xml new file mode 100644 index 00000000..d198bc8c --- /dev/null +++ b/app/src/main/res/color/selector_less_complete_button_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_more_complete_button_color.xml b/app/src/main/res/color/selector_more_complete_button_color.xml new file mode 100644 index 00000000..34b8b6fb --- /dev/null +++ b/app/src/main/res/color/selector_more_complete_button_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/img_home_background_evening.png b/app/src/main/res/drawable-hdpi/img_home_background_evening.png new file mode 100644 index 00000000..9ac86176 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_home_background_evening.png differ diff --git a/app/src/main/res/drawable-hdpi/img_home_background_morning.png b/app/src/main/res/drawable-hdpi/img_home_background_morning.png new file mode 100644 index 00000000..29b7678e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_home_background_morning.png differ diff --git a/app/src/main/res/drawable-hdpi/img_home_background_night.png b/app/src/main/res/drawable-hdpi/img_home_background_night.png new file mode 100644 index 00000000..66deca8f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_home_background_night.png differ diff --git a/app/src/main/res/drawable-hdpi/img_logo.png b/app/src/main/res/drawable-hdpi/img_logo.png new file mode 100644 index 00000000..4c55a334 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_logo.png differ diff --git a/app/src/main/res/drawable-hdpi/img_onboarding_1.png b/app/src/main/res/drawable-hdpi/img_onboarding_1.png new file mode 100644 index 00000000..4fe323cc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_onboarding_1.png differ diff --git a/app/src/main/res/drawable-hdpi/img_onboarding_2.png b/app/src/main/res/drawable-hdpi/img_onboarding_2.png new file mode 100644 index 00000000..351f05a0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_onboarding_2.png differ diff --git a/app/src/main/res/drawable-hdpi/img_onboarding_3.png b/app/src/main/res/drawable-hdpi/img_onboarding_3.png new file mode 100644 index 00000000..a4eda141 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_onboarding_3.png differ diff --git a/app/src/main/res/drawable-hdpi/img_service_intro_background.png b/app/src/main/res/drawable-hdpi/img_service_intro_background.png new file mode 100644 index 00000000..45d8ee35 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_service_intro_background.png differ diff --git a/app/src/main/res/drawable-hdpi/img_service_intro_logo.png b/app/src/main/res/drawable-hdpi/img_service_intro_logo.png new file mode 100644 index 00000000..ef2f848e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_service_intro_logo.png differ diff --git a/app/src/main/res/drawable-hdpi/img_snail_splash.png b/app/src/main/res/drawable-hdpi/img_snail_splash.png new file mode 100644 index 00000000..26db87a4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_snail_splash.png differ diff --git a/app/src/main/res/drawable-mdpi/img_home_background_evening.png b/app/src/main/res/drawable-mdpi/img_home_background_evening.png new file mode 100644 index 00000000..6fce04cc Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_home_background_evening.png differ diff --git a/app/src/main/res/drawable-mdpi/img_home_background_morning.png b/app/src/main/res/drawable-mdpi/img_home_background_morning.png new file mode 100644 index 00000000..21bc3d9a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_home_background_morning.png differ diff --git a/app/src/main/res/drawable-mdpi/img_home_background_night.png b/app/src/main/res/drawable-mdpi/img_home_background_night.png new file mode 100644 index 00000000..38da8899 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_home_background_night.png differ diff --git a/app/src/main/res/drawable-mdpi/img_logo.png b/app/src/main/res/drawable-mdpi/img_logo.png new file mode 100644 index 00000000..74bb7fdf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_logo.png differ diff --git a/app/src/main/res/drawable-mdpi/img_onboarding_1.png b/app/src/main/res/drawable-mdpi/img_onboarding_1.png new file mode 100644 index 00000000..5af72498 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_onboarding_1.png differ diff --git a/app/src/main/res/drawable-mdpi/img_onboarding_2.png b/app/src/main/res/drawable-mdpi/img_onboarding_2.png new file mode 100644 index 00000000..662f752e Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_onboarding_2.png differ diff --git a/app/src/main/res/drawable-mdpi/img_onboarding_3.png b/app/src/main/res/drawable-mdpi/img_onboarding_3.png new file mode 100644 index 00000000..f2971886 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_onboarding_3.png differ diff --git a/app/src/main/res/drawable-mdpi/img_snail_splash.png b/app/src/main/res/drawable-mdpi/img_snail_splash.png new file mode 100644 index 00000000..6cc232a1 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/img_snail_splash.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_home_background_evening.png b/app/src/main/res/drawable-xhdpi/img_home_background_evening.png new file mode 100644 index 00000000..c7c78625 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_home_background_evening.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_home_background_morning.png b/app/src/main/res/drawable-xhdpi/img_home_background_morning.png new file mode 100644 index 00000000..0430890d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_home_background_morning.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_home_background_night.png b/app/src/main/res/drawable-xhdpi/img_home_background_night.png new file mode 100644 index 00000000..6b5ec0cb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_home_background_night.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_logo.png b/app/src/main/res/drawable-xhdpi/img_logo.png new file mode 100644 index 00000000..c91e1f67 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_logo.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_onboarding_1.png b/app/src/main/res/drawable-xhdpi/img_onboarding_1.png new file mode 100644 index 00000000..e122c1d4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_onboarding_1.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_onboarding_2.png b/app/src/main/res/drawable-xhdpi/img_onboarding_2.png new file mode 100644 index 00000000..bdb7f791 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_onboarding_2.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_onboarding_3.png b/app/src/main/res/drawable-xhdpi/img_onboarding_3.png new file mode 100644 index 00000000..86da9bf7 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_onboarding_3.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_service_intro_background.png b/app/src/main/res/drawable-xhdpi/img_service_intro_background.png new file mode 100644 index 00000000..7b440fab Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_service_intro_background.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_service_intro_logo.png b/app/src/main/res/drawable-xhdpi/img_service_intro_logo.png new file mode 100644 index 00000000..9f239d3a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_service_intro_logo.png differ diff --git a/app/src/main/res/drawable-xhdpi/img_snail_splash.png b/app/src/main/res/drawable-xhdpi/img_snail_splash.png new file mode 100644 index 00000000..3bc01a9e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_snail_splash.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_home_background_evening.png b/app/src/main/res/drawable-xxhdpi/img_home_background_evening.png new file mode 100644 index 00000000..7a6a4abd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_home_background_evening.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_home_background_morning.png b/app/src/main/res/drawable-xxhdpi/img_home_background_morning.png new file mode 100644 index 00000000..5fd4adce Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_home_background_morning.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_home_background_night.png b/app/src/main/res/drawable-xxhdpi/img_home_background_night.png new file mode 100644 index 00000000..8efb6dc6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_home_background_night.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_logo.png b/app/src/main/res/drawable-xxhdpi/img_logo.png new file mode 100644 index 00000000..6c0a6cdc Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_logo.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_onboarding_1.png b/app/src/main/res/drawable-xxhdpi/img_onboarding_1.png new file mode 100644 index 00000000..9eb21e28 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_onboarding_1.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_onboarding_2.png b/app/src/main/res/drawable-xxhdpi/img_onboarding_2.png new file mode 100644 index 00000000..407d5cfd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_onboarding_2.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_onboarding_3.png b/app/src/main/res/drawable-xxhdpi/img_onboarding_3.png new file mode 100644 index 00000000..078afcb7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_onboarding_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_service_intro_background.png b/app/src/main/res/drawable-xxhdpi/img_service_intro_background.png new file mode 100644 index 00000000..277eca95 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_service_intro_background.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_service_intro_logo.png b/app/src/main/res/drawable-xxhdpi/img_service_intro_logo.png new file mode 100644 index 00000000..07524a89 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_service_intro_logo.png differ diff --git a/app/src/main/res/drawable-xxhdpi/img_snail_splash.png b/app/src/main/res/drawable-xxhdpi/img_snail_splash.png new file mode 100644 index 00000000..644c21fa Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_snail_splash.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_home_background_evening.png b/app/src/main/res/drawable-xxxhdpi/img_home_background_evening.png new file mode 100644 index 00000000..1b1cd00b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_home_background_evening.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_home_background_morning.png b/app/src/main/res/drawable-xxxhdpi/img_home_background_morning.png new file mode 100644 index 00000000..d605098f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_home_background_morning.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_home_background_night.png b/app/src/main/res/drawable-xxxhdpi/img_home_background_night.png new file mode 100644 index 00000000..e80b5b82 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_home_background_night.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_logo.png b/app/src/main/res/drawable-xxxhdpi/img_logo.png new file mode 100644 index 00000000..cf7aca7a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_logo.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_onboarding_1.png b/app/src/main/res/drawable-xxxhdpi/img_onboarding_1.png new file mode 100644 index 00000000..f4a88494 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_onboarding_1.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_onboarding_2.png b/app/src/main/res/drawable-xxxhdpi/img_onboarding_2.png new file mode 100644 index 00000000..53786269 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_onboarding_2.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_onboarding_3.png b/app/src/main/res/drawable-xxxhdpi/img_onboarding_3.png new file mode 100644 index 00000000..078737f9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_onboarding_3.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_service_intro_background.png b/app/src/main/res/drawable-xxxhdpi/img_service_intro_background.png new file mode 100644 index 00000000..f0f6b4ea Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_service_intro_background.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_service_intro_logo.png b/app/src/main/res/drawable-xxxhdpi/img_service_intro_logo.png new file mode 100644 index 00000000..912ab790 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_service_intro_logo.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/img_snail_splash.png b/app/src/main/res/drawable-xxxhdpi/img_snail_splash.png new file mode 100644 index 00000000..4861b674 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_snail_splash.png differ diff --git a/app/src/main/res/drawable/background_cheer_msg.xml b/app/src/main/res/drawable/background_cheer_msg.xml new file mode 100644 index 00000000..81af6f92 --- /dev/null +++ b/app/src/main/res/drawable/background_cheer_msg.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_goal_info.xml b/app/src/main/res/drawable/background_goal_info.xml new file mode 100644 index 00000000..887b7b5d --- /dev/null +++ b/app/src/main/res/drawable/background_goal_info.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_goal_list.xml b/app/src/main/res/drawable/background_goal_list.xml new file mode 100644 index 00000000..03c7e2ca --- /dev/null +++ b/app/src/main/res/drawable/background_goal_list.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_home_bottom.xml b/app/src/main/res/drawable/background_home_bottom.xml new file mode 100644 index 00000000..604c4118 --- /dev/null +++ b/app/src/main/res/drawable/background_home_bottom.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_keep_delete.xml b/app/src/main/res/drawable/background_keep_delete.xml new file mode 100644 index 00000000..c9928fb3 --- /dev/null +++ b/app/src/main/res/drawable/background_keep_delete.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_sign_box.xml b/app/src/main/res/drawable/background_sign_box.xml new file mode 100644 index 00000000..26abb375 --- /dev/null +++ b/app/src/main/res/drawable/background_sign_box.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 00000000..37787112 --- /dev/null +++ b/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 00000000..65bc01c3 --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_green.xml b/app/src/main/res/drawable/ic_check_green.xml new file mode 100644 index 00000000..91cc52c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_green.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_orange.xml b/app/src/main/res/drawable/ic_check_orange.xml new file mode 100644 index 00000000..7752f1b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_orange.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_checkbox_checked.xml b/app/src/main/res/drawable/ic_checkbox_checked.xml new file mode 100644 index 00000000..664d25ae --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_checked.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_checkbox_unchecked.xml b/app/src/main/res/drawable/ic_checkbox_unchecked.xml new file mode 100644 index 00000000..c83d0044 --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_unchecked.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_default_green_sticker.xml b/app/src/main/res/drawable/ic_default_green_sticker.xml new file mode 100644 index 00000000..46285e8f --- /dev/null +++ b/app/src/main/res/drawable/ic_default_green_sticker.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_default_orange_sticker.xml b/app/src/main/res/drawable/ic_default_orange_sticker.xml new file mode 100644 index 00000000..7070d6a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_default_orange_sticker.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_detail.xml b/app/src/main/res/drawable/ic_detail.xml new file mode 100644 index 00000000..ad33042d --- /dev/null +++ b/app/src/main/res/drawable/ic_detail.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_detail_large.xml b/app/src/main/res/drawable/ic_detail_large.xml new file mode 100644 index 00000000..ca0a0ca8 --- /dev/null +++ b/app/src/main/res/drawable/ic_detail_large.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_eating_less_tag_minus.xml b/app/src/main/res/drawable/ic_eating_less_tag_minus.xml new file mode 100644 index 00000000..dbb323bb --- /dev/null +++ b/app/src/main/res/drawable/ic_eating_less_tag_minus.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_eating_more_tag_plus.xml b/app/src/main/res/drawable/ic_eating_more_tag_plus.xml new file mode 100644 index 00000000..bfea1d02 --- /dev/null +++ b/app/src/main/res/drawable/ic_eating_more_tag_plus.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 00000000..9d80cdab --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_kakao.xml b/app/src/main/res/drawable/ic_kakao.xml new file mode 100644 index 00000000..e05d34a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_kakao.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_keep.xml b/app/src/main/res/drawable/ic_keep.xml new file mode 100644 index 00000000..91206a81 --- /dev/null +++ b/app/src/main/res/drawable/ic_keep.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_keepgoeat_background.xml b/app/src/main/res/drawable/ic_keepgoeat_background.xml new file mode 100644 index 00000000..ca3826a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_keepgoeat_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9c..ca3826a4 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_more.xml b/app/src/main/res/drawable/ic_more.xml new file mode 100644 index 00000000..94832da4 --- /dev/null +++ b/app/src/main/res/drawable/ic_more.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_my_page_white.xml b/app/src/main/res/drawable/ic_my_page_white.xml new file mode 100644 index 00000000..7b23b01e --- /dev/null +++ b/app/src/main/res/drawable/ic_my_page_white.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_naver.xml b/app/src/main/res/drawable/ic_naver.xml new file mode 100644 index 00000000..defb2dfa --- /dev/null +++ b/app/src/main/res/drawable/ic_naver.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_plate_gray.xml b/app/src/main/res/drawable/ic_plate_gray.xml new file mode 100644 index 00000000..d2458f0f --- /dev/null +++ b/app/src/main/res/drawable/ic_plate_gray.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml new file mode 100644 index 00000000..dd916d7b --- /dev/null +++ b/app/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml new file mode 100644 index 00000000..70f2c9be --- /dev/null +++ b/app/src/main/res/drawable/ic_right.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_snail_green_cheer_left.xml b/app/src/main/res/drawable/ic_snail_green_cheer_left.xml new file mode 100644 index 00000000..82aa0d02 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_green_cheer_left.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_green_ribbon.xml b/app/src/main/res/drawable/ic_snail_green_ribbon.xml new file mode 100644 index 00000000..db6e045a --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_green_ribbon.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_green_sticker.xml b/app/src/main/res/drawable/ic_snail_green_sticker.xml new file mode 100644 index 00000000..99925510 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_green_sticker.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_orange_cheer_left.xml b/app/src/main/res/drawable/ic_snail_orange_cheer_left.xml new file mode 100644 index 00000000..d5a69e75 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_orange_cheer_left.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_orange_cheer_right.xml b/app/src/main/res/drawable/ic_snail_orange_cheer_right.xml new file mode 100644 index 00000000..0ee4a688 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_orange_cheer_right.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_orange_hungry.xml b/app/src/main/res/drawable/ic_snail_orange_hungry.xml new file mode 100644 index 00000000..40ebd3e7 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_orange_hungry.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_orange_spoon_left.xml b/app/src/main/res/drawable/ic_snail_orange_spoon_left.xml new file mode 100644 index 00000000..7abdb1f2 --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_orange_spoon_left.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_snail_orange_sticker.xml b/app/src/main/res/drawable/ic_snail_orange_sticker.xml new file mode 100644 index 00000000..a85793dd --- /dev/null +++ b/app/src/main/res/drawable/ic_snail_orange_sticker.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_trash.xml b/app/src/main/res/drawable/ic_trash.xml new file mode 100644 index 00000000..29fa5721 --- /dev/null +++ b/app/src/main/res/drawable/ic_trash.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/img_service_intro_background.png b/app/src/main/res/drawable/img_service_intro_background.png new file mode 100644 index 00000000..2d41e8c0 Binary files /dev/null and b/app/src/main/res/drawable/img_service_intro_background.png differ diff --git a/app/src/main/res/drawable/img_service_intro_logo.png b/app/src/main/res/drawable/img_service_intro_logo.png new file mode 100644 index 00000000..8955389c Binary files /dev/null and b/app/src/main/res/drawable/img_service_intro_logo.png differ diff --git a/app/src/main/res/drawable/selector_checkbox.xml b/app/src/main/res/drawable/selector_checkbox.xml new file mode 100644 index 00000000..79da86f9 --- /dev/null +++ b/app/src/main/res/drawable/selector_checkbox.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_edittext_background.xml b/app/src/main/res/drawable/selector_edittext_background.xml new file mode 100644 index 00000000..8433c68d --- /dev/null +++ b/app/src/main/res/drawable/selector_edittext_background.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_edittext_withdraw.xml b/app/src/main/res/drawable/selector_edittext_withdraw.xml new file mode 100644 index 00000000..5d639d0a --- /dev/null +++ b/app/src/main/res/drawable/selector_edittext_withdraw.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_tab_dot.xml b/app/src/main/res/drawable/selector_tab_dot.xml new file mode 100644 index 00000000..6eb31cec --- /dev/null +++ b/app/src/main/res/drawable/selector_tab_dot.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_gray_radius_8.xml b/app/src/main/res/drawable/shape_border_gray_radius_8.xml new file mode 100644 index 00000000..647973be --- /dev/null +++ b/app/src/main/res/drawable/shape_border_gray_radius_8.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_12.xml b/app/src/main/res/drawable/shape_border_radius_12.xml new file mode 100644 index 00000000..bb57fd05 --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_12.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_16.xml b/app/src/main/res/drawable/shape_border_radius_16.xml new file mode 100644 index 00000000..819d221e --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_16.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_4.xml b/app/src/main/res/drawable/shape_border_radius_4.xml new file mode 100644 index 00000000..c0f94629 --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_4.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_6.xml b/app/src/main/res/drawable/shape_border_radius_6.xml new file mode 100644 index 00000000..5f2b856a --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_6.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_border_radius_8.xml b/app/src/main/res/drawable/shape_border_radius_8.xml new file mode 100644 index 00000000..a15ba5c8 --- /dev/null +++ b/app/src/main/res/drawable/shape_border_radius_8.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_cursor.xml b/app/src/main/res/drawable/shape_cursor.xml new file mode 100644 index 00000000..52cda949 --- /dev/null +++ b/app/src/main/res/drawable/shape_cursor.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/shape_selected_dot.xml b/app/src/main/res/drawable/shape_selected_dot.xml new file mode 100644 index 00000000..d002c89e --- /dev/null +++ b/app/src/main/res/drawable/shape_selected_dot.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_unselected_dot.xml b/app/src/main/res/drawable/shape_unselected_dot.xml new file mode 100644 index 00000000..91ac5e51 --- /dev/null +++ b/app/src/main/res/drawable/shape_unselected_dot.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/drawable/sign_keepgoeat.xml b/app/src/main/res/drawable/sign_keepgoeat.xml new file mode 100644 index 00000000..0a207150 --- /dev/null +++ b/app/src/main/res/drawable/sign_keepgoeat.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/snail_green_spoon.xml b/app/src/main/res/drawable/snail_green_spoon.xml new file mode 100644 index 00000000..3ab71c7a --- /dev/null +++ b/app/src/main/res/drawable/snail_green_spoon.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/snail_orange_spoon.xml b/app/src/main/res/drawable/snail_orange_spoon.xml new file mode 100644 index 00000000..e268f1ef --- /dev/null +++ b/app/src/main/res/drawable/snail_orange_spoon.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_account_info.xml b/app/src/main/res/layout/activity_account_info.xml new file mode 100644 index 00000000..09144048 --- /dev/null +++ b/app/src/main/res/layout/activity_account_info.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_archived_goal.xml b/app/src/main/res/layout/activity_archived_goal.xml new file mode 100644 index 00000000..edb6875e --- /dev/null +++ b/app/src/main/res/layout/activity_archived_goal.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_goal_detail.xml b/app/src/main/res/layout/activity_goal_detail.xml new file mode 100644 index 00000000..05885c79 --- /dev/null +++ b/app/src/main/res/layout/activity_goal_detail.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_goal_setting.xml b/app/src/main/res/layout/activity_goal_setting.xml new file mode 100644 index 00000000..f9fe4f3a --- /dev/null +++ b/app/src/main/res/layout/activity_goal_setting.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 00000000..b02167e3 --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_my.xml b/app/src/main/res/layout/activity_my.xml new file mode 100644 index 00000000..25a4bfa5 --- /dev/null +++ b/app/src/main/res/layout/activity_my.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml new file mode 100644 index 00000000..22a98d9e --- /dev/null +++ b/app/src/main/res/layout/activity_onboarding.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_service_intro.xml b/app/src/main/res/layout/activity_service_intro.xml new file mode 100644 index 00000000..6f2776c3 --- /dev/null +++ b/app/src/main/res/layout/activity_service_intro.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_sign.xml b/app/src/main/res/layout/activity_sign.xml new file mode 100644 index 00000000..ecdf0006 --- /dev/null +++ b/app/src/main/res/layout/activity_sign.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 00000000..58289b9f --- /dev/null +++ b/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web_view.xml b/app/src/main/res/layout/activity_web_view.xml new file mode 100644 index 00000000..612dd106 --- /dev/null +++ b/app/src/main/res/layout/activity_web_view.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_withdraw.xml b/app/src/main/res/layout/activity_withdraw.xml new file mode 100644 index 00000000..2a951a2b --- /dev/null +++ b/app/src/main/res/layout/activity_withdraw.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_bottom_goal_delete.xml b/app/src/main/res/layout/dialog_bottom_goal_delete.xml new file mode 100644 index 00000000..1e48d723 --- /dev/null +++ b/app/src/main/res/layout/dialog_bottom_goal_delete.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_bottom_goal_keep.xml b/app/src/main/res/layout/dialog_bottom_goal_keep.xml new file mode 100644 index 00000000..e238b0df --- /dev/null +++ b/app/src/main/res/layout/dialog_bottom_goal_keep.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_bottom_home.xml b/app/src/main/res/layout/dialog_bottom_home.xml new file mode 100644 index 00000000..7f3c9727 --- /dev/null +++ b/app/src/main/res/layout/dialog_bottom_home.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_force_update.xml b/app/src/main/res/layout/dialog_force_update.xml new file mode 100644 index 00000000..0f7460c6 --- /dev/null +++ b/app/src/main/res/layout/dialog_force_update.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_goal_delete.xml b/app/src/main/res/layout/dialog_goal_delete.xml new file mode 100644 index 00000000..767f92e0 --- /dev/null +++ b/app/src/main/res/layout/dialog_goal_delete.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_logout.xml b/app/src/main/res/layout/dialog_logout.xml new file mode 100644 index 00000000..d5f8ea23 --- /dev/null +++ b/app/src/main/res/layout/dialog_logout.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_withdraw.xml b/app/src/main/res/layout/dialog_withdraw.xml new file mode 100644 index 00000000..44c81d73 --- /dev/null +++ b/app/src/main/res/layout/dialog_withdraw.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_add_goal.xml b/app/src/main/res/layout/item_add_goal.xml new file mode 100644 index 00000000..3bbc496f --- /dev/null +++ b/app/src/main/res/layout/item_add_goal.xml @@ -0,0 +1,47 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_archived_goal.xml b/app/src/main/res/layout/item_archived_goal.xml new file mode 100644 index 00000000..fb80be52 --- /dev/null +++ b/app/src/main/res/layout/item_archived_goal.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/item_goal_sticker.xml similarity index 53% rename from app/src/main/res/layout/activity_main.xml rename to app/src/main/res/layout/item_goal_sticker.xml index a9b8cabc..75ddef16 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/item_goal_sticker.xml @@ -4,18 +4,21 @@ xmlns:tools="http://schemas.android.com/tools"> + android:layout_height="wrap_content" + android:padding="3dp" + tools:background="@color/orange_50"> - - + app:layout_constraintTop_toTopOf="parent" + tools:background="@drawable/ic_snail_orange_sticker" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_goal.xml b/app/src/main/res/layout/item_home_goal.xml new file mode 100644 index 00000000..693e0276 --- /dev/null +++ b/app/src/main/res/layout/item_home_goal.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_onboarding.xml b/app/src/main/res/layout/item_onboarding.xml new file mode 100644 index 00000000..82079952 --- /dev/null +++ b/app/src/main/res/layout/item_onboarding.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_withdraw.xml b/app/src/main/res/layout/item_withdraw.xml new file mode 100644 index 00000000..27252a6b --- /dev/null +++ b/app/src/main/res/layout/item_withdraw.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_archived_goal_header.xml b/app/src/main/res/layout/layout_archived_goal_header.xml new file mode 100644 index 00000000..4d02661e --- /dev/null +++ b/app/src/main/res/layout/layout_archived_goal_header.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_eating_tag.xml b/app/src/main/res/layout/view_eating_tag.xml new file mode 100644 index 00000000..79782e42 --- /dev/null +++ b/app/src/main/res/layout/view_eating_tag.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_kge_snackbar.xml b/app/src/main/res/layout/view_kge_snackbar.xml new file mode 100644 index 00000000..299b10ea --- /dev/null +++ b/app/src/main/res/layout/view_kge_snackbar.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_dummy.xml b/app/src/main/res/layout/view_toolbar.xml similarity index 53% rename from app/src/main/res/layout/activity_dummy.xml rename to app/src/main/res/layout/view_toolbar.xml index 07a3154a..4c4de785 100644 --- a/app/src/main/res/layout/activity_dummy.xml +++ b/app/src/main/res/layout/view_toolbar.xml @@ -6,28 +6,35 @@ + name="title" + type="String" /> + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent"> - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat.xml b/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat.xml new file mode 100644 index 00000000..6fa0b1ce --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat_round.xml new file mode 100644 index 00000000..6fa0b1ce --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_keepgoeat_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe..c4a603d4 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe..c4a603d4 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_keepgoeat.png b/app/src/main/res/mipmap-hdpi/ic_keepgoeat.png new file mode 100644 index 00000000..c1cba5c0 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_keepgoeat.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_keepgoeat_foreground.png b/app/src/main/res/mipmap-hdpi/ic_keepgoeat_foreground.png new file mode 100644 index 00000000..9ac3b202 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_keepgoeat_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_keepgoeat_round.png b/app/src/main/res/mipmap-hdpi/ic_keepgoeat_round.png new file mode 100644 index 00000000..51b6fa85 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_keepgoeat_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..dc34d0e9 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78e..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..a0e1ca9c Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..ebe855ef Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_keepgoeat.png b/app/src/main/res/mipmap-mdpi/ic_keepgoeat.png new file mode 100644 index 00000000..5d0d90bf Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_keepgoeat.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_keepgoeat_foreground.png b/app/src/main/res/mipmap-mdpi/ic_keepgoeat_foreground.png new file mode 100644 index 00000000..c40601f5 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_keepgoeat_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_keepgoeat_round.png b/app/src/main/res/mipmap-mdpi/ic_keepgoeat_round.png new file mode 100644 index 00000000..34d26a29 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_keepgoeat_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..acd377a1 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..723d9957 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..fd862dce Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611da..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_keepgoeat.png b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat.png new file mode 100644 index 00000000..5bc1e568 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_foreground.png new file mode 100644 index 00000000..1b557869 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_round.png b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_round.png new file mode 100644 index 00000000..8e77830a Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_keepgoeat_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..0a918b49 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..60cf7bbf Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..89329f6c Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat.png b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat.png new file mode 100644 index 00000000..1809bc5d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_foreground.png new file mode 100644 index 00000000..8bba0ff1 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_round.png b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_round.png new file mode 100644 index 00000000..fb5a7665 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_keepgoeat_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..7a3677d4 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..6419ba1b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..7a98e3b3 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f508..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat.png b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat.png new file mode 100644 index 00000000..eefa927b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_foreground.png new file mode 100644 index 00000000..44671e5b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_round.png new file mode 100644 index 00000000..d68aaa00 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_keepgoeat_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..16ec9039 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..c235540d Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..183a2cad Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae37..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/raw/home_background.json b/app/src/main/res/raw/home_background.json new file mode 100644 index 00000000..653fba59 --- /dev/null +++ b/app/src/main/res/raw/home_background.json @@ -0,0 +1 @@ +{"v":"5.10.1","fr":60,"ip":0,"op":85,"w":360,"h":245,"nm":"homeBackgroundImage_0_and.png 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[68,61,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[57.485,57.485,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.303,11.303],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.349,-10.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"boom_41","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"boom_40","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"boom_39","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"boom_38","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"boom_37","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":225,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"boom_36","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"boom_35","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":315,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"boom_34","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"t":57.4,"s":[100]}],"ix":11},"r":{"a":0,"k":360,"ix":10},"p":{"a":0,"k":[268.185,202.467,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":55.8,"s":[102.458,102.458,100]},{"t":74.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42.2,"s":[0]},{"t":73.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40.6,"s":[0]},{"t":71.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":92,"st":39,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"round_5","parent":1,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69,"s":[39]},{"t":72,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[268.939,202.259,0],"ix":2,"l":2},"a":{"a":0,"k":[-2,3,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":33,"s":[31.68,31.68,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":72,"s":[292.769,292.769,100]},{"t":83.4,"s":[210.767,210.767,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[27.438,27.438],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":54,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.281,3.219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":33,"op":92,"st":33,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"star_4","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"t":52,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[273.072,279.393,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[137.738,137.738,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.5,-55.5],[-3.5,35.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[49]},{"t":40,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[49]},{"t":44,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":14,"op":92,"st":14,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[68,61,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[57.485,57.485,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.303,11.303],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.349,-10.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"boom_33","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"boom_32","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"boom_31","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"boom_30","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"boom_29","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":225,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"boom_28","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"boom_27","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":315,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"boom_26","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":36,"s":[0]},{"t":50.4,"s":[100]}],"ix":11},"r":{"a":0,"k":360,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48.8,"s":[102.458,102.458,100]},{"t":67.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35.2,"s":[0]},{"t":66.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.6,"s":[0]},{"t":64.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":32,"op":85,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"round_4","parent":12,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":62,"s":[39]},{"t":65,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-33.746,-11.708,0],"ix":2,"l":2},"a":{"a":0,"k":[-2,3,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":26,"s":[31.68,31.68,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":65,"s":[292.769,292.769,100]},{"t":76.4,"s":[210.767,210.767,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[27.438,27.438],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":64,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.281,3.219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":26,"op":85,"st":26,"ct":1,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"star_3","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[100]},{"t":45,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.614,65.425,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[137.738,137.738,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.5,-55.5],[-3.5,35.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[49]},{"t":33,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[49]},{"t":37,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":7,"op":85,"st":7,"ct":1,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[325.5,82.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[78.706,78.706,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.303,11.303],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.349,-10.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"boom_25","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"boom_24","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"boom_23","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"boom_22","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"boom_21","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":225,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"boom_20","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"boom_19","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":315,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"boom_18","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"t":34.4,"s":[100]}],"ix":11},"r":{"a":0,"k":360,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16.8,"s":[67.503,67.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32.8,"s":[102.458,102.458,100]},{"t":51.2,"s":[136.89,136.89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19.2,"s":[0]},{"t":50.4,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.6,"s":[0]},{"t":48.8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":16,"op":85,"st":16,"ct":1,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"round_3","parent":23,"sr":0.8,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[98]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":46,"s":[39]},{"t":49,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-33.746,-11.708,0],"ix":2,"l":2},"a":{"a":0,"k":[-2,3,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[31.68,31.68,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":49,"s":[292.769,292.769,100]},{"t":60.4,"s":[210.767,210.767,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[27.438,27.438],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":54,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":52,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.281,3.219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":82,"st":10,"ct":1,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"star_2","parent":23,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[100]},{"t":29,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.614,65.425,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[137.738,137.738,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.5,-55.5],[-3.5,35.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[49]},{"t":28,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[49]},{"t":32,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":-9,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/home_snail.json b/app/src/main/res/raw/home_snail.json new file mode 100644 index 00000000..ca274807 --- /dev/null +++ b/app/src/main/res/raw/home_snail.json @@ -0,0 +1 @@ +{"v":"5.10.1","fr":60,"ip":0,"op":85,"w":140,"h":140,"nm":"snail_orange_cheer 2","ddd":0,"assets":[{"id":"comp_0","nm":"snail_orange_cheer","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":0.9,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[129,52.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[72.602,72.602,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.303,11.303],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":0,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.349,-10.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"boom_17","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"boom_16","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"boom_15","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"boom_14","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"boom_13","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":225,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"boom_12","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"boom_11","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":315,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"boom_10","parent":1,"sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":33.5,"s":[0]},{"t":49.7,"s":[100]}],"ix":11},"r":{"a":0,"k":360,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":29.9,"s":[67.5,67.5,100]},{"t":47.9,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.552144667682,1,0.813283823051,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.6,"s":[0]},{"t":67.7,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30.8,"s":[0]},{"t":65.9,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":29,"op":85,"st":29,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"round_2","sr":0.9,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60.999,"s":[100]},{"t":64.1,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[104.5,44.5,0],"ix":2,"l":2},"a":{"a":0,"k":[-2,3,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":20,"s":[23,23,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":64.1,"s":[167.904,167.904,100]},{"t":76.7,"s":[153.02,153.02,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[27.438,27.438],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":28,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.997341978784,0.892892156863,1,1],"ix":4},"o":{"a":0,"k":40,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.281,3.219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":20,"op":85,"st":20,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":3,"nm":"โ–ฝ Group","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[75.265,75,0],"ix":2,"l":2},"a":{"a":0,"k":[74.583,62.727,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"particle_6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[114.755,35.363,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.776,-1.894],[0,0],[0,0],[0,0],[-2.403,-0.439],[0,0],[0,0]],"o":[[-2.507,-0.669],[0,0],[0,0],[0,0],[1.637,-1.804],[0,0],[0,0],[0,0]],"v":[[3.827,1.705],[-3.131,3.697],[-3.131,3.69],[-4.44,-0.97],[-4.064,-1.381],[2.427,-3.582],[3.159,-3.449],[4.44,1.865]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.709803938866,0.674509823322,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"particle_5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[21.158,62.503,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.591,0.084],[0,0],[0,0],[0,0],[1.386,2.006],[0,0],[0,0]],"o":[[1.302,2.243],[0,0],[0,0],[0,0],[-2.438,0.118],[0,0],[0,0],[0,0]],"v":[[-1.581,-3.608],[4.743,-0.098],[4.75,-0.098],[2.382,4.122],[1.825,4.15],[-4.325,1.114],[-4.75,0.501],[-1.901,-4.159]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.709803938866,0.674509823322,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"particle_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[73.931,64.461,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.591,0.084],[0,0],[0,0],[0,0],[1.386,2.006],[0,0],[0,0]],"o":[[1.302,2.243],[0,0],[0,0],[0,0],[-2.438,0.118],[0,0],[0,0],[0,0]],"v":[[-1.581,-3.608],[4.743,-0.098],[4.75,-0.098],[2.382,4.122],[1.825,4.15],[-4.325,1.114],[-4.75,0.501],[-1.901,-4.159]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.596078455448,0.549019634724,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"particle_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24.15,31.677,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-3.211,-0.104],[0,0],[0,0],[0,0],[1.546,2.591],[0,0],[0,0]],"o":[[1.428,2.876],[0,0],[0,0],[0,0],[-3.023,-0.049],[0,0],[0,0],[0,0]],"v":[[-1.647,-4.583],[5.882,0.265],[5.889,0.272],[2.622,5.286],[1.933,5.272],[-5.415,1.031],[-5.889,0.244],[-1.995,-5.286]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.596078455448,0.549019634724,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"particle_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.642,43.465,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.104,3.211],[0,0],[0,0],[0,0],[2.486,-1.713],[0,0],[0,0]],"o":[[2.779,-1.609],[0,0],[0,0],[0,0],[0.146,3.016],[0,0],[0,0],[0,0]],"v":[[-4.459,1.954],[-0.113,-5.875],[-0.127,-5.875],[5.097,-2.943],[5.131,-2.253],[1.377,5.352],[0.625,5.875],[-5.142,2.351]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.807843148708,0.505882382393,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"particle_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[68.784,15.37,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.11,0.878],[0,0],[0,0],[0,0],[1.769,1.219],[0,0],[0,0]],"o":[[1.776,1.442],[0,0],[0,0],[0,0],[-1.964,0.857],[0,0],[0,0],[0,0]],"v":[[-2.493,-2.742],[3.81,-1.829],[3.81,-1.822],[3.183,2.384],[2.73,2.579],[-3.273,2.001],[-3.81,1.632],[-2.925,-3.097]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.784313738346,0.337254911661,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"cheer_2","parent":28,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-7.302,-4.357,0],"ix":2,"l":2},"a":{"a":0,"k":[10,-10,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.089,0.146],[-1.254,-1.86],[0,0],[0.536,-2.187],[-1.073,-1.295],[-0.369,-0.077],[0,0],[0.634,1.414],[0.049,0.334],[0,0],[0,0],[0,0],[-0.501,2.124],[-0.286,0.627],[0,0],[0.404,-3.106],[-0.021,-0.467],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.358,1.553],[-0.592,0.954],[2.814,-0.174],[0.279,0.021],[0,0],[-1.915,0.926],[-0.696,0.759],[1.24,1.811],[0.118,0.32],[0,0],[-1.79,0.237]],"o":[[2.25,-0.153],[0,0],[1.901,2.821],[-0.759,3.092],[0.362,0.432],[0,0],[-0.474,-0.209],[-0.188,-0.418],[0,0],[0,0],[0,0],[-0.23,-1.198],[0.202,-0.857],[0,0],[-1.08,0.989],[-0.063,0.529],[0,0],[0,0],[0,0],[0,0],[0,0],[1.17,-0.857],[0.961,-1.093],[-1.024,1.114],[-0.299,0.021],[0,0],[1.045,-0.063],[1.839,-0.891],[-0.55,-0.014],[-0.153,-0.223],[0,0],[1.066,0.383],[2.591,-0.348]],"v":[[5.417,-13.3],[11.393,-10.264],[11.407,-10.257],[11.804,-1.314],[10.71,5.303],[11.957,5.992],[9.366,10.652],[6.991,8.374],[6.664,7.232],[5.619,7.901],[6.19,11.188],[1.558,12.449],[1.649,7.288],[2.408,5.059],[1.823,5.595],[-1.638,11.815],[-1.701,13.312],[-6.681,12.268],[-4.027,6.459],[-4.94,5.86],[-8.701,10.38],[-12.009,6.334],[-8.102,2.754],[-5.789,-0.367],[-11.668,2.788],[-12.531,2.788],[-11.709,-3.173],[-7.008,-4.392],[-3.247,-7.192],[-7.551,-9.107],[-7.969,-9.915],[-4.313,-12.005],[0.089,-11.475]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.588235318661,0.498039245605,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"cheer_1","parent":29,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-8.562,-5.959,0],"ix":2,"l":2},"a":{"a":0,"k":[6,-10,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.839,0.383],[-1.344,-1.497],[0,0],[0.209,-2.013],[-1.114,-1.017],[-0.334,-0.028],[0,0],[0.738,1.184],[0.084,0.293],[0,0],[0,0],[0,0],[-0.181,1.95],[-0.174,0.592],[0,0],[-0.028,-2.814],[-0.077,-0.418],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.017,1.553],[-0.411,0.926],[2.479,-0.501],[0.258,-0.021],[0,0],[-1.588,1.059],[-0.522,0.759],[1.323,1.456],[0.146,0.272],[0,0],[-1.56,0.432]],"o":[[1.985,-0.418],[0,0],[2.034,2.271],[-0.293,2.842],[0.376,0.341],[0,0],[-0.453,-0.132],[-0.216,-0.348],[0,0],[0,0],[0,0],[-0.348,-1.038],[0.07,-0.787],[0,0],[-0.836,1.017],[0,0.474],[0,0],[0,0],[0,0],[0,0],[0,0],[0.933,-0.905],[0.717,-1.093],[-0.766,1.114],[-0.265,0.049],[0,0],[0.926,-0.181],[1.518,-1.017],[-0.488,0.056],[-0.16,-0.174],[0,0],[0.989,0.216],[2.264,-0.634]],"v":[[2.894,-12.216],[8.584,-10.259],[8.591,-10.252],[10.047,-2.354],[9.893,3.663],[11.084,4.123],[9.357,8.587],[6.961,6.853],[6.529,5.878],[5.687,6.602],[6.599,9.451],[2.636,11.144],[2.079,6.547],[2.476,4.471],[2.023,5.021],[-0.282,10.983],[-0.157,12.321],[-4.712,12.007],[-3.068,6.519],[-3.953,6.094],[-6.738,10.579],[-10.179,7.396],[-7.149,3.733],[-5.478,0.668],[-10.311,4.2],[-11.084,4.304],[-11.084,-1.101],[-7.052,-2.765],[-4.057,-5.718],[-8.117,-6.888],[-8.584,-7.55],[-5.596,-9.862],[-1.619,-9.932]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.588235318661,0.498039245605,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":21,"ty":3,"nm":"โ–ฝ Group 2","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.106,69.501,0],"ix":2,"l":2},"a":{"a":0,"k":[65.06,55.953,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"eye_2","parent":30,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.964,-15.717,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[1.351,0.55],[-0.23,0.564],[0,0],[-1.351,-0.55],[0.299,-0.738]],"o":[[-1.351,-0.55],[0,0],[0.286,-0.703],[1.351,0.55],[-0.418,1.024]],"v":[[-0.364,0.835],[-2.454,-1.025],[-2.454,-1.032],[0.346,-1.06],[2.436,0.96]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17,"s":[{"i":[[1.218,0.741],[-0.212,0.519],[0,0],[-1.237,-0.522],[0.277,-0.679]],"o":[[-1.147,-0.698],[0,0],[0.552,-1.309],[1.455,0.615],[-0.385,0.942]],"v":[[-1.412,2.024],[-2.472,-0.723],[-2.472,-0.729],[0.91,-2.078],[2.027,1.104]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[1.218,0.741],[-0.212,0.519],[0,0],[-1.237,-0.522],[0.277,-0.679]],"o":[[-1.147,-0.698],[0,0],[0.552,-1.309],[1.455,0.615],[-0.385,0.942]],"v":[[-1.412,2.024],[-2.472,-0.723],[-2.472,-0.729],[0.91,-2.078],[2.027,1.104]],"c":true}]},{"t":48,"s":[{"i":[[1.351,0.55],[-0.23,0.564],[0,0],[-1.351,-0.55],[0.299,-0.738]],"o":[[-1.351,-0.55],[0,0],[0.286,-0.703],[1.351,0.55],[-0.418,1.024]],"v":[[-0.364,0.835],[-2.454,-1.025],[-2.454,-1.032],[0.346,-1.06],[2.436,0.96]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.372549027205,0.368627458811,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"eye_1","parent":30,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-47.734,-15.612,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-1.198,0.488],[0.202,0.501],[0,0],[1.198,-0.495],[-0.265,-0.655]],"o":[[1.198,-0.488],[0,0],[-0.251,-0.62],[-1.198,0.495],[0.369,0.912]],"v":[[0.321,0.743],[2.181,-0.914],[2.181,-0.921],[-0.305,-0.942],[-2.165,0.855]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17,"s":[{"i":[[-1.564,0.58],[0.361,0.886],[0,0],[1.785,-0.836],[-0.265,-0.655]],"o":[[1.408,-0.522],[0,0],[-0.327,-0.732],[-1.174,0.55],[0.369,0.912]],"v":[[1.134,2.118],[2.181,-0.914],[2.181,-0.921],[-1.118,-1.817],[-2.165,0.855]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[-1.564,0.58],[0.361,0.886],[0,0],[1.785,-0.836],[-0.265,-0.655]],"o":[[1.408,-0.522],[0,0],[-0.327,-0.732],[-1.174,0.55],[0.369,0.912]],"v":[[1.134,2.118],[2.181,-0.914],[2.181,-0.921],[-1.118,-1.817],[-2.165,0.855]],"c":true}]},{"t":48,"s":[{"i":[[-1.198,0.488],[0.202,0.501],[0,0],[1.198,-0.495],[-0.265,-0.655]],"o":[[1.198,-0.488],[0,0],[-0.251,-0.62],[-1.198,0.495],[0.369,0.912]],"v":[[0.321,0.743],[2.181,-0.914],[2.181,-0.921],[-0.305,-0.942],[-2.165,0.855]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.372549027205,0.368627458811,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Vector","parent":30,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-21.365,-10.39,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.043,0],[0,1.9],[3.043,0],[0,-1.9]],"o":[[3.043,0],[0,-1.9],[-3.043,0],[0,1.9]],"v":[[0,3.441],[5.509,0],[0,-3.441],[-5.509,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.666666686535,0.57647061348,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"Vector","parent":30,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-52.699,-10.822,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.7,0],[0,1.281],[1.7,0],[0,-1.281]],"o":[[1.7,0],[0,-1.281],[-1.7,0],[0,1.281]],"v":[[0,2.319],[3.078,0],[0,-2.319],[-3.078,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.666666686535,0.57647061348,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"Vector","parent":30,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-38.031,-7.883,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.481,2.382],[0.425,0],[0,0],[-0.084,-0.411],[-3.106,0]],"o":[[0.084,-0.411],[0,0],[-0.418,0],[0.481,2.389],[3.106,0]],"v":[[6.171,-1.699],[5.537,-2.507],[-5.537,-2.507],[-6.171,-1.699],[0,2.507]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.372549027205,0.368627458811,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"hand_2","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16,"s":[6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":31,"s":[-3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[6]},{"t":65,"s":[-3]}],"ix":10},"p":{"a":0,"k":[56.935,71.222,0],"ix":2,"l":2},"a":{"a":0,"k":[7,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.466,1.609],[0.738,-1.282],[-0.752,-1.08],[-4.437,-1.811],[0,0]],"o":[[0,0],[-2.793,0],[-1.128,-0.738],[-0.669,1.163],[3.044,4.367],[0,0],[0,0]],"v":[[5.305,-4.71],[5.124,-4.71],[-2.879,-7.155],[-6.138,-6.2],[-5.992,-2.391],[5.444,7.047],[6.601,7.52]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":7,"k":{"a":0,"k":[0.089,0.98,0.902,0.792,0.25,0.98,0.896,0.78,0.411,0.98,0.89,0.769,0.636,0.98,0.842,0.741,0.86,0.98,0.794,0.714,0.93,0.99,0.804,0.711,1,1,0.813,0.708,0.089,0,0.25,0.11,0.411,0.22,0.636,0.525,0.86,0.83,0.93,0.915,1,1],"ix":9}},"s":{"a":0,"k":[6.601,-0.002],"ix":5},"e":{"a":0,"k":[-6.598,-0.002],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"hand_1","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16,"s":[10]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":31,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[10]},{"t":65,"s":[-3]}],"ix":10},"p":{"a":0,"k":[13.601,73.702,0],"ix":2,"l":2},"a":{"a":0,"k":[7,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.466,1.609],[0.738,-1.282],[-0.752,-1.08],[-4.437,-1.811],[0,0]],"o":[[0,0],[-2.793,0],[-1.128,-0.738],[-0.669,1.163],[3.044,4.367],[0,0],[0,0]],"v":[[5.305,-4.71],[5.124,-4.71],[-2.879,-7.155],[-6.138,-6.2],[-5.992,-2.391],[5.444,7.047],[6.601,7.52]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":5,"k":{"a":0,"k":[0,0.98,0.902,0.792,0.096,0.982,0.894,0.794,0.193,0.984,0.886,0.796,0.596,0.992,0.874,0.79,1,1,0.861,0.783,0,0,0.096,0.5,0.193,1,0.596,1,1,1],"ix":9}},"s":{"a":0,"k":[6.608,-0.002],"ix":5},"e":{"a":0,"k":[-6.591,-0.002],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"body","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64.278,65.132,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":14,"s":[98,104,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":27,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":54,"s":[98,104,100]},{"t":67,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.682,0.501],[4.611,2.584],[0,0],[1.616,12.739],[0,0],[0,6.38],[-5.3,4.297],[0.299,1.351],[0,0],[-1.658,1.191],[-1.867,-1.637],[-5.349,-0.898],[0,0],[0,0],[0,0],[0,0],[0,0],[-3.462,3.462],[0,0],[-0.258,-3.58],[0.348,-2.013],[0,0],[-0.425,-1.685],[-0.146,-0.641],[-1.344,-3.594],[-0.341,-2.863],[-9.827,-1.867],[-1.003,-0.195],[-4.054,-3.51],[0,0],[0.947,-2.835],[3.099,0.313],[0,0],[13.714,-3.204]],"o":[[-5.279,-0.348],[0,0],[-10.134,-7.877],[0,0],[-5.091,-4.269],[0,-6.38],[0.209,-1.358],[0,0],[-0.348,-2.013],[2.013,-1.449],[4.081,3.58],[0,0],[0,0],[0.007,0],[0,0],[0,0],[4.827,-0.815],[0,0],[1.651,-1.651],[0.348,1.574],[0,0],[-0.376,1.692],[0.153,0.641],[1.226,2.709],[1.017,2.695],[1.156,9.73],[1.045,0.195],[5.265,1.038],[0,0],[2.479,1.672],[-0.989,2.953],[0,0],[-14.02,-1.414],[-4.464,1.045]],"v":[[-25.794,46.575],[-40.81,42.089],[-40.824,42.089],[-57.191,7.836],[-53.974,11.549],[-57.972,-1.434],[-51.007,-29.112],[-51.132,-33.207],[-52.107,-38.883],[-49.962,-44.121],[-43.332,-43.801],[-28.907,-36.947],[-27.055,-36.627],[-27.062,-36.627],[-27.048,-36.634],[-27.055,-36.634],[-23.524,-37.226],[-10.785,-43.794],[-8.612,-45.967],[-1.682,-43.181],[-2.002,-38.883],[-2.977,-33.207],[-2.908,-28.074],[-2.462,-26.159],[1.215,-17.961],[3.145,-9.569],[21.218,10.135],[24.29,10.72],[38.512,17.657],[55.13,32.046],[57.728,39.672],[50.742,44.193],[37.432,42.848],[-4.454,45.551]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,0.969,0.89,0.5,0.99,0.926,0.841,1,0.98,0.883,0.792],"ix":9}},"s":{"a":0,"k":[-20.951,4.441],"ix":5},"e":{"a":0,"k":[28.254,4.441],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.111,0.705],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"back_4","parent":33,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-12.935,24.072,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.31,-10.935],[-22.656,-11.896],[0,0],[-6.31,10.935],[22.155,12.794]],"o":[[-6.31,10.935],[0,0],[6.624,3.482],[6.31,-10.935],[-22.155,-12.794]],"v":[[-40.098,-21.891],[-11.41,21.068],[-11.41,21.068],[40.136,24.432],[11.448,-18.527]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":11,"k":{"a":0,"k":[0,0.937,0.565,0.439,0.045,0.912,0.524,0.418,0.09,0.886,0.482,0.396,0.14,0.869,0.449,0.378,0.19,0.851,0.416,0.361,0.255,0.837,0.392,0.349,0.32,0.824,0.369,0.337,0.41,0.816,0.355,0.329,0.5,0.808,0.341,0.322,0.75,0.806,0.339,0.322,1,0.804,0.337,0.322],"ix":9}},"s":{"a":0,"k":[-15.647,-0.018],"ix":5},"e":{"a":0,"k":[70.134,-5.084],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"back_3","parent":33,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3.096,-2.073,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.074,3.455],[-15.88,-9.173],[0,0],[-4.827,-16.597],[2.528,-4.019],[0.536,-0.85],[20.219,11.673],[15.664,-2.082],[-0.369,0.592]],"o":[[14.793,-1.024],[0,0],[19.606,11.318],[-1.351,4.325],[-0.578,0.912],[-4.423,-17.092],[-17.301,-9.988],[0.355,-0.564],[2.995,-4.764]],"v":[[-36.906,-35.89],[10.733,-23.841],[10.747,-23.841],[48.691,20.831],[42.889,33.403],[41.218,36.049],[2.849,-10.169],[-48.691,-21.828],[-47.604,-23.555]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":5,"k":{"a":0,"k":[0,0.937,0.565,0.439,0.285,0.927,0.451,0.38,0.57,0.918,0.337,0.322,0.785,0.927,0.451,0.38,1,0.937,0.565,0.439],"ix":9}},"s":{"a":0,"k":[-48.698,0],"ix":5},"e":{"a":0,"k":[48.67,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"back_2","parent":21,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[-4]},{"t":67,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[70.93,52.773,0],"to":[0.583,-0.5,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[74.43,49.773,0],"to":[0,0,0],"ti":[0.583,-0.5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[70.93,52.773,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[70.93,52.773,0],"to":[0.583,-0.333,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":54,"s":[74.43,50.773,0],"to":[0,0,0],"ti":[0.583,-0.333,0]},{"t":67,"s":[70.93,52.773,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-50.251,-31.961],[-15.727,24.983],[0,0],[24.983,15.727],[15.727,-24.983]],"o":[[40.793,25.944],[0,0],[15.733,-24.99],[-24.99,-15.733],[-15.727,24.99]],"v":[[-11.37,31.705],[45.971,31.329],[45.964,31.329],[29.207,-42.393],[-44.516,-25.636]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":7,"k":{"a":0,"k":[0,0.937,0.784,0.718,0.182,0.937,0.62,0.558,0.365,0.937,0.455,0.399,0.477,0.937,0.434,0.378,0.589,0.937,0.412,0.357,0.784,0.937,0.547,0.457,0.98,0.937,0.682,0.557],"ix":9}},"s":{"a":0,"k":[-54.19,0.002],"ix":5},"e":{"a":0,"k":[54.189,0.002],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"back_1","parent":33,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30.365,-36.361,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.885,-0.773],[0,0],[0,0],[0,0],[0,0],[0.808,1.644],[13.045,2.02]],"o":[[0,0],[0,0],[0,0],[0,0],[1.316,-1.282],[-5.356,-12.063],[-1.163,-0.181]],"v":[[-13.242,-14.432],[-18.577,-9.779],[-18.584,-9.772],[11.093,15.413],[17.305,9.367],[18.148,4.471],[-10.01,-15.365]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":7,"k":{"a":0,"k":[0,0.937,0.784,0.718,0.182,0.937,0.62,0.558,0.365,0.937,0.455,0.399,0.477,0.937,0.434,0.378,0.589,0.937,0.412,0.357,0.784,0.937,0.547,0.457,0.98,0.937,0.682,0.557],"ix":9}},"s":{"a":0,"k":[-18.584,0.001],"ix":5},"e":{"a":0,"k":[18.584,0.001],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":"boom_9","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":360,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":36,"ty":4,"nm":"boom_8","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":315,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":37,"ty":4,"nm":"boom_7","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":38,"ty":4,"nm":"boom_6","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":225,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":39,"ty":4,"nm":"boom_5","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":180,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":40,"ty":4,"nm":"boom_4","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":41,"ty":4,"nm":"boom_3","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":42,"ty":4,"nm":"boom_2","parent":43,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":28,"s":[100]}],"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[-34.5,-11.5,0],"ix":2,"l":2},"a":{"a":0,"k":[26,-5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[67.5,67.5,100]},{"t":26,"s":[102.455,102.455,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[26,-5],[26,-35]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.862545955882,0.862545955882,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.372549019608,0.36862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"t":48,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":46,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":43,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[75,75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.303,11.303],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.937255021638,0.494118006089,0.415686005237,1],"ix":4},"o":{"a":0,"k":0,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.349,-10.349],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":85,"st":5,"ct":1,"bm":0},{"ddd":0,"ind":44,"ty":4,"nm":"round_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[55]},{"t":41,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41,64.5,0],"ix":2,"l":2},"a":{"a":0,"k":[-2,3,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[23,23,100]},{"t":41,"s":[227.439,227.439,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[27.438,27.438],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":28,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.281,3.219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":45,"ty":4,"nm":"snail_orange_cheer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[75,75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[150,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"snail_orange_cheer","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"snail_orange_cheer","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[70,70,0],"ix":2,"l":2},"a":{"a":0,"k":[75,75,0],"ix":1,"l":2},"s":{"a":0,"k":[-94,94,100],"ix":6,"l":2}},"ao":0,"w":150,"h":150,"ip":0,"op":85,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 78d150ed..0968afce 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,6 +9,7 @@ #FFFFFFFF + #FFF6F2 #FFECE5 #FFD8CC #FFCDBD diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..56c79535 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,15 @@ + + + 4dp + 8dp + 10dp + 12dp + 14dp + 16dp + 20dp + 24dp + 36dp + + + 48dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94ddbc75..3ca535ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,169 @@ KeepGoEat upload + 1.0.0 + Keepgo2at@gmail.com + market://details?id= + + + ํ‚ต๊ณ ์ž‡ + ์™„๋ฃŒ + ๋œ ๋จน๊ธฐ + ๋” ๋จน๊ธฐ + ์•„๋‹ˆ์˜ค + %s ๋” ๋จน๊ธฐ + %s ๋œ ๋จน๊ธฐ + ์ทจ์†Œ + ๋„ค + ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. + + + ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ + ๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ + ์‹์Šต๊ด€ ๊ด€๋ฆฌ, + ๊ฐ™์ด ์ฒœ์ฒœํžˆ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”?\n๋จน๋Š” ์žฌ๋ฏธ๋Š” ์–ธ์ œ๋‚˜ ์†Œ์ค‘ํ•˜๋‹ˆ๊นŒ! + ์ž๋™ ๋กœ๊ทธ์ธ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ๋กœ๊ทธ์ธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค. + + + ๋‚˜์˜ ๋ชฉํ‘œ + %s %s + ์ง€๋‚œ๋‹ฌ %s ๋‚  + ์ด๋ฒˆ๋‹ฌ %s ๋‚  + ๋จน์€ + ์ฐธ์€ + ๋ชฉํ‘œ๊ฐ€ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ๋ชฉํ‘œ๊ฐ€ ๋ณด๊ด€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + + + ์˜ค๋Š˜ ์‹์Šต๊ด€ ๋ชฉํ‘œ + + ์˜ค๋Š˜๋„ ์ž˜ ๋จน์—ˆ๋‚˜์š”? + ์˜ค๋Š˜๋„ ํ•ด๋ƒˆ์–ด์š”! + ์˜ค๋Š˜๋„ ์ž˜ ์ฐธ์•˜๋‚˜์š”? + ์˜ค๋Š˜๋„ ์ฐธ์•˜์–ด์š”! + + ์ผ ํ•ด๋ƒˆ์–ด์š”. + + ๋˜ ๋‹ค๋ฅธ ๋„์ „์„ ์›ํ•˜์‹œ๋‚˜์š”? + 2๊ฐœ์˜ ๋ชฉํ‘œ๋ฅผ ๋” ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. + 1๊ฐœ์˜ ๋ชฉํ‘œ๋ฅผ ๋” ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. + 3๊ฐœ์˜ ๋ชฉํ‘œ๊นŒ์ง€ ๋™์‹œ์— ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์š”. + + ์•„์ง ๋ชฉํ‘œ๋ฅผ ์ •ํ•˜์ง€ ์•Š์•˜์–ด์š”. + ์ƒˆ๋กœ์šด ๋ชฉํ‘œ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”. + + ๋ชฉํ‘œ ์ถ”๊ฐ€ํ•˜๊ธฐ + + ๋‚˜ ๊น€๋”ํŒฝ, ๋ฉ‹์ง„ ๋‹ฌํŒฝ...โœจ\n๋‚ด๊ฐ€ ์„ธ์ƒ์„ ์ง€๋ฐฐํ•œ๋‹ค...!! + + + ๊ฑด๊ฐ•ํ•œ ์Œ์‹์„ ๋” ๋จน์–ด๋ณผ๊นŒ? + ์•ˆ ์ข‹์€ ์‹์Šต๊ด€์„ ์ค„์—ฌ๊ฐ€๋ณด์ž! + + + ๋ชฉํ‘œ๋ฅผ ๋ณด๊ด€ํ•˜์‹œ๊ฒ ์–ด์š”? + ๋‹ฌ์„ฑ์„ ์™„๋ฃŒํ•œ ๋ชฉํ‘œ๋Š” ๋ณด๊ด€ํ•ด์„œ ๋ชจ์•„๋ณผ ์ˆ˜ ์žˆ์–ด์š”. + ์ž˜๋ชป๋œ ๋ชฉํ‘œ์ธ๊ฐ€์š”? ์‚ญ์ œํ•˜๊ธฐ + ๋ชฉํ‘œ ๋ณด๊ด€ํ•˜๊ธฐ + + + ์‚ญ์ œํ•œ ๋ชฉํ‘œ๋Š” ๋ณต๊ตฌํ•  ์ˆ˜ ์—†์–ด์š”. + ์‚ญ์ œํ•˜์‹œ๊ฒ ์–ด์š”? + ๋„ค, ์‚ญ์ œํ• ๋ž˜์š” + + + ๋ชฉํ‘œ ์ž…๋ ฅ + ๋‚˜์˜ ๋ชฉํ‘œ๋ฅผ ์ž…๋ ฅํ•ด ๋ณผ๊นŒ์š”? + ex) ๋ฌผ + ex) ์ปคํ”ผ + ๋ชฉํ‘œ๋Š” ์ตœ์†Œ 1๊ธ€์ž ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”. + ํ•œ๊ธ€, ์˜๋ฌธ, ์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + (%d/15) + ๋ชฉํ‘œ ๋‹ฌ์„ฑ ๊ธฐ์ค€์„ ์„ธ์›Œ๋ณผ๊นŒ์š”? + ๋‚˜์—๊ฒŒ ๋งž๋Š” ๋‹ฌ์„ฑ ๊ธฐ์ค€์„ ์„ธ์›Œ์š”. + ex) ํ•˜๋ฃจ์— 2L์”ฉ ๋งˆ์‹œ๊ธฐ + ex) ์•„์นจ์— ๋”ฑ 1์ž”๋งŒ ๋งˆ์‹œ๊ธฐ + (%d/20) + ๋ชฉํ‘œ๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ๋ชฉํ‘œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + + + ์ฐจ๊ทผ์ฐจ๊ทผ ์‹์Šต๊ด€ ๋ชฉํ‘œ๋ฅผ ์„ธ์›Œ๋ณด์„ธ์š”. + ๊ฑด๊ฐ•ํ•œ ์‹์Šต๊ด€์˜ ํ•„์š”์„ฑ์„ ๋Š๋ผ์…จ๋‚˜์š”?\n์ด์ œ๋Š” ์กฐ๊ธˆ์”ฉ ๋” ๋จน๊ณ , ๋œ ๋จน์œผ๋ฉฐ ๊ท ํ˜•์„ ๋งž์ถฐ์š”. + ์กฐ๊ธˆ ๋” ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”! + ์šฐ๋ฆฌ๋Š” ๊ฐ™์ด ํ•ด๋‚ผ ๊ฑฐ์˜ˆ์š”. + ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•œ ๋‚ ์ด๋ฉด ํ‚ต๊ณ ์ž‡์„ ์ฐพ์•„์™€\n๋‹ฌ์„ฑ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”. + ๋งค์ผ ๊ธฐ๋ก์— ์‹คํŒจํ•˜๋ฉด ์–ด์ฉŒ์ฃ ? + ์™„๋ฒฝํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์•„์š”. + ๋…ธ๋ ฅํ•œ ๋‚ ๋“ค์€ ๋นˆํ‹ˆ์—†์ด ์ฑ„์›Œ์งˆ ๊ฑฐ์˜ˆ์š”.\n์„ฑ์ทจ์˜ ์ฆ๊ฑฐ์›€๊ณผ ๋ฟŒ๋“ฏํ•จ์ด ์˜ค๋กฏ์ด ๋‚จ์•„์š”. + KEEP GO EAT ์‹œ์ž‘ํ•˜๊ธฐ + ๊ฑด๋„ˆ๋›ฐ๊ธฐ + + + ์ „์ฒด + ๋งˆ์ดํŽ˜์ด์ง€ + ๋ณด๊ด€ํ•œ ๋ชฉํ‘œ + ์ด ๋‹ฌ์„ฑ์ผ + ์ง„ํ–‰๊ธฐ๊ฐ„ + %s. %s. %s ~ %s. %s. %s + ๋กœ๊ทธ์•„์›ƒ + ํƒˆํ‡ดํ•˜๊ธฐ + ๋ฌธ์˜ ๋ฐ ํ”ผ๋“œ๋ฐฑ + ๋ฌธ์˜ํ•˜๊ธฐ + ๋ฆฌ๋ทฐ ๋‚จ๊ธฐ๊ธฐ + ์•ฑ ์ •๋ณด + ์„œ๋น„์Šค ์†Œ๊ฐœ + ๋ฒ„์ „ ์ •๋ณด + ์•ฝ๊ด€ ๋ฐ ์ •์ฑ… + ์„œ๋น„์Šค ์ด์šฉ์•ฝ๊ด€ + ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ + + ๊ณ„์ • ์ •๋ณด + ๋กœ๊ทธ์•„์›ƒ ํ•˜์‹œ๊ฒ ์–ด์š”? + ๊ทธ๋™์•ˆ์˜ ๊ธฐ๋ก์€ ์‚ฌ๋ผ์ง€์ง€ ์•Š์œผ๋‹ˆ\n์•ˆ์‹ฌํ•˜๊ณ  ๋‹ค๋…€์˜ค์„ธ์š”. + ๋กœ๊ทธ์•„์›ƒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ๋กœ๊ทธ์•„์›ƒ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค. + + ๋”์ด์ƒ ๊ธฐ๋กํ•˜์ง€ ์•Š๋Š” ๋ชฉํ‘œ๋Š” ๋ณด๊ด€ํ•ด์š”. + ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š” ๋ชฉํ‘œ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด,\n๊ทธ๋™์•ˆ์˜ ๋…ธ๋ ฅ์„ ์ง€์šฐ์ง€ ๋ง๊ณ  ๋‚จ๊ฒจ๋ณด์„ธ์š”. + ๋ชฉํ‘œ ๋งŒ๋“ค๋Ÿฌ ๊ฐ€๊ธฐ + ์‚ญ์ œ ํ•˜์‹œ๊ฒ ์–ด์š”? + ์‚ญ์ œ๋œ ๋ชฉํ‘œ์™€ ๋‹ฌ์„ฑ ๊ธฐ๋ก์€\n๋‹ค์‹œ ๋ณต๊ตฌํ•  ์ˆ˜ ์—†์–ด์š”. + + ํ‚ต๊ณ ์ž‡์€\n์˜์–‘์†Œ๋‚˜ ์นผ๋กœ๋ฆฌ์ฒ˜๋Ÿผ ๋ณต์žกํ•œ ๋ฐฉ๋ฒ• ๋Œ€์‹ \n๊ฐ€๋ณ๊ณ , ์ง€์†๊ฐ€๋Šฅํ•œ ์‹์Šต๊ด€ ๊ด€๋ฆฌ๋ฅผ\n๋•๊ธฐ ์œ„ํ•ด ํƒ„์ƒํ–ˆ์–ด์š”.\n\n๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์ค„์ด๊ณ  ์‹ถ์€ ์‹์Šต๊ด€ ๋ชฉํ‘œ๋ฅผ ์„ค์ •ํ•˜๊ณ ,\n๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•œ ๋‚ ์ด๋ฉด ํ‚ต๊ณ ์ž‡์„ ์ฐพ์•„์™€์ฃผ์„ธ์š”.\n๊ฑด๊ฐ•ํ•œ ํ•˜๋ฃจํ•˜๋ฃจ๋ฅผ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์–ด์š”.\n\n๋งค์ผ ์™„๋ฒฝํ•œ ์‹์‚ฌ๋งŒ์„ ํ•˜๊ธฐ๋ž€ ์–ด๋ ต์ฃ .\n๋Œ€์‹  ๊ฑด๊ฐ•ํ•˜์ง€ ์•Š์€ ์Œ์‹์„ ๋œ ๋จน๊ณ ,\n๊ฑด๊ฐ•ํ•œ ์Œ์‹์„ ๋” ๋จน์€ ๋‚ ์„ ๋Š˜๋ ค๊ฐ€๋ณผ๊นŒ์š”?\n\n๋จน๋Š” ๊ฑด ์›๋ž˜ ์ฆ๊ฑฐ์šด ์ผ์ด๋‹ˆ๊นŒ.\n์™„๋ฒฝํ•œ ์‹์‚ฌ์— ๋Œ€ํ•œ ๋ถ€๋‹ด ์—†์ด\nํ‚ต๊ณ ์ž‡์—์„œ ๊ฑด๊ฐ•ํ•œ ์ฆ๊ฑฐ์›€์„ ์Œ“์•„๋ณด์„ธ์š”. + ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ + + ํ‚ต๊ณ ์ž‡ ๋ฌธ์˜์‚ฌํ•ญ + Device: %s %s\nApp version: %s\nOS Version: %d(%s)\n-------------\n๋‚ด์šฉ : + + ๋ฆฌ๋ทฐ๋ฅผ ๋‚จ๊ฒจ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. + + + ํƒˆํ‡ดํ•˜๊ธฐ + ๊ทธ๋™์•ˆ ํ•จ๊ป˜ ํ•ด์„œ ๊ธฐ๋ปค์–ด์š”. + ๋– ๋‚ฌ๋˜ ์ด์œ ๋ฅผ ๋ชจ๋‘ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๋” ๋‚˜์€ ํ‚ต๊ณ ์ž‡์ด ๋ ๊ฒŒ์š”. + + ์‹์Šต๊ด€ ๊ด€๋ฆฌ๋ฅผ ๊ทธ๋งŒ ๋’€์–ด์š”. + ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์š”. + ์•ฑ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์–ด์š”. + ์ฝ˜ํ…์ธ ๊ฐ€ ๋งŒ์กฑ์Šค๋Ÿฝ์ง€ ์•Š์•„์š”. + + ์ง์ ‘ ์ž…๋ ฅ + ๋‹ค๋ฅธ ์ด์œ ๊ฐ€ ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”. + ํƒˆํ‡ด ์ด์œ ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. + + ํƒˆํ‡ด๋Š” ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์–ด์š”. + ์ด ๊ณ„์ •์—์„œ ๋งŒ๋“  ๋ชฉํ‘œ์™€\nํ™œ๋™ ๊ธฐ๋ก์ด ๋ชจ๋‘ ์‚ฌ๋ผ์ ธ์š”. + ํƒˆํ‡ด + ์ทจ์†Œ + + ํšŒ์› ํƒˆํ‡ด๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + ํšŒ์› ํƒˆํ‡ด์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค. + + + ์—…๋ฐ์ดํŠธ ์•Œ๋ฆผ + ์—…๋ฐ์ดํŠธ ํ•˜๋Ÿฌ๊ฐ€๊ธฐ + ๋” ์ข‹์•„์ง„ ํ‚ต๊ณ ์ž‡ ์•ฑ์„ ์‚ฌ์šฉํ•˜์‹œ๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒˆ๋กœ์šด ๋ฒ„์ „ %s์œผ๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ฒ„์ „์€ %s ์ž…๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/app/src/main/res/values/text_appearance.xml b/app/src/main/res/values/text_appearance.xml new file mode 100644 index 00000000..957b2c8b --- /dev/null +++ b/app/src/main/res/values/text_appearance.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index c989bc2d..3b5639e8 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 932ffdc0..64d1389c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,8 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + + maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' } } } rootProject.name = "KeepGoEat"