├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── font │ │ │ │ ├── inter_bold.ttf │ │ │ │ ├── inter_medium.ttf │ │ │ │ └── inter_regular.ttf │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── themes.xml │ │ │ │ └── strings.xml │ │ │ ├── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ ├── drawable │ │ │ │ ├── ic_back_arrow.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ └── which_one_logo.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── raw │ │ │ │ ├── force_update_store_anim.json │ │ │ │ └── opps_anim.json │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ │ └── com │ │ │ │ └── loftymr │ │ │ │ └── whichone │ │ │ │ ├── domain │ │ │ │ ├── viewstate │ │ │ │ │ ├── ViewData.kt │ │ │ │ │ ├── splash │ │ │ │ │ │ └── SplashViewState.kt │ │ │ │ │ └── survey │ │ │ │ │ │ └── SurveyViewState.kt │ │ │ │ └── repository │ │ │ │ │ └── SurveyRepository.kt │ │ │ │ ├── data │ │ │ │ ├── remote │ │ │ │ │ ├── util │ │ │ │ │ │ └── DataState.kt │ │ │ │ │ ├── api │ │ │ │ │ │ └── SurveyService.kt │ │ │ │ │ └── source │ │ │ │ │ │ └── BaseRemoteDataSource.kt │ │ │ │ ├── di │ │ │ │ │ ├── ServiceModule.kt │ │ │ │ │ ├── AppModule.kt │ │ │ │ │ └── RemoteDataModule.kt │ │ │ │ └── model │ │ │ │ │ └── RingsOfThePowerResponse.kt │ │ │ │ ├── feature │ │ │ │ ├── theme │ │ │ │ │ ├── Shape.kt │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Type.kt │ │ │ │ │ └── Theme.kt │ │ │ │ ├── component │ │ │ │ │ ├── WhichOneLottieAnim.kt │ │ │ │ │ ├── WhichOneTemplate.kt │ │ │ │ │ ├── WhichOneButton.kt │ │ │ │ │ ├── CircularProgressAnimated.kt │ │ │ │ │ ├── WhichOneTopBar.kt │ │ │ │ │ ├── Head.kt │ │ │ │ │ ├── StepsProgressBar.kt │ │ │ │ │ ├── SelectionBox.kt │ │ │ │ │ └── AdsView.kt │ │ │ │ ├── screen │ │ │ │ │ ├── splash │ │ │ │ │ │ ├── SplashViewModel.kt │ │ │ │ │ │ └── SplashActivity.kt │ │ │ │ │ ├── main │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ ├── forceupdate │ │ │ │ │ │ └── ForceUpdateScreen.kt │ │ │ │ │ ├── survey │ │ │ │ │ │ ├── SurveyViewModel.kt │ │ │ │ │ │ └── SurveyScreen.kt │ │ │ │ │ └── result │ │ │ │ │ │ └── ResultScreen.kt │ │ │ │ ├── base │ │ │ │ │ └── BaseViewModel.kt │ │ │ │ └── navigation │ │ │ │ │ └── NavGraph.kt │ │ │ │ ├── util │ │ │ │ ├── extension │ │ │ │ │ └── Modifier.kt │ │ │ │ └── ForceUpdateChecker.kt │ │ │ │ └── WhichOneApp.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── loftymr │ │ │ └── whichone │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── loftymr │ │ └── whichone │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── .github └── FUNDING.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── LICENSE ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [tfaki] 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/font/inter_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/font/inter_bold.ttf -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/res/font/inter_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/font/inter_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/inter_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/font/inter_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tfaki/WhichOne-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/domain/viewstate/ViewData.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.domain.viewstate 2 | 3 | /** 4 | * Created by talhafaki on 9.09.2022. 5 | */ 6 | 7 | interface WhichOneViewState 8 | interface WhichOneViewEvent -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 09 13:08:31 TRT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/domain/viewstate/splash/SplashViewState.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.domain.viewstate.splash 2 | 3 | import com.loftymr.whichone.domain.viewstate.WhichOneViewState 4 | 5 | /** 6 | * Created by talhafaki on 9.09.2022. 7 | */ 8 | 9 | data class SplashViewState( 10 | val isLoading: Boolean = false 11 | ) : WhichOneViewState -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/remote/util/DataState.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.remote.util 2 | 3 | /** 4 | * Created by talhafaki on 9.09.2022. 5 | */ 6 | 7 | sealed class DataState { 8 | class Success(val data: T) : DataState() 9 | class Loading : DataState() 10 | class Error(val exception: Exception) : DataState() 11 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "WhichOne" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/loftymr/whichone/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/domain/viewstate/survey/SurveyViewState.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.domain.viewstate.survey 2 | 3 | import com.loftymr.whichone.data.model.RingsOfThePowerResponse 4 | import com.loftymr.whichone.domain.viewstate.WhichOneViewState 5 | 6 | /** 7 | * Created by talhafaki on 9.09.2022. 8 | */ 9 | 10 | data class SurveyViewState( 11 | var isLoading: Boolean = false, 12 | val isError: Boolean = false, 13 | val data: RingsOfThePowerResponse? = null 14 | ) : WhichOneViewState -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/remote/api/SurveyService.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.remote.api 2 | 3 | import com.loftymr.whichone.data.model.RingsOfThePowerResponse 4 | import retrofit2.Response 5 | import retrofit2.http.GET 6 | import retrofit2.http.Query 7 | 8 | /** 9 | * Created by talhafaki on 9.09.2022. 10 | */ 11 | 12 | interface SurveyService { 13 | @GET("") 14 | suspend fun getRingsOfThePowerSurveys( 15 | @Query("count") count: Int = 10 16 | ): Response 17 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/di/ServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.di 2 | 3 | import com.loftymr.whichone.data.remote.api.SurveyService 4 | import dagger.Module 5 | import dagger.Provides 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ViewModelComponent 8 | import retrofit2.Retrofit 9 | 10 | /** 11 | * Created by talhafaki on 9.09.2022. 12 | */ 13 | 14 | @Module 15 | @InstallIn(ViewModelComponent::class) 16 | class ServiceModule { 17 | 18 | @Provides 19 | fun provideSurveyService(retrofit: Retrofit): SurveyService = 20 | retrofit.create(SurveyService::class.java) 21 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/util/extension/Modifier.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.util.extension 2 | 3 | import androidx.compose.foundation.clickable 4 | import androidx.compose.foundation.interaction.MutableInteractionSource 5 | import androidx.compose.runtime.remember 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.composed 8 | 9 | /** 10 | * Created by talhafaki on 13.09.2022. 11 | */ 12 | 13 | inline fun Modifier.noRippleClickable(crossinline onClick: ()->Unit): Modifier = composed { 14 | clickable(indication = null, 15 | interactionSource = remember { MutableInteractionSource() }) { 16 | onClick() 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.di 2 | 3 | import android.content.Context 4 | import com.loftymr.whichone.WhichOneApp 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.android.qualifiers.ApplicationContext 9 | import dagger.hilt.components.SingletonComponent 10 | import javax.inject.Singleton 11 | 12 | /** 13 | * Created by talhafaki on 9.09.2022. 14 | */ 15 | 16 | @Module 17 | @InstallIn(SingletonComponent::class) 18 | class AppModule { 19 | 20 | @Singleton 21 | @Provides 22 | fun provideApplication(@ApplicationContext app: Context): WhichOneApp { 23 | return app as WhichOneApp 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/loftymr/whichone/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.loftymr.whichone", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/domain/repository/SurveyRepository.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.domain.repository 2 | 3 | import com.loftymr.whichone.data.model.RingsOfThePowerResponse 4 | import com.loftymr.whichone.data.remote.source.BaseRemoteDataSource 5 | import com.loftymr.whichone.data.remote.api.SurveyService 6 | import com.loftymr.whichone.data.remote.util.DataState 7 | import kotlinx.coroutines.flow.Flow 8 | import javax.inject.Inject 9 | 10 | /** 11 | * Created by talhafaki on 9.09.2022. 12 | */ 13 | 14 | class SurveyRepository @Inject constructor( 15 | private val surveyService: SurveyService 16 | ) : BaseRemoteDataSource() { 17 | 18 | suspend fun ringsOfThePowerSurvey(): Flow> = 19 | getResult { 20 | surveyService.getRingsOfThePowerSurveys() 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | object SurveyColor { 6 | val Purple200 = Color(0xFFBB86FC) 7 | val Purple500 = Color(0xFF6200EE) 8 | val Purple700 = Color(0xFF3700B3) 9 | val Teal200 = Color(0xFF03DAC5) 10 | 11 | val SolidBlue = Color(0xFF3552A2) 12 | val Black = Color(0xFF000000) 13 | val DarkGray = Color(0xFF444444) 14 | val Gray = Color(0xFF888888) 15 | val LightGray = Color(0xFFCCCCCC) 16 | val White = Color(0xFFFFFFFF) 17 | val Red = Color(0xFFFF0000) 18 | val Green = Color(0xFF00FF00) 19 | val Blue = Color(0xFF0000FF) 20 | val Yellow = Color(0xFFFFFF00) 21 | val Transparent = Color(0x00000000) 22 | val DeepBlue = Color(0xFF005A70) 23 | val Alabaster = Color(0xFFFAFAFA) 24 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WhichOne 3 | 4 | ca-app-pub-3940256099942544/6300978111 5 | 6 | ca-app-pub-3940256099942544/8691691433 7 | 8 | Something went wrong! 9 | RETRY 10 | AGAIN 11 | 12 | 13 | Rings Of Power 14 | 15 | 16 | 17 | New version available 18 | Please, update app to new version to continue reposting. 19 | Go To Play Store 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Talha Fakıoğlu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/WhichOneLottieAnim.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.annotation.RawRes 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.height 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.unit.dp 9 | import com.airbnb.lottie.compose.* 10 | 11 | /** 12 | * Created by talhafaki on 13.09.2022. 13 | */ 14 | 15 | @Composable 16 | fun WhichOneAnim(modifier: Modifier = Modifier, @RawRes rawResId: Int) { 17 | val composition = 18 | rememberLottieComposition(LottieCompositionSpec.RawRes(rawResId)) 19 | 20 | val progress = animateLottieCompositionAsState( 21 | composition.value, 22 | iterations = LottieConstants.IterateForever, 23 | speed = 1f, 24 | restartOnPlay = false 25 | ) 26 | 27 | LottieAnimation( 28 | composition = composition.value, 29 | progress = progress.value, 30 | modifier = modifier 31 | .fillMaxWidth() 32 | .height(300.dp) 33 | ) 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/WhichOneTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.PaddingValues 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.material.Scaffold 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Modifier 10 | import com.loftymr.whichone.feature.theme.SurveyColor 11 | 12 | /** 13 | * Created by talhafaki on 13.09.2022. 14 | */ 15 | 16 | @Composable 17 | fun WhichOneTemplate( 18 | topBar: @Composable () -> Unit = {}, 19 | content: @Composable (PaddingValues) -> Unit 20 | ) { 21 | Box( 22 | modifier = Modifier 23 | .fillMaxSize() 24 | ) { 25 | Scaffold( 26 | backgroundColor = SurveyColor.Transparent, 27 | content = content, 28 | topBar = topBar, 29 | modifier = Modifier 30 | .fillMaxSize() 31 | .background(color = SurveyColor.SolidBlue) 32 | ) 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/splash/SplashViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.splash 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.loftymr.whichone.feature.base.BaseViewModel 5 | import com.loftymr.whichone.domain.viewstate.WhichOneViewEvent 6 | import com.loftymr.whichone.domain.viewstate.splash.SplashViewState 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.delay 9 | import kotlinx.coroutines.launch 10 | import javax.inject.Inject 11 | 12 | /** 13 | * Created by talhafaki on 9.09.2022. 14 | */ 15 | 16 | @HiltViewModel 17 | class SplashViewModel @Inject constructor( 18 | ) : BaseViewModel() { 19 | 20 | init { 21 | viewModelScope.launch { 22 | delay(1000) 23 | setEvent(SplashViewEvent.DirectToDashBoard) 24 | } 25 | } 26 | 27 | override fun createInitialState() = SplashViewState() 28 | override fun onTriggerEvent(event: SplashViewEvent) {} 29 | } 30 | 31 | sealed class SplashViewEvent : WhichOneViewEvent { 32 | object DirectToDashBoard : SplashViewEvent() 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.Font 6 | import androidx.compose.ui.text.font.FontFamily 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.unit.sp 9 | import com.loftymr.whichone.R 10 | 11 | // Set of Material typography styles to start with 12 | val Fonts = FontFamily( 13 | Font(R.font.inter_bold), 14 | Font(R.font.inter_medium), 15 | Font(R.font.inter_regular) 16 | ) 17 | 18 | val Typography = Typography( 19 | body1 = TextStyle( 20 | fontFamily = Fonts, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 16.sp, 23 | color = SurveyColor.Black 24 | ), 25 | h1 = TextStyle( 26 | fontFamily = Fonts, 27 | fontWeight = FontWeight.Bold, 28 | fontSize = 16.sp, 29 | color = SurveyColor.Black 30 | ), 31 | body2 = TextStyle( 32 | fontFamily = Fonts, 33 | fontWeight = FontWeight.Medium, 34 | fontSize = 16.sp, 35 | color = SurveyColor.Black 36 | ) 37 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/remote/source/BaseRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.remote.source 2 | 3 | import com.loftymr.whichone.data.remote.util.DataState 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.flow.* 6 | import retrofit2.Response 7 | import java.lang.Exception 8 | 9 | /** 10 | * Created by talhafaki on 9.09.2022. 11 | */ 12 | 13 | open class BaseRemoteDataSource { 14 | protected suspend fun getResult(call: suspend () -> Response): Flow> { 15 | return flow> { 16 | val response = call() 17 | if (response.isSuccessful) { 18 | val body = response.body() 19 | if (body != null) emit(DataState.Success(body)) 20 | else { 21 | emit(DataState.Error(exception = Exception("Something went wrong"))) 22 | } 23 | } else { 24 | emit(DataState.Error(exception = Exception("Something went wrong"))) 25 | } 26 | 27 | } 28 | .catch { 29 | emit(DataState.Error(exception = Exception(it.message ?: "Something went wrong"))) 30 | } 31 | .onStart { emit(DataState.Loading()) } 32 | .flowOn(Dispatchers.IO) 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/model/RingsOfThePowerResponse.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * Created by talhafaki on 9.09.2022. 7 | */ 8 | 9 | data class RingsOfThePowerResponse( 10 | @SerializedName("questions") 11 | val questions: List? = null, 12 | @SerializedName("charecter") 13 | val character: Character? = null, 14 | @SerializedName("backGroundPictures") 15 | val backgroundPictures: List? = null 16 | ) 17 | 18 | data class Questions( 19 | @SerializedName("id") 20 | val id: String? = null, 21 | @SerializedName("questionText") 22 | val questionText: String? = null, 23 | @SerializedName("choices") 24 | val choices: List? = null 25 | ) 26 | 27 | data class Character( 28 | @SerializedName("title") 29 | val title: String? = null, 30 | @SerializedName("srcset") 31 | val srcSet: SrcSet? = null, 32 | @SerializedName("description") 33 | val description: String? = null 34 | ) 35 | 36 | data class SrcSet( 37 | @SerializedName("320w") 38 | val threeHundred: String? = null, 39 | @SerializedName("480w") 40 | val fourHundred: String? = null, 41 | @SerializedName("650w") 42 | val sixHundred: String? = null, 43 | @SerializedName("970w") 44 | val nineHundred: String? = null 45 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = SurveyColor.Purple200, 11 | primaryVariant = SurveyColor.Purple700, 12 | secondary = SurveyColor.Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = SurveyColor.Purple500, 17 | primaryVariant = SurveyColor.Purple700, 18 | secondary = SurveyColor.Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun WhichOneTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { 32 | val colors = if (darkTheme) { 33 | DarkColorPalette 34 | } else { 35 | LightColorPalette 36 | } 37 | 38 | MaterialTheme( 39 | colors = colors, 40 | typography = Typography, 41 | shapes = Shapes, 42 | content = content 43 | ) 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/WhichOneApp.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone 2 | 3 | import android.app.Application 4 | import android.util.Log 5 | import com.google.firebase.FirebaseApp 6 | import com.google.firebase.remoteconfig.FirebaseRemoteConfig 7 | import com.loftymr.whichone.util.ForceUpdateChecker 8 | import dagger.hilt.android.HiltAndroidApp 9 | 10 | 11 | /** 12 | * Created by talhafaki on 9.09.2022. 13 | */ 14 | 15 | @HiltAndroidApp 16 | class WhichOneApp : Application() { 17 | 18 | override fun onCreate() { 19 | super.onCreate() 20 | 21 | val firebaseRemoteConfig: FirebaseRemoteConfig = FirebaseRemoteConfig.getInstance() 22 | 23 | // set in-app defaults 24 | val remoteConfigDefaults: MutableMap = HashMap() 25 | remoteConfigDefaults[ForceUpdateChecker.KEY_UPDATE_REQUIRED] = false 26 | remoteConfigDefaults[ForceUpdateChecker.KEY_CURRENT_VERSION] = "1.0.0" 27 | remoteConfigDefaults[ForceUpdateChecker.KEY_UPDATE_URL] = 28 | "https://play.google.com/store/apps/details?id=com.fribbleapp.mathriddles&hl=tr&gl=US" 29 | firebaseRemoteConfig.setDefaultsAsync(remoteConfigDefaults) 30 | firebaseRemoteConfig.fetch(60) // fetch every minutes 31 | .addOnCompleteListener { 32 | if (it.isSuccessful) { 33 | firebaseRemoteConfig.fetchAndActivate() 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | 25 | BASE_URL = "" -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.splash 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import androidx.activity.viewModels 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen 9 | import androidx.lifecycle.lifecycleScope 10 | import com.loftymr.whichone.feature.screen.main.MainActivity 11 | 12 | /** 13 | * Created by talhafaki on 9.09.2022. 14 | */ 15 | @SuppressLint("CustomSplashScreen") 16 | class SplashActivity : AppCompatActivity() { 17 | private val viewModel by viewModels() 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | val splashScreen = installSplashScreen() 21 | super.onCreate(savedInstanceState) 22 | splashScreen.setKeepOnScreenCondition { true } 23 | 24 | lifecycleScope.launchWhenCreated { 25 | viewModel.uiEvent.collect { 26 | when (it) { 27 | is SplashViewEvent.DirectToDashBoard -> { 28 | startMainActivity() 29 | finish() 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | private fun startMainActivity() { 37 | val intent = 38 | Intent(Intent.ACTION_VIEW).setClassName(packageName, MainActivity::class.java.name) 39 | startActivity(intent) 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/WhichOneButton.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.foundation.layout.* 4 | import androidx.compose.foundation.shape.RoundedCornerShape 5 | import androidx.compose.material.* 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.unit.dp 10 | import androidx.compose.ui.unit.sp 11 | import com.loftymr.whichone.feature.theme.SurveyColor 12 | 13 | /** 14 | * Created by talhafaki on 13.09.2022. 15 | */ 16 | 17 | @Composable 18 | fun WhichOneButton(modifier: Modifier = Modifier, buttonText: String, onClick: () -> Unit) { 19 | Card( 20 | modifier = modifier 21 | .fillMaxWidth() 22 | .height(50.dp) 23 | .padding(horizontal = 16.dp), 24 | elevation = 10.dp, 25 | shape = RoundedCornerShape(8.dp) 26 | ) { 27 | Button( 28 | onClick = { onClick.invoke() }, 29 | modifier = Modifier 30 | .fillMaxSize(), 31 | colors = ButtonDefaults.buttonColors(backgroundColor = SurveyColor.Alabaster) 32 | ) { 33 | Text( 34 | text = buttonText, 35 | modifier = Modifier 36 | .fillMaxSize() 37 | .wrapContentSize(align = Alignment.Center), 38 | style = MaterialTheme.typography.h1.copy( 39 | fontSize = 18.sp, 40 | color = SurveyColor.SolidBlue 41 | ) 42 | ) 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.base 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.loftymr.whichone.domain.viewstate.WhichOneViewEvent 6 | import com.loftymr.whichone.domain.viewstate.WhichOneViewState 7 | import kotlinx.coroutines.flow.* 8 | import kotlinx.coroutines.launch 9 | 10 | /** 11 | * Created by talhafaki on 9.09.2022. 12 | */ 13 | abstract class BaseViewModel : ViewModel() { 14 | 15 | private val initialState: State by lazy { createInitialState() } 16 | abstract fun createInitialState(): State 17 | 18 | val currentState: State get() = uiState.value 19 | 20 | abstract fun onTriggerEvent(event: Event) 21 | 22 | private val _uiState: MutableStateFlow = MutableStateFlow(initialState) 23 | val uiState: StateFlow = _uiState 24 | 25 | private val _uiEvent: MutableSharedFlow = MutableSharedFlow() 26 | val uiEvent = _uiEvent.asSharedFlow() 27 | 28 | protected fun setState(reduce: State.() -> State) { 29 | val newState = currentState.reduce() 30 | _uiState.value = newState 31 | } 32 | 33 | protected fun setEvent(event: Event) { 34 | viewModelScope.launch { _uiEvent.emit(event) } 35 | } 36 | 37 | protected suspend fun call( 38 | callFlow: Flow, 39 | completionHandler: (collect: T) -> Unit = {} 40 | ) { 41 | callFlow 42 | .catch { } 43 | .collect { 44 | completionHandler.invoke(it) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/CircularProgressAnimated.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.animation.core.animateFloat 4 | import androidx.compose.animation.core.infiniteRepeatable 5 | import androidx.compose.animation.core.rememberInfiniteTransition 6 | import androidx.compose.animation.core.tween 7 | import androidx.compose.foundation.background 8 | import androidx.compose.foundation.layout.Box 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.foundation.layout.wrapContentSize 11 | import androidx.compose.material.CircularProgressIndicator 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import com.loftymr.whichone.feature.theme.SurveyColor 17 | 18 | /** 19 | * Created by talhafaki on 13.09.2022. 20 | */ 21 | 22 | @Composable 23 | fun CircularProgressAnimated(modifier: Modifier = Modifier) { 24 | val progressValue = 1f 25 | val infiniteTransition = rememberInfiniteTransition() 26 | 27 | val progressAnimationValue by infiniteTransition.animateFloat( 28 | initialValue = 0.0f, 29 | targetValue = progressValue, animationSpec = infiniteRepeatable(animation = tween(900)) 30 | ) 31 | 32 | Box( 33 | modifier = modifier 34 | .fillMaxSize() 35 | .background(color = SurveyColor.SolidBlue), 36 | contentAlignment = Alignment.Center 37 | ) { 38 | CircularProgressIndicator( 39 | progress = progressAnimationValue, 40 | modifier = Modifier 41 | .wrapContentSize(), 42 | color = SurveyColor.White.copy(alpha = 0.8f) 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/WhichOneTopBar.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.material.MaterialTheme 9 | import androidx.compose.material.Text 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.res.painterResource 15 | import androidx.compose.ui.text.style.TextAlign 16 | import androidx.compose.ui.unit.dp 17 | import com.loftymr.whichone.R 18 | import com.loftymr.whichone.feature.theme.SurveyColor 19 | 20 | /** 21 | * Created by talhafaki on 13.09.2022. 22 | */ 23 | 24 | @Composable 25 | fun WhichOneTopBar( 26 | title: String, 27 | backButtonEnabled: Boolean = true, 28 | clickBack: () -> Unit = {} 29 | ) { 30 | Box( 31 | modifier = Modifier 32 | .fillMaxWidth() 33 | ) { 34 | if (backButtonEnabled) { 35 | Image( 36 | painter = painterResource(id = R.drawable.ic_back_arrow), 37 | contentDescription = "", 38 | modifier = Modifier 39 | .align(Alignment.TopStart) 40 | .padding(start = 16.dp) 41 | .clickable { 42 | clickBack.invoke() 43 | } 44 | ) 45 | } 46 | 47 | Text( 48 | text = title, 49 | style = MaterialTheme.typography.body1.copy(color = SurveyColor.White), 50 | textAlign = TextAlign.Center, 51 | modifier = Modifier 52 | .fillMaxWidth() 53 | .align(Alignment.Center) 54 | ) 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/Head.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.foundation.Image 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material3.Card 8 | import androidx.compose.material3.CardDefaults 9 | import androidx.compose.material3.LinearProgressIndicator 10 | import androidx.compose.material3.ProgressIndicatorDefaults 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.getValue 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.layout.ContentScale 15 | import androidx.compose.ui.unit.dp 16 | import coil.compose.rememberImagePainter 17 | import com.loftymr.whichone.feature.theme.SurveyColor 18 | 19 | /** 20 | * Created by talhafaki on 9.09.2022. 21 | */ 22 | 23 | @Composable 24 | fun Head(imageSource: String, numberOfSteps: Int, currentStep: Int) { 25 | val animatedProgress by animateFloatAsState( 26 | targetValue = (currentStep + 1) / (numberOfSteps + 1).toFloat(), 27 | animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec 28 | ) 29 | Card( 30 | modifier = Modifier 31 | .fillMaxWidth() 32 | .height(250.dp) 33 | .padding(16.dp), 34 | shape = RoundedCornerShape(8.dp), 35 | elevation = CardDefaults.cardElevation(10.dp) 36 | ) { 37 | Image( 38 | painter = rememberImagePainter(imageSource), 39 | contentDescription = "", 40 | contentScale = ContentScale.Crop, 41 | modifier = Modifier 42 | .fillMaxSize() 43 | ) 44 | } 45 | Spacer(modifier = Modifier.height(8.dp)) 46 | LinearProgressIndicator( 47 | progress = animatedProgress, 48 | modifier = Modifier 49 | .fillMaxWidth() 50 | .padding(horizontal = 20.dp), 51 | color = SurveyColor.DeepBlue, 52 | trackColor = SurveyColor.DeepBlue.copy(alpha = 0.12f), 53 | ) 54 | Spacer(modifier = Modifier.height(12.dp)) 55 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | #OKHTTP 24 | -keepattributes Signature 25 | -keepattributes *Annotation* 26 | -keep class okhttp3.** { *; } 27 | -keep interface okhttp3.** { *; } 28 | -dontwarn okhttp3.** 29 | -dontwarn Java.nio.file.* 30 | 31 | #Gson 32 | -keep class com.google.gson.stream.** { *; } 33 | 34 | #Retrofit 35 | -keep class com.google.gson.** { *; } 36 | -keep public class com.google.gson.** {public private protected *;} 37 | -keep class com.google.inject.** { *; } 38 | -keep class org.apache.http.** { *; } 39 | -keep class org.apache.james.mime4j.** { *; } 40 | -keep class javax.inject.** { *; } 41 | -keep class javax.xml.stream.** { *; } 42 | -keep class retrofit.** { *; } 43 | -keep class com.google.appengine.** { *; } 44 | -keepattributes *Annotation* 45 | -keepattributes Signature 46 | -dontwarn com.squareup.okhttp.* 47 | -dontwarn javax.xml.stream.** 48 | -dontwarn com.google.appengine.** 49 | -dontwarn java.nio.file.** 50 | -dontwarn org.codehaus.** 51 | 52 | -keep class com.loftymr.whichone.feature.screen.splash.SplashActivity 53 | -keep class com.loftymr.whichone.feature.screen.main.MainActivity 54 | -keep class com.loftymr.whichone.data.model.Character 55 | -keep class com.loftymr.whichone.data.model.Questions 56 | -keep class com.loftymr.whichone.data.model.RingsOfThePowerResponse 57 | -keep class com.loftymr.whichone.data.model.SrcSet 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/StepsProgressBar.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.foundation.Canvas 4 | import androidx.compose.foundation.border 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.Row 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.foundation.shape.CircleShape 9 | import androidx.compose.material.Divider 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.unit.dp 15 | import com.loftymr.whichone.feature.theme.SurveyColor 16 | 17 | /** 18 | * Created by talhafaki on 13.09.2022. 19 | */ 20 | 21 | @Composable 22 | fun StepsProgressBar(modifier: Modifier = Modifier, numberOfSteps: Int, currentStep: Int) { 23 | Row( 24 | modifier = modifier, 25 | verticalAlignment = Alignment.CenterVertically 26 | ) { 27 | for (step in 0..numberOfSteps) { 28 | Step( 29 | modifier = Modifier.weight(1F), 30 | isCompete = step < currentStep, 31 | isCurrent = step == currentStep 32 | ) 33 | } 34 | } 35 | } 36 | 37 | @Composable 38 | fun Step(modifier: Modifier = Modifier, isCompete: Boolean, isCurrent: Boolean) { 39 | val color = if (isCompete || isCurrent) SurveyColor.SolidBlue else Color.LightGray 40 | val innerCircleColor = if (isCompete) SurveyColor.SolidBlue else Color.LightGray 41 | 42 | Box(modifier = modifier) { 43 | 44 | //Line 45 | Divider( 46 | modifier = Modifier.align(Alignment.CenterStart), 47 | color = color, 48 | thickness = 2.dp 49 | ) 50 | 51 | //Circle 52 | Canvas(modifier = Modifier 53 | .size(15.dp) 54 | .align(Alignment.CenterEnd) 55 | .border( 56 | shape = CircleShape, 57 | width = 2.dp, 58 | color = color 59 | ), 60 | onDraw = { 61 | drawCircle(color = innerCircleColor) 62 | } 63 | ) 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.main 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import androidx.activity.ComponentActivity 7 | import androidx.activity.compose.setContent 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.material.MaterialTheme 10 | import androidx.compose.material.Surface 11 | import androidx.compose.ui.Modifier 12 | import com.google.android.gms.ads.MobileAds 13 | import com.google.firebase.FirebaseApp 14 | import com.loftymr.whichone.feature.navigation.NavGraph 15 | import com.loftymr.whichone.feature.theme.WhichOneTheme 16 | import com.loftymr.whichone.util.ForceUpdateChecker 17 | import dagger.hilt.android.AndroidEntryPoint 18 | 19 | @AndroidEntryPoint 20 | class MainActivity : ComponentActivity() { 21 | 22 | private var navigateURL = "" 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | FirebaseApp.initializeApp(this) 27 | ForceUpdateChecker.with(this) 28 | .onUpdateNeeded(object : ForceUpdateChecker.OnUpdateNeededListener { 29 | override fun onUpdateNeeded(updateUrl: String?) { 30 | navigateURL = updateUrl.orEmpty() 31 | } 32 | 33 | }).check() 34 | setContent { 35 | WhichOneTheme(darkTheme = false) { 36 | // A surface container using the 'background' color from the theme 37 | Surface( 38 | modifier = Modifier 39 | .fillMaxSize(), 40 | color = MaterialTheme.colors.background 41 | ) { 42 | NavGraph( 43 | updateURL = navigateURL, 44 | navigateToPlayStore = { 45 | redirectStore(navigateURL) 46 | } 47 | ) 48 | } 49 | } 50 | } 51 | MobileAds.initialize(this) 52 | } 53 | 54 | private fun redirectStore(updateUrl: String) { 55 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(updateUrl)) 56 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 57 | startActivity(intent) 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/navigation/NavGraph.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.navigation 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.navigation.NavType 5 | import androidx.navigation.compose.NavHost 6 | import androidx.navigation.compose.composable 7 | import androidx.navigation.compose.rememberNavController 8 | import androidx.navigation.navArgument 9 | import com.loftymr.whichone.feature.screen.forceupdate.ForceUpdateScreen 10 | import com.loftymr.whichone.feature.screen.result.ResultScreen 11 | import com.loftymr.whichone.feature.screen.survey.SurveyScreen 12 | import java.net.URLEncoder 13 | import java.nio.charset.StandardCharsets 14 | 15 | /** 16 | * Created by talhafaki on 10.09.2022. 17 | */ 18 | 19 | @Composable 20 | fun NavGraph(updateURL: String, navigateToPlayStore: () -> Unit) { 21 | val navController = rememberNavController() 22 | 23 | NavHost( 24 | navController, 25 | startDestination = if (updateURL.isEmpty()) "survey" else "forceUpdate" 26 | ) { 27 | composable(route = "survey") { 28 | SurveyScreen( 29 | navigateToResult = { title, srcSet, desc -> 30 | val encodedUrl = URLEncoder.encode(srcSet, StandardCharsets.UTF_8.toString()) 31 | navController.navigate("result/$encodedUrl/$title/$desc") 32 | } 33 | ) 34 | } 35 | 36 | composable( 37 | route = "result/{imageSource}/{title}/{desc}", 38 | arguments = listOf( 39 | navArgument("title") { 40 | defaultValue = "" 41 | type = NavType.StringType 42 | }, 43 | navArgument("imageSource") { 44 | defaultValue = "" 45 | type = NavType.StringType 46 | }, 47 | navArgument("desc") { 48 | defaultValue = "" 49 | type = NavType.StringType 50 | } 51 | )) { 52 | ResultScreen( 53 | title = it.arguments?.getString("title").orEmpty(), 54 | character = it.arguments?.getString("imageSource").orEmpty(), 55 | desc = it.arguments?.getString("desc").orEmpty(), 56 | navigateToSurvey = { 57 | navController.navigate("survey") 58 | } 59 | ) 60 | } 61 | 62 | composable(route = "forceUpdate") { 63 | ForceUpdateScreen( 64 | navigateToPlayStore = { 65 | navigateToPlayStore.invoke() 66 | } 67 | ) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/SelectionBox.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import androidx.compose.animation.Animatable 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.foundation.BorderStroke 6 | import androidx.compose.foundation.layout.* 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material3.Card 9 | import androidx.compose.material.MaterialTheme 10 | import androidx.compose.material.Text 11 | import androidx.compose.material3.CardDefaults 12 | import androidx.compose.runtime.* 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.text.style.TextAlign 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.unit.sp 17 | import com.loftymr.whichone.feature.theme.SurveyColor 18 | import com.loftymr.whichone.util.extension.noRippleClickable 19 | import kotlinx.coroutines.delay 20 | 21 | /** 22 | * Created by talhafaki on 13.09.2022. 23 | */ 24 | 25 | @Composable 26 | fun SelectionBox( 27 | modifier: Modifier = Modifier, 28 | text: String, 29 | isSelected: () -> Unit 30 | ) { 31 | val color = remember { Animatable(SurveyColor.White) } 32 | var isClicked by remember { mutableStateOf(false) } 33 | if (isClicked) { 34 | LaunchedEffect(Unit) { 35 | color.animateTo(SurveyColor.DeepBlue, animationSpec = tween(150)) 36 | delay(500) 37 | isSelected.invoke() 38 | isClicked = false 39 | } 40 | } 41 | 42 | Spacer(modifier = Modifier.height(6.dp)) 43 | val backgroundColor = if (isClicked) color.value else SurveyColor.White 44 | val borderStroke = if (isClicked) BorderStroke( 45 | width = 1.dp, 46 | color = SurveyColor.SolidBlue 47 | ) else BorderStroke(width = 1.dp, color = SurveyColor.Black) 48 | Card( 49 | modifier = modifier 50 | .fillMaxWidth() 51 | .noRippleClickable { 52 | isClicked = true 53 | }, 54 | shape = RoundedCornerShape(8.dp), 55 | elevation = CardDefaults.cardElevation(6.dp), 56 | colors = CardDefaults.cardColors(backgroundColor), 57 | border = borderStroke 58 | ) { 59 | Text( 60 | text = text, 61 | modifier = Modifier 62 | .fillMaxSize() 63 | .padding(10.dp), 64 | style = MaterialTheme.typography.body2.copy( 65 | color = if (isClicked) SurveyColor.White else SurveyColor.Black, 66 | textAlign = TextAlign.Center, 67 | fontSize = 14.sp 68 | ) 69 | ) 70 | } 71 | Spacer(modifier = Modifier.height(6.dp)) 72 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/forceupdate/ForceUpdateScreen.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.forceupdate 2 | 3 | import androidx.compose.foundation.layout.* 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.res.stringResource 10 | import androidx.compose.ui.text.style.TextAlign 11 | import androidx.compose.ui.unit.dp 12 | import androidx.compose.ui.unit.sp 13 | import com.loftymr.whichone.R 14 | import com.loftymr.whichone.feature.component.WhichOneAnim 15 | import com.loftymr.whichone.feature.component.WhichOneButton 16 | import com.loftymr.whichone.feature.component.WhichOneTemplate 17 | import com.loftymr.whichone.feature.theme.SurveyColor 18 | 19 | /** 20 | * Created by talhafaki on 10.09.2022. 21 | */ 22 | 23 | @Composable 24 | fun ForceUpdateScreen(navigateToPlayStore: () -> Unit) { 25 | WhichOneTemplate { 26 | ForceUpdateContent( 27 | navigateToPlayStore = { 28 | navigateToPlayStore.invoke() 29 | } 30 | ) 31 | } 32 | } 33 | 34 | @Composable 35 | fun ForceUpdateContent(navigateToPlayStore: () -> Unit) { 36 | 37 | Column( 38 | modifier = Modifier 39 | .fillMaxSize(), 40 | verticalArrangement = Arrangement.Top 41 | ) { 42 | 43 | WhichOneAnim( 44 | rawResId = R.raw.force_update_store_anim 45 | ) 46 | 47 | Text( 48 | text = stringResource(id = R.string.new_version_available), 49 | modifier = Modifier 50 | .fillMaxWidth() 51 | .wrapContentWidth(Alignment.CenterHorizontally) 52 | .padding(horizontal = 16.dp, vertical = 4.dp), 53 | style = MaterialTheme.typography.h1.copy(fontSize = 24.sp, color = SurveyColor.White) 54 | ) 55 | 56 | Text( 57 | text = stringResource(id = R.string.new_version_available_desc), 58 | modifier = Modifier 59 | .fillMaxWidth() 60 | .wrapContentWidth(Alignment.CenterHorizontally) 61 | .padding(horizontal = 16.dp, vertical = 8.dp), 62 | style = MaterialTheme.typography.h1.copy( 63 | fontSize = 16.sp, 64 | textAlign = TextAlign.Center, 65 | color = SurveyColor.Alabaster 66 | ) 67 | ) 68 | 69 | Spacer(modifier = Modifier.height(24.dp)) 70 | 71 | WhichOneButton(buttonText = stringResource(id = R.string.go_to_play_store)) { 72 | navigateToPlayStore.invoke() 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/survey/SurveyViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.survey 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.loftymr.whichone.data.remote.util.DataState 5 | import com.loftymr.whichone.domain.repository.SurveyRepository 6 | import com.loftymr.whichone.domain.viewstate.WhichOneViewEvent 7 | import com.loftymr.whichone.domain.viewstate.survey.SurveyViewState 8 | import com.loftymr.whichone.feature.base.BaseViewModel 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import kotlinx.coroutines.delay 11 | import kotlinx.coroutines.launch 12 | import javax.inject.Inject 13 | 14 | /** 15 | * Created by talhafaki on 9.09.2022. 16 | */ 17 | 18 | @HiltViewModel 19 | class SurveyViewModel @Inject constructor( 20 | private val surveyRepository: SurveyRepository 21 | ) : BaseViewModel() { 22 | 23 | init { 24 | getRingsOfThePower() 25 | } 26 | 27 | fun getRingsOfThePower() { 28 | viewModelScope.launch { 29 | setState { currentState.copy(isLoading = true) } 30 | delay(2000) 31 | surveyRepository.ringsOfThePowerSurvey().collect { 32 | when (it) { 33 | is DataState.Success -> { 34 | setState { 35 | if (it.data.character != null && !it.data.questions.isNullOrEmpty() && it.data.backgroundPictures?.isNotEmpty() == true) { 36 | currentState.copy( 37 | data = it.data, 38 | isLoading = false, 39 | isError = false 40 | ) 41 | } else { 42 | currentState.copy(isLoading = false, isError = true) 43 | } 44 | } 45 | } 46 | is DataState.Loading -> { 47 | setState { currentState.copy(isLoading = true, isError = false) } 48 | } 49 | is DataState.Error -> { 50 | setState { currentState.copy(isLoading = false, isError = true) } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | fun showLoading(hasLoading: Boolean) { 58 | if (hasLoading) { 59 | setState { currentState.copy(isLoading = true) } 60 | } else { 61 | setState { currentState.copy(isLoading = false) } 62 | } 63 | } 64 | 65 | override fun createInitialState(): SurveyViewState = SurveyViewState() 66 | override fun onTriggerEvent(event: SurveyViewEvent) {} 67 | } 68 | 69 | sealed class SurveyViewEvent : WhichOneViewEvent {} -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/data/di/RemoteDataModule.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.data.di 2 | 3 | import android.content.Context 4 | import com.chuckerteam.chucker.api.ChuckerCollector 5 | import com.chuckerteam.chucker.api.ChuckerInterceptor 6 | import com.chuckerteam.chucker.api.RetentionManager 7 | import com.loftymr.whichone.BuildConfig 8 | import dagger.Module 9 | import dagger.Provides 10 | import dagger.hilt.InstallIn 11 | import dagger.hilt.android.qualifiers.ApplicationContext 12 | import dagger.hilt.components.SingletonComponent 13 | import okhttp3.OkHttpClient 14 | import okhttp3.logging.HttpLoggingInterceptor 15 | import retrofit2.Retrofit 16 | import retrofit2.converter.gson.GsonConverterFactory 17 | import javax.inject.Singleton 18 | 19 | /** 20 | * Created by talhafaki on 9.09.2022. 21 | */ 22 | 23 | @Module 24 | @InstallIn(SingletonComponent::class) 25 | object RemoteDataModule { 26 | 27 | @Provides 28 | @Singleton 29 | fun provideRetrofit( 30 | okHttpClient: OkHttpClient, 31 | gsonConverterFactory: GsonConverterFactory, 32 | baseURL: String 33 | ): Retrofit { 34 | return Retrofit.Builder().baseUrl(baseURL) 35 | .addConverterFactory(gsonConverterFactory) 36 | .client(okHttpClient).build() 37 | } 38 | 39 | @Provides 40 | fun provideSurveyBaseURL(): String = BuildConfig.BASE_URL 41 | 42 | @Provides 43 | @Singleton 44 | fun provideOkHttpClient( 45 | httpLoggingInterceptor: HttpLoggingInterceptor, 46 | chuckInterceptor: ChuckerInterceptor 47 | ): OkHttpClient = 48 | OkHttpClient.Builder() 49 | .addInterceptor(chuckInterceptor) 50 | .addInterceptor(httpLoggingInterceptor).build() 51 | 52 | @Singleton 53 | @Provides 54 | fun provideGsonConverterFactory(): GsonConverterFactory = GsonConverterFactory.create() 55 | 56 | @Singleton 57 | @Provides 58 | fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply { 59 | setLevel(HttpLoggingInterceptor.Level.BASIC) 60 | } 61 | 62 | @Singleton 63 | @Provides 64 | fun provideChuckerInterceptor( 65 | @ApplicationContext context: Context, 66 | chuckerCollector: ChuckerCollector 67 | ): ChuckerInterceptor = 68 | ChuckerInterceptor.Builder(context).collector(chuckerCollector) 69 | .alwaysReadResponseBody(true).build() 70 | 71 | @Singleton 72 | @Provides 73 | fun provideChuckerCollector(@ApplicationContext context: Context): ChuckerCollector = ChuckerCollector( 74 | context = context, 75 | // Toggles visibility of the push notification 76 | showNotification = true, 77 | // Allows to customize the retention period of collected data 78 | retentionPeriod = RetentionManager.Period.ONE_HOUR 79 | ) 80 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/util/ForceUpdateChecker.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.util 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageManager 5 | import android.text.TextUtils 6 | import android.util.Log 7 | import com.google.firebase.remoteconfig.FirebaseRemoteConfig 8 | 9 | 10 | /** 11 | * Created by talhafaki on 10.09.2022. 12 | */ 13 | 14 | class ForceUpdateChecker( 15 | context: Context, 16 | onUpdateNeededListener: OnUpdateNeededListener? 17 | ) { 18 | private val onUpdateNeededListener: OnUpdateNeededListener? 19 | private val context: Context 20 | 21 | interface OnUpdateNeededListener { 22 | fun onUpdateNeeded(updateUrl: String?) 23 | } 24 | 25 | init { 26 | this.context = context 27 | this.onUpdateNeededListener = onUpdateNeededListener 28 | } 29 | 30 | fun check() { 31 | val remoteConfig = FirebaseRemoteConfig.getInstance() 32 | if (remoteConfig.getBoolean(KEY_UPDATE_REQUIRED)) { 33 | val currentVersion = remoteConfig.getString(KEY_CURRENT_VERSION) 34 | val appVersion = getAppVersion(context) 35 | val updateUrl = remoteConfig.getString(KEY_UPDATE_URL) 36 | if (!TextUtils.equals(currentVersion, appVersion) 37 | && onUpdateNeededListener != null 38 | ) { 39 | onUpdateNeededListener.onUpdateNeeded(updateUrl) 40 | } 41 | } 42 | } 43 | 44 | private fun getAppVersion(context: Context): String { 45 | var result = "" 46 | try { 47 | result = context.packageManager 48 | .getPackageInfo(context.packageName, 0).versionName 49 | result = result.replace("[a-zA-Z]|-".toRegex(), "") 50 | } catch (e: PackageManager.NameNotFoundException) { 51 | Log.e(TAG, e.message.orEmpty()) 52 | } 53 | return result 54 | } 55 | 56 | class Builder(context: Context) { 57 | private val context: Context 58 | private var onUpdateNeededListener: OnUpdateNeededListener? = null 59 | 60 | init { 61 | this.context = context 62 | } 63 | 64 | fun onUpdateNeeded(onUpdateNeededListener: OnUpdateNeededListener?): Builder { 65 | this.onUpdateNeededListener = onUpdateNeededListener 66 | return this 67 | } 68 | 69 | private fun build(): ForceUpdateChecker { 70 | return ForceUpdateChecker(context, onUpdateNeededListener) 71 | } 72 | 73 | fun check(): ForceUpdateChecker { 74 | val forceUpdateChecker = build() 75 | forceUpdateChecker.check() 76 | return forceUpdateChecker 77 | } 78 | } 79 | 80 | companion object { 81 | private val TAG = ForceUpdateChecker::class.java.simpleName 82 | const val KEY_UPDATE_REQUIRED = "force_update_required" 83 | const val KEY_CURRENT_VERSION = "force_update_current_version" 84 | const val KEY_UPDATE_URL = "force_update_store_url" 85 | fun with(context: Context): Builder { 86 | return Builder(context) 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/component/AdsView.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.component 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.os.Handler 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.viewinterop.AndroidView 10 | import com.google.android.gms.ads.* 11 | import com.google.android.gms.ads.interstitial.InterstitialAd 12 | import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback 13 | import com.loftymr.whichone.BuildConfig 14 | import com.loftymr.whichone.R 15 | 16 | /** 17 | * Created by talhafaki on 13.09.2022. 18 | */ 19 | 20 | private var mInterstitialAd: InterstitialAd? = null 21 | 22 | @Composable 23 | fun AdvertView(modifier: Modifier = Modifier) { 24 | AndroidView( 25 | modifier = modifier.fillMaxWidth(), 26 | factory = { context -> 27 | AdView(context).apply { 28 | val bannerID = 29 | if (BuildConfig.DEBUG) context.getString(R.string.ad_test_id_banner) else context.getString( 30 | R.string.ad_id_banner 31 | ) 32 | setAdSize(AdSize.BANNER) 33 | adUnitId = bannerID 34 | loadAd(AdRequest.Builder().build()) 35 | } 36 | }, update = { 37 | it.apply { 38 | loadAd(AdRequest.Builder().build()) 39 | } 40 | } 41 | ) 42 | } 43 | 44 | fun loadInterstitial(context: Context, isFinished: (Boolean) -> Unit) { 45 | InterstitialAd.load( 46 | context, 47 | context.getString(if (BuildConfig.DEBUG) R.string.ad_test_id_interstitial else R.string.ad_id_interstitial), 48 | AdRequest.Builder().build(), 49 | object : InterstitialAdLoadCallback() { 50 | override fun onAdFailedToLoad(adError: LoadAdError) { 51 | mInterstitialAd = null 52 | isFinished.invoke(true) 53 | } 54 | 55 | override fun onAdLoaded(interstitialAd: InterstitialAd) { 56 | mInterstitialAd = interstitialAd 57 | showInterstitial(context, isFinished) 58 | } 59 | } 60 | ) 61 | Handler().postDelayed({ 62 | if (mInterstitialAd == null) { 63 | isFinished.invoke(true) 64 | } 65 | }, 3500) 66 | } 67 | 68 | fun showInterstitial(context: Context, isFinished: (Boolean) -> Unit) { 69 | if (mInterstitialAd != null) { 70 | mInterstitialAd!!.show(context as Activity) 71 | 72 | addInterstitialCallbacks(isFinished = isFinished) 73 | } else { 74 | isFinished.invoke(true) 75 | } 76 | } 77 | 78 | // add the interstitial ad callbacks 79 | fun addInterstitialCallbacks(isFinished: (Boolean) -> Unit) { 80 | mInterstitialAd?.fullScreenContentCallback = object : FullScreenContentCallback() { 81 | override fun onAdFailedToShowFullScreenContent(p0: AdError) { 82 | isFinished.invoke(true) 83 | } 84 | 85 | override fun onAdShowedFullScreenContent() { 86 | mInterstitialAd = null 87 | isFinished.invoke(false) 88 | } 89 | 90 | override fun onAdDismissedFullScreenContent() { 91 | isFinished.invoke(true) 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/result/ResultScreen.kt: -------------------------------------------------------------------------------- 1 | package com.loftymr.whichone.feature.screen.result 2 | 3 | import androidx.activity.compose.BackHandler 4 | import androidx.compose.foundation.Image 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material3.Card 8 | import androidx.compose.material.MaterialTheme 9 | import androidx.compose.material.Text 10 | import androidx.compose.material3.CardDefaults 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.layout.ContentScale 15 | import androidx.compose.ui.res.stringResource 16 | import androidx.compose.ui.text.style.TextAlign 17 | import androidx.compose.ui.unit.dp 18 | import androidx.compose.ui.unit.sp 19 | import coil.compose.rememberImagePainter 20 | import com.loftymr.whichone.R 21 | import com.loftymr.whichone.feature.component.WhichOneButton 22 | import com.loftymr.whichone.feature.component.WhichOneTemplate 23 | import com.loftymr.whichone.feature.theme.SurveyColor 24 | 25 | /** 26 | * Created by talhafaki on 10.09.2022. 27 | */ 28 | 29 | @Composable 30 | fun ResultScreen( 31 | title: String = "", 32 | character: String = "", 33 | desc: String = "", 34 | navigateToSurvey: () -> Unit 35 | ) { 36 | WhichOneTemplate( 37 | topBar = {}, 38 | ) { 39 | ResultContent( 40 | title = title, 41 | character = character, 42 | desc = desc, 43 | navigateToSurvey = { 44 | navigateToSurvey.invoke() 45 | } 46 | ) 47 | } 48 | BackHandler { 49 | navigateToSurvey.invoke() 50 | } 51 | } 52 | 53 | @Composable 54 | fun ResultContent( 55 | modifier: Modifier = Modifier, 56 | title: String = "", 57 | desc: String = "", 58 | character: String = "", 59 | navigateToSurvey: () -> Unit 60 | ) { 61 | Column( 62 | modifier = modifier 63 | .fillMaxSize() 64 | ) { 65 | Card( 66 | modifier = Modifier 67 | .fillMaxWidth() 68 | .height(400.dp) 69 | .padding(16.dp), 70 | elevation = CardDefaults.cardElevation(10.dp), 71 | shape = RoundedCornerShape(8.dp) 72 | ) { 73 | Image( 74 | painter = rememberImagePainter(character), 75 | contentDescription = "", 76 | contentScale = ContentScale.Crop, 77 | modifier = Modifier.fillMaxSize() 78 | ) 79 | } 80 | 81 | Text( 82 | text = title, 83 | modifier = Modifier 84 | .fillMaxWidth() 85 | .wrapContentWidth(Alignment.CenterHorizontally) 86 | .padding(horizontal = 16.dp, vertical = 8.dp), 87 | color = SurveyColor.White, 88 | style = MaterialTheme.typography.h1.copy(color = SurveyColor.White, fontSize = 24.sp) 89 | ) 90 | 91 | Spacer(modifier = Modifier.height(8.dp)) 92 | Text( 93 | text = desc, 94 | modifier = Modifier 95 | .fillMaxWidth() 96 | .wrapContentWidth(Alignment.CenterHorizontally) 97 | .padding(horizontal = 16.dp, vertical = 8.dp), 98 | color = SurveyColor.White, 99 | style = MaterialTheme.typography.body1.copy( 100 | color = SurveyColor.White.copy(0.5f), 101 | fontSize = 16.sp, 102 | textAlign = TextAlign.Center 103 | ) 104 | ) 105 | Box( 106 | modifier = Modifier 107 | .fillMaxSize() 108 | .padding(bottom = 24.dp), 109 | contentAlignment = Alignment.BottomCenter 110 | ) { 111 | WhichOneButton(buttonText = stringResource(id = R.string.again)) { 112 | navigateToSurvey.invoke() 113 | } 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WhichOne 2 | A mobile application where you can learn which TV series/movie character it is after answering 10 questions. 3 | 4 | 5 | ## GET IT ON 6 | [![Play Store](https://img.shields.io/badge/Google_Play-414141?style=for-the-badge&logo=google-play&logoColor=white)](https://play.google.com/store/apps/details?id=com.loftymr.whichone&hl=tr&gl=US) 7 | 8 | ## TECH STACK 9 | - [Compose](https://developer.android.com/jetpack/compose) 10 | 11 | - [Material](https://developer.android.com/jetpack/androidx/releases/compose-material) - Build Jetpack Compose UIs with ready to use Material Design Components. 12 | - [Foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) - Write Jetpack Compose applications with ready to use building blocks and extend foundation to build your own design system pieces. 13 | - [UI](https://developer.android.com/jetpack/androidx/releases/compose-ui) - Fundamental components of compose UI needed to interact with the device, including layout, drawing, and input. 14 | - [Lifecycle-ViewModel](https://developer.android.com/jetpack/androidx/releases/lifecycle) - Perform actions in response to a change in the lifecycle status of another component, such as activities and fragments. 15 | - [Lottie](https://github.com/airbnb/lottie/blob/master/android-compose.md) - Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as json with Bodymovin and renders them natively on mobile! 16 | - [Coil](https://coil-kt.github.io/coil/compose/) - An image loading library for Android backed by Kotlin Coroutines. 17 | 18 | - [Jetpack](https://developer.android.com/jetpack) 19 | 20 | - [AndroidX](https://developer.android.com/jetpack/androidx) - Major improvement to the original Android [Support Library](https://developer.android.com/topic/libraries/support-library/index), which is no longer maintained. 21 | - [Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle) - Perform actions in response to a change in the lifecycle status of another component, such as activities and fragments. 22 | - [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations. 23 | 24 | - Others 25 | - [Retrofit](https://square.github.io/retrofit/) 26 | - [OkHttp-Logging-Interceptor](https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/README.md) - Logs HTTP request and response data. 27 | - [Flow](https://developer.android.com/kotlin/flow) - Flows are built on top of coroutines and can provide multiple values. 28 | - [Material Design](https://material.io/develop/android/docs/getting-started/) - Build awesome beautiful UIs. 29 | - [Coroutines](https://github.com/Kotlin/kotlinx.coroutines) - Library Support for coroutines,provides runBlocking coroutine builder used in tests. 30 | - [Gson](https://github.com/google/gson) - A modern JSON library for Kotlin and Java. 31 | 32 | ## CONTACT 33 | 34 | - [Linkedin](https://www.linkedin.com/in/talha-fakioglu/) 35 | - [Twitter](https://twitter.com/talhafaki) 36 | - [Medium](https://medium.com/@talhafaki) 37 | 38 | ```xml 39 | MIT License 40 | 41 | Copyright (c) 2023 Talha Fakıoğlu 42 | 43 | Permission is hereby granted, free of charge, to any person obtaining a copy 44 | of this software and associated documentation files (the "Software"), to deal 45 | in the Software without restriction, including without limitation the rights 46 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 47 | copies of the Software, and to permit persons to whom the Software is 48 | furnished to do so, subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all 51 | copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 54 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 55 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 56 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 57 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 58 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 59 | SOFTWARE. 60 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'org.jetbrains.kotlin.kapt' 5 | id 'dagger.hilt.android.plugin' 6 | id 'com.google.gms.google-services' 7 | id 'com.google.firebase.crashlytics' 8 | } 9 | 10 | android { 11 | namespace 'com.loftymr.whichone' 12 | compileSdk 32 13 | 14 | defaultConfig { 15 | applicationId "com.loftymr.whichone" 16 | minSdk 24 17 | targetSdk 32 18 | versionCode 1 19 | versionName "1.0.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | vectorDrawables { 23 | useSupportLibrary true 24 | } 25 | } 26 | 27 | buildTypes { 28 | release { 29 | shrinkResources true 30 | minifyEnabled true 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | debug { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | 39 | buildTypes.each { 40 | it.buildConfigField 'String', 'BASE_URL', BASE_URL 41 | } 42 | 43 | compileOptions { 44 | sourceCompatibility JavaVersion.VERSION_1_8 45 | targetCompatibility JavaVersion.VERSION_1_8 46 | } 47 | kotlinOptions { 48 | jvmTarget = '1.8' 49 | } 50 | buildFeatures { 51 | compose true 52 | } 53 | composeOptions { 54 | kotlinCompilerExtensionVersion '1.1.1' 55 | } 56 | packagingOptions { 57 | resources { 58 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 59 | } 60 | } 61 | } 62 | 63 | dependencies { 64 | 65 | // Hilt 66 | implementation "com.google.dagger:hilt-android:2.41" 67 | implementation 'com.google.firebase:firebase-config-ktx:21.1.0' 68 | kapt "com.google.dagger:hilt-compiler:2.41" 69 | 70 | // Kotlin Coroutines 71 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1" 72 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" 73 | 74 | // Networking 75 | implementation "com.squareup.retrofit2:retrofit:2.9.0" 76 | implementation "com.squareup.retrofit2:converter-gson:2.9.0" 77 | implementation "com.squareup.retrofit2:converter-scalars:2.9.0" 78 | implementation "com.squareup.okhttp3:logging-interceptor:4.9.1" 79 | 80 | // AndroidX 81 | implementation "androidx.core:core-ktx:1.8.0" 82 | implementation "androidx.annotation:annotation:1.4.0" 83 | implementation "androidx.appcompat:appcompat:1.5.1" 84 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" 85 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" 86 | implementation "androidx.compose.ui:ui:1.2.1" 87 | implementation "androidx.compose.material:material:1.2.1" 88 | implementation "androidx.compose.ui:ui-tooling:1.2.1" 89 | implementation "androidx.activity:activity-compose:1.5.1" 90 | implementation "androidx.navigation:navigation-compose:2.4.0-alpha07" 91 | implementation "androidx.hilt:hilt-navigation-compose:1.0.0" 92 | 93 | // Splash 94 | implementation "androidx.core:core-splashscreen:1.0.0" 95 | 96 | // Compose Material 3 97 | implementation "androidx.compose.material3:material3:1.0.0-alpha15" 98 | 99 | // Coil 100 | implementation "io.coil-kt:coil-compose:1.4.0" 101 | 102 | // Lottie 103 | implementation "com.airbnb.android:lottie-compose:4.2.2" 104 | 105 | // Google Ads 106 | implementation "com.google.android.gms:play-services-ads:21.2.0" 107 | 108 | // Firebase 109 | implementation 'com.google.firebase:firebase-crashlytics-ktx:18.2.13' 110 | implementation 'com.google.firebase:firebase-analytics-ktx:21.1.1' 111 | implementation "com.google.firebase:firebase-ads:21.2.0" 112 | implementation "com.google.firebase:firebase-core:21.1.1" 113 | 114 | // Chucker 115 | debugImplementation "com.github.chuckerteam.chucker:library:3.5.2" 116 | releaseImplementation "com.github.chuckerteam.chucker:library-no-op:3.5.2" 117 | 118 | // Testing 119 | testImplementation 'junit:junit:4.13.2' 120 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 121 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 122 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" 123 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" 124 | debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" 125 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/which_one_logo.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/loftymr/whichone/feature/screen/survey/SurveyScreen.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalAnimationApi::class) 2 | 3 | package com.loftymr.whichone.feature.screen.survey 4 | 5 | import androidx.compose.animation.AnimatedContent 6 | import androidx.compose.animation.AnimatedContentScope 7 | import androidx.compose.animation.ExperimentalAnimationApi 8 | import androidx.compose.animation.core.TweenSpec 9 | import androidx.compose.animation.core.tween 10 | import androidx.compose.animation.with 11 | import androidx.compose.foundation.background 12 | import androidx.compose.foundation.layout.* 13 | import androidx.compose.foundation.rememberScrollState 14 | import androidx.compose.foundation.shape.RoundedCornerShape 15 | import androidx.compose.foundation.verticalScroll 16 | import androidx.compose.material.MaterialTheme 17 | import androidx.compose.material.Text 18 | import androidx.compose.material3.Card 19 | import androidx.compose.material3.CardDefaults 20 | import androidx.compose.runtime.* 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.platform.LocalContext 24 | import androidx.compose.ui.res.stringResource 25 | import androidx.compose.ui.text.style.TextAlign 26 | import androidx.compose.ui.unit.IntOffset 27 | import androidx.compose.ui.unit.dp 28 | import androidx.compose.ui.unit.sp 29 | import androidx.hilt.navigation.compose.hiltViewModel 30 | import com.loftymr.whichone.R 31 | import com.loftymr.whichone.data.model.RingsOfThePowerResponse 32 | import com.loftymr.whichone.data.model.SrcSet 33 | import com.loftymr.whichone.feature.component.* 34 | import com.loftymr.whichone.feature.theme.SurveyColor 35 | 36 | /** 37 | * Created by talhafaki on 9.09.2022. 38 | */ 39 | 40 | @Composable 41 | fun SurveyScreen( 42 | viewModel: SurveyViewModel = hiltViewModel(), 43 | navigateToResult: (String, String, String) -> Unit 44 | ) { 45 | val viewState = viewModel.uiState.collectAsState().value 46 | 47 | when { 48 | viewState.isLoading -> { 49 | CircularProgressAnimated() 50 | } 51 | viewState.isError -> { 52 | ErrorView( 53 | clickToRetry = { 54 | viewModel.getRingsOfThePower() 55 | } 56 | ) 57 | } 58 | viewState.data != null -> { 59 | WhichOneTemplate( 60 | topBar = { 61 | WhichOneTopBar( 62 | title = stringResource(id = R.string.survey_title), 63 | backButtonEnabled = false 64 | ) 65 | }, 66 | content = { 67 | SurveyContent( 68 | data = viewState.data, 69 | showResult = { title, srcSet, desc -> 70 | navigateToResult.invoke(title, srcSet?.nineHundred.orEmpty(), desc) 71 | }, 72 | adMob = { 73 | viewModel.showLoading(it) 74 | } 75 | ) 76 | } 77 | ) 78 | } 79 | else -> { 80 | ErrorView { 81 | viewModel.getRingsOfThePower() 82 | } 83 | } 84 | } 85 | } 86 | 87 | @Composable 88 | fun SurveyContent( 89 | data: RingsOfThePowerResponse, 90 | showResult: (String, SrcSet?, String) -> Unit, 91 | adMob: (Boolean) -> Unit 92 | ) { 93 | val context = LocalContext.current 94 | var questionIndex by remember { 95 | mutableStateOf(0) 96 | } 97 | val questionState = remember(questionIndex) { 98 | questionIndex 99 | } 100 | Card( 101 | modifier = Modifier 102 | .fillMaxSize() 103 | .padding(start = 8.dp, end = 8.dp, top = 36.dp, bottom = 8.dp), 104 | shape = RoundedCornerShape(10.dp), 105 | colors = CardDefaults.cardColors(SurveyColor.Alabaster), 106 | elevation = CardDefaults.cardElevation(10.dp) 107 | ) { 108 | Column( 109 | modifier = Modifier 110 | .fillMaxSize() 111 | .verticalScroll(state = rememberScrollState()) 112 | ) { 113 | Head( 114 | imageSource = data.backgroundPictures?.get(questionIndex).orEmpty(), 115 | numberOfSteps = data.questions?.lastIndex ?: 0, 116 | currentStep = questionIndex 117 | ) 118 | 119 | AnimatedContent( 120 | targetState = questionState, 121 | transitionSpec = { 122 | val animationSpec: TweenSpec = tween(150) 123 | val direction = 124 | if (targetState > initialState) { 125 | AnimatedContentScope.SlideDirection.Left 126 | } else { 127 | AnimatedContentScope.SlideDirection.Right 128 | } 129 | slideIntoContainer( 130 | towards = direction, 131 | animationSpec = animationSpec 132 | ) with 133 | slideOutOfContainer( 134 | towards = direction, 135 | animationSpec = animationSpec 136 | ) 137 | } 138 | ) { 139 | Column { 140 | Text( 141 | text = data.questions?.get(questionIndex)?.questionText.orEmpty(), 142 | modifier = Modifier 143 | .fillMaxWidth() 144 | .wrapContentWidth(Alignment.CenterHorizontally) 145 | .padding(horizontal = 16.dp, vertical = 8.dp), 146 | style = MaterialTheme.typography.h1.copy( 147 | fontSize = 18.sp, 148 | color = SurveyColor.Black, 149 | textAlign = TextAlign.Center 150 | ) 151 | ) 152 | 153 | data.questions?.get(questionIndex)?.choices?.let { choices -> 154 | choices.forEach { 155 | SelectionBox( 156 | modifier = Modifier 157 | .padding(horizontal = 12.dp), 158 | text = it, 159 | isSelected = { 160 | if (questionIndex < data.questions.lastIndex) { 161 | questionIndex++ 162 | } else { 163 | adMob.invoke(true) 164 | loadInterstitial( 165 | context = context, 166 | isFinished = { 167 | adMob.invoke(false) 168 | showResult.invoke( 169 | data.character?.title.orEmpty(), 170 | data.character?.srcSet, 171 | data.character?.description.orEmpty() 172 | ) 173 | } 174 | ) 175 | } 176 | } 177 | ) 178 | } 179 | Spacer(modifier = Modifier.height(8.dp)) 180 | } 181 | } 182 | } 183 | 184 | Box( 185 | modifier = Modifier 186 | .fillMaxSize() 187 | .padding(bottom = 2.dp) 188 | .weight(1f), 189 | contentAlignment = Alignment.BottomCenter 190 | ) { 191 | AdvertView(modifier = Modifier.wrapContentSize()) 192 | } 193 | } 194 | } 195 | } 196 | 197 | @Composable 198 | fun ErrorView(clickToRetry: () -> Unit) { 199 | Column( 200 | modifier = Modifier 201 | .fillMaxSize() 202 | .background(color = SurveyColor.SolidBlue), 203 | verticalArrangement = Arrangement.Top 204 | ) { 205 | WhichOneAnim(rawResId = R.raw.opps_anim) 206 | 207 | Text( 208 | text = stringResource(id = R.string.something_went_wrong), 209 | modifier = Modifier 210 | .fillMaxWidth() 211 | .wrapContentWidth(Alignment.CenterHorizontally) 212 | .padding(horizontal = 16.dp, vertical = 4.dp), 213 | style = MaterialTheme.typography.h1.copy( 214 | fontSize = 24.sp, 215 | color = SurveyColor.White 216 | ) 217 | ) 218 | 219 | Spacer(modifier = Modifier.height(16.dp)) 220 | 221 | WhichOneButton(buttonText = stringResource(id = R.string.retry)) { 222 | clickToRetry.invoke() 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /app/src/main/res/raw/force_update_store_anim.json: -------------------------------------------------------------------------------- 1 | {"v":"5.4.3","fr":25,"ip":0,"op":80,"w":1920,"h":1080,"nm":"PlayStore","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector_PlayStore contornos","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[683,384,0],"ix":1},"s":{"a":0,"k":[124.816,124.816,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[55.112,-55.111]],"o":[[0,0],[-55.112,55.112],[0,0]],"v":[[-82.667,-165.335],[82.667,0.001],[-82.667,165.335]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.161000001197,0.670999983245,0.885999971278,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[579.667,368.082],"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":"Transformar"}],"nm":"Grupo 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.632,-3.55],[0,0],[0,0],[76.165,44.778],[1.816,-0.557]],"o":[[0,0],[0,0],[-76.165,-44.779],[-0.935,-0.262],[-2.995,0.918]],"v":[[-119.368,-178.956],[-119.368,186.829],[119.368,-51.936],[-109.126,-186.272],[-113.515,-186.272]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.736999990426,0.395999983245,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[616.368,346.588],"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":"Transformar"}],"nm":"Grupo 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.14,2.43],[0.085,0.898]],"o":[[0,0],[0,0],[-3.961,3.674],[-2.437,-1.885],[0,0]],"v":[[-124.452,-195.081],[124.452,53.808],[-109.095,190.949],[-121.526,192.651],[-124.452,186.798]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.277999997606,0.33300000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[621.452,397.829],"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":"Transformar"}],"nm":"Grupo 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.424,2.011],[0,0],[0,-4.022],[0,0],[-3.467,2.036],[0,0]],"o":[[0,0],[-3.467,-2.036],[0,0],[0,4.021],[0,0],[3.424,-2.01]],"v":[[183.214,-4.486],[-178.802,-217.064],[-186.638,-212.578],[-186.638,212.579],[-178.802,217.065],[183.214,4.486]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.984000052658,0.689999988032,0.231000010173,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[683.638,376.13],"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":"Transformar"}],"nm":"Grupo 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector_Android contornos","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]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[20]},{"t":25}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[864,532,0],"e":[948,404,0],"to":[14,-21.3333339691162,0],"ti":[-14,21.3333339691162,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":25,"s":[948,404,0],"e":[948,404,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":59,"s":[948,404,0],"e":[862,541,0],"to":[-14.3333330154419,22.8333339691162,0],"ti":[14.3333330154419,-22.8333339691162,0]},{"t":79}],"ix":2},"a":{"a":0,"k":[683,384,0],"ix":1},"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]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[60,60,100],"e":[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]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":25,"s":[100,100,100],"e":[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]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":59,"s":[100,100,100],"e":[57,57,100]},{"t":79}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[10.77,0],[0,0],[0,10.77],[0,0],[-10.769,0],[0,0],[0,-10.769],[0,0]],"o":[[0,0],[-10.769,0],[0,0],[0,-10.769],[0,0],[10.77,0],[0,0],[0,10.77]],"v":[[3.022,74.496],[-3.022,74.496],[-22.522,54.996],[-22.522,-54.995],[-3.022,-74.495],[3.022,-74.495],[22.522,-54.995],[22.522,54.996]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[848.954,342.374],"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":"Transformar"}],"nm":"Grupo 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[10.77,0],[0,0],[0,10.77],[0,0],[-10.77,0],[0,0],[0,-10.769],[0,0]],"o":[[0,0],[-10.77,0],[0,0],[0,-10.769],[0,0],[10.77,0],[0,0],[0,10.77]],"v":[[3.022,74.496],[-3.021,74.496],[-22.521,54.996],[-22.521,-54.995],[-3.021,-74.495],[3.022,-74.495],[22.522,-54.995],[22.522,54.996]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[562.648,291.95],"ix":2},"a":{"a":0,"k":[0.126,-50.424],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":21,"s":[0],"e":[174]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[174],"e":[130]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[130],"e":[174]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":41,"s":[174],"e":[130]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":46,"s":[130],"e":[174]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":51,"s":[174],"e":[130]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":55,"s":[130],"e":[0]},{"t":64}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"BrazoAnimado","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[10.77,0],[0,0],[0,10.77],[0,0],[-10.769,0],[0,0],[0,-10.769],[0,0]],"o":[[0,0],[-10.769,0],[0,0],[0,-10.769],[0,0],[10.77,0],[0,0],[0,10.77]],"v":[[3.022,53.128],[-3.022,53.128],[-22.522,33.628],[-22.522,-33.629],[-3.022,-53.129],[3.022,-53.129],[22.522,-33.629],[22.522,33.628]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[751.936,498.872],"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":"Transformar"}],"nm":"Grupo 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[10.77,0],[0,0],[0,10.77],[0,0],[-10.77,0],[0,0],[0,-10.769],[0,0]],"o":[[0,0],[-10.77,0],[0,0],[0,-10.769],[0,0],[10.77,0],[0,0],[0,10.77]],"v":[[3.021,53.128],[-3.022,53.128],[-22.522,33.628],[-22.522,-33.629],[-3.022,-53.129],[3.021,-53.129],[22.521,-33.629],[22.521,33.628]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[661.849,498.872],"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":"Transformar"}],"nm":"Grupo 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[2.466,-2.098],[0.659,0.127],[0,0],[3.541,3.978],[0.11,1.248]],"o":[[0,0],[0,0],[-0.236,1.013],[-3.836,3.265],[0,0],[-1.069,-0.098],[-2.873,-3.229],[0,0]],"v":[[-109.722,-98.65],[109.722,-98.65],[109.722,89.611],[106.257,95.384],[97.017,97.694],[-97.017,97.694],[-106.257,93.076],[-109.722,84.991]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[706.315,372.303],"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":"Transformar"}],"nm":"Grupo 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-7.468],[7.468,0],[0,7.468],[-7.468,0]],"o":[[0,7.468],[-7.468,0],[0,-7.468],[7.468,0]],"v":[[13.522,0],[0,13.522],[-13.522,0],[0,-13.522]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[751.478,213.99],"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":"Transformar"}],"nm":"Grupo 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-7.468],[7.468,0],[0,7.468],[-7.468,0]],"o":[[0,7.468],[-7.468,0],[0,-7.468],[7.468,0]],"v":[[13.522,0],[0,13.522],[-13.522,0],[0,-13.522]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[666.484,213.99],"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":"Transformar"}],"nm":"Grupo 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.239,0.715],[0,0],[-0.715,1.239],[0,0],[-1.239,-0.716],[0,0],[0.714,-1.24],[0,0]],"o":[[0,0],[-1.238,-0.716],[0,0],[0.714,-1.238],[0,0],[1.24,0.715],[0,0],[-0.715,1.239]],"v":[[-8.153,18.181],[-11.669,16.152],[-12.617,12.613],[4.616,-17.234],[8.154,-18.181],[11.669,-16.151],[12.618,-12.612],[-4.615,17.233]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[765.413,165.692],"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":"Transformar"}],"nm":"Grupo 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.239,-0.715],[0,0],[0.716,1.238],[0,0],[-1.239,0.716],[0,0],[-0.715,-1.239],[0,0]],"o":[[0,0],[-1.239,0.715],[0,0],[-0.715,-1.239],[0,0],[1.239,-0.715],[0,0],[0.716,1.24]],"v":[[11.669,16.151],[8.154,18.181],[4.615,17.233],[-12.617,-12.613],[-11.669,-16.152],[-8.154,-18.181],[-4.616,-17.233],[12.617,12.613]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[645.297,163.382],"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":"Transformar"}],"nm":"Grupo 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.661,-6.85],[0,0],[37.683,27.006],[9.772,-0.321],[28.5,-21.398]],"o":[[0,0],[-0.783,-7.006],[-27.37,-19.615],[-9.089,-0.4],[-36.274,27.235]],"v":[[-109.722,48.235],[109.722,48.235],[65.833,-27.993],[0,-47.626],[-68.143,-26.837]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.522000002394,0.783999992819,0.030999998953,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[706.315,213.868],"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":"Transformar"}],"nm":"Grupo 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /app/src/main/res/raw/opps_anim.json: -------------------------------------------------------------------------------- 1 | {"v":"5.5.10","fr":30,"ip":0,"op":60,"w":512,"h":512,"nm":"Try Again","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Layer 3 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,314.669,0],"ix":2},"a":{"a":0,"k":[256,314.669,0],"ix":1},"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":30,"s":[0,0,100]},{"t":38.5,"s":[100,100,100]}],"ix":6,"x":"var $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n amp = $bm_div(effect('Amplitude')('Slider'), 100);\n freq = effect('Frequency')('Slider');\n decay = effect('Decay')('Slider');\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":5,"nm":"Amplitude","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":3,"ix":1}}]},{"ty":5,"nm":"Frequency","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":2.5,"ix":1}}]},{"ty":5,"nm":"Decay","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":5,"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.753,0.004],[-0.122,4.039],[-4.559,-0.014],[0.031,-3.637]],"o":[[-4.836,-0.004],[0.112,-3.729],[4.237,0.014],[-0.034,3.987]],"v":[[-0.044,7.47],[-8.87,-0.097],[0.391,-7.46],[8.96,-0.024]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[455.324,339.988],"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-6.436,-0.475],[2.258,-12.861],[3.659,0.266]],"o":[[6.027,0.444],[-2.227,12.672],[-4.012,-0.293],[0,0]],"v":[[-9.022,-19.59],[9.021,-18.26],[2.372,19.59],[-9.022,18.758]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[459.752,306.977],"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":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.028,0.81],[-0.834,-4.509],[6.31,-0.519],[-0.636,-14.316],[6.15,-1.14],[0,0],[4.162,-0.329],[0.004,0.286],[-11.661,0.875]],"o":[[0.912,4.926],[-5.497,0.452],[0.605,13.641],[-5.356,0.993],[0,0],[-4.852,0.385],[-0.375,-0.809],[-0.181,-12.017],[10.592,-0.795]],"v":[[20.692,-29.766],[23.185,-16.298],[6.285,-14.906],[8.127,26.59],[-9,29.766],[-9,-14.174],[-22.383,-13.116],[-23.004,-14.492],[-11.523,-27.397]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[66.269,311.646],"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":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.706],[-1.663,1.623],[0.496,1.97],[3.076,-0.87],[0.269,-1.429]],"o":[[2.651,-1.343],[2.159,-2.108],[-1.003,-3.985],[-1.352,0.381],[-0.508,2.711]],"v":[[-7.68,-4.21],[-0.788,-7.857],[4.157,-15.255],[-4.336,-17.194],[-7.534,-13.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[3.472,-3.02],[-5.454,-6.342],[4.797,-3.665],[6.299,8.301],[0,0],[5.433,-1.146],[-0.11,7.114],[-0.166,10.313],[-1.001,0.116],[-8.922,0.369],[-2.147,-5.408],[3.712,-4.241]],"o":[[4.958,5.764],[-4.381,3.346],[-5.724,-7.544],[0,0],[-4.761,1.004],[0.411,-7.745],[0.159,-10.314],[0.021,-1.283],[8.875,-1.025],[5.442,-0.226],[2.138,5.389],[-2.48,2.834]],"v":[[7.633,0.966],[23.012,18.852],[9.557,29.133],[-8.081,5.886],[-8.081,26.409],[-23.012,29.556],[-21.9,7.591],[-21.855,-23.356],[-19.348,-27.01],[7.355,-29.331],[19.891,-21.973],[16.766,-7.106]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[115.047,311.635],"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":"Group 4","np":5,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.871,17.784],[5.908,-0.764],[0,0],[0.739,-0.096],[3.449,11.455],[6.067,-0.814],[0,0],[-4.981,0.987],[0,0],[0,0]],"o":[[0.888,-18.12],[-5.382,0.696],[0,0],[-0.738,0.098],[-3.279,-10.893],[-5.175,0.695],[0,0],[4.798,-0.952],[0,0],[0,0],[0,0]],"v":[[20.123,23.997],[22.745,-29.516],[6.236,-27.382],[6.236,3.021],[4.02,3.313],[-5.987,-29.929],[-22.745,-27.68],[-22.745,29.929],[-8.204,27.046],[-8.204,7.641],[3.349,25.639]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[418.762,316.126],"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":"Group 5","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-5.243,0.656],[0.802,-18.23],[5.034,-0.983]],"o":[[5.936,-0.743],[-0.782,17.766],[-4.213,0.823],[0,0]],"v":[[-8.13,-26.132],[8.13,-28.167],[5.77,25.453],[-8.13,28.167]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[380.834,315.719],"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":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.466,-18.783],[5.647,0.48],[-1.618,4.746],[0.821,1.792],[5.078,10.452],[-5.912,2.107],[-1.72,-6.847],[0,0],[-2.174,5.637],[-2.879,-0.605],[-4.358,-0.541]],"o":[[-4.049,-0.345],[2.125,-4.993],[0.648,-1.902],[-4.614,-10.065],[6.131,-2.184],[1.839,7.32],[0,0],[2.278,-5.597],[0.89,-2.308],[4.166,0.874],[-8.402,18.641]],"v":[[-0.874,29.301],[-15.344,28.071],[-9.396,13.788],[-9.558,7.449],[-24.206,-22.977],[-6.452,-29.301],[-1.173,-8.289],[-0.14,-8.303],[6.659,-25.108],[11.388,-28.022],[24.206,-26.34]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[163.107,312.134],"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":"Group 7","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.493,7.721],[0.247,0.019],[1.507,-8.076],[0,0]],"o":[[-0.247,-0.018],[-1.441,7.725],[0,0],[-1.552,-8.029]],"v":[[0.233,-14.082],[-0.508,-14.136],[-4.897,9.389],[4.77,9.389]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-4.084,0.381],[-4.69,-18.437],[5.206,-1.013],[15.003,-2.118],[0.857,-3.483],[5.399,1.143],[-3.553,12.559],[-0.467,2.106],[-8.856,0.845]],"o":[[4.712,18.523],[-5.001,0.974],[-3.946,-11.582],[-0.703,2.861],[-5.735,-1.214],[3.72,-13.065],[0.587,-2.074],[1.963,-8.853],[4.425,-0.423]],"v":[[11.087,-29.441],[25.059,25.484],[9.571,28.498],[-6.201,20.094],[-8.5,29.441],[-25.058,25.935],[-14.201,-12.241],[-12.66,-18.527],[-1.849,-28.231]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233.78,313.672],"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":"Group 8","np":5,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.463,7.392],[0.259,0.019],[1.454,-7.772],[0,0]],"o":[[-0.26,-0.019],[-1.402,7.491],[0,0],[-1.566,-7.915]],"v":[[0.099,-13.798],[-0.681,-13.855],[-4.937,8.899],[4.591,8.899]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-5.168,0.539],[-4.715,-18.566],[4.573,-0.874],[6.94,0.384],[-0.232,-8.456],[5.08,1.07],[-3.704,15.791],[-4.484,0.227]],"o":[[4.694,18.484],[-4.999,0.956],[-3.955,-2.587],[-7.623,-0.421],[-6.133,-1.293],[4.315,-16.024],[1.172,-4.995],[5.121,-0.258]],"v":[[10.948,-29.52],[24.923,25.507],[10.078,28.345],[3.087,18.877],[-8.273,29.521],[-24.922,26.012],[-12.359,-21.399],[-4.437,-27.979]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[342.92,315.31],"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":"Group 9","np":5,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.441,-5.359],[2.73,-2.518],[5.699,-5.019],[-2.501,-6.968],[-4.57,-0.443],[-3.605,4.695],[-0.91,1.971],[5.743,-0.82],[-0.241,3.367],[-9.971,1.321],[8.273,-7.117],[8.792,10.465],[-2.69,9.66],[-13.698,-1.616]],"o":[[-3.136,2.893],[-8.362,-4.234],[-6.47,5.697],[1.686,4.694],[5.886,0.57],[0.87,-1.133],[-5.48,0.782],[0.242,-3.398],[9.877,-1.308],[1.381,11.96],[-11.281,9.705],[-6.88,-8.189],[4.205,-15.101],[6.154,0.725]],"v":[[21.756,-21.925],[13.111,-13.949],[-5.728,-12.862],[-12.673,9.718],[-2.829,16.945],[11.538,10.561],[13.714,5.959],[-2.391,8.257],[-1.686,-1.645],[27.51,-5.513],[15.828,22.414],[-21.992,20.823],[-26.201,-6.553],[5.867,-30.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.054999998504,0,0.451000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"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":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[288.577,315.228],"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":"Group 10","np":3,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":450,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 2 Outlines","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":15,"s":[133]},{"t":23.5,"s":[0]}],"ix":10,"x":"var $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n amp = $bm_div(effect('Amplitude')('Slider'), 100);\n freq = effect('Frequency')('Slider');\n decay = effect('Decay')('Slider');\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[256.839,378.218,0],"ix":2},"a":{"a":0,"k":[256.839,378.218,0],"ix":1},"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":15,"s":[0,0,100]},{"t":23.5,"s":[100,100,100]}],"ix":6,"x":"var $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n amp = $bm_div(effect('Amplitude')('Slider'), 100);\n freq = effect('Frequency')('Slider');\n decay = effect('Decay')('Slider');\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":5,"nm":"Amplitude","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":3,"ix":1}}]},{"ty":5,"nm":"Frequency","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":2.5,"ix":1}}]},{"ty":5,"nm":"Decay","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":5,"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.223,1.562],[-1.561,0],[-0.446,-0.446],[0,0],[0.224,-1.784],[1.562,0],[1.339,0.224],[0,1.562]],"o":[[2.9,-0.446],[1.786,0],[0,0],[0,2.231],[-2.008,0.224],[-1.562,0],[0.223,-1.561],[0,-1.562]],"v":[[-4.853,9.874],[1.84,9.204],[5.188,9.874],[5.188,13.221],[4.853,19.245],[-0.502,19.58],[-4.853,19.245],[-4.519,14.56]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.339,16.067],[-1.561,0],[-1.339,-0.446],[0.224,-4.463],[0.223,-4.463],[1.338,0],[0.892,-0.446]],"o":[[2.231,-0.446],[1.786,0],[0,2.902],[-0.446,5.578],[-1.339,-0.446],[-1.116,0],[0,-1.339]],"v":[[-5.188,-18.911],[0.501,-19.58],[5.188,-18.911],[4.853,-7.865],[3.849,7.196],[-0.167,6.526],[-3.18,7.196]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[330.372,254.271],"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":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.115,4.648],[-5.615,0.056],[-0.892,-0.521],[-1.004,0],[-0.763,0.223],[-0.558,0.855],[0,0.745],[0.149,0.52],[2.212,0.205],[0.576,0],[0.688,-0.037],[0.464,0],[0,0],[2.511,2.176],[0,2.566],[-0.094,0.521],[-3.124,2.176],[-3.906,0],[0,0],[-3.347,-1.859],[-1.451,-3.273],[1.859,0],[0.168,0.038],[1.154,0.409],[0.93,0],[0.408,-0.056],[0.399,-0.539],[0,-0.706],[-0.131,-0.372],[-1.507,-0.502],[-1.655,-0.148],[-0.595,-0.074],[-1.636,-1.227],[0,-3.569],[3.328,-2.511],[4.574,-0.241],[0.465,0],[3.254,1.654]],"o":[[2.882,-0.539],[0.744,1.804],[1.097,0.335],[0.818,0],[0.724,-0.204],[0.595,-0.91],[0,-0.463],[-0.446,-1.507],[-0.428,-0.037],[-0.557,0],[-1.135,0.037],[0,0],[-3.311,-0.112],[-2.287,-1.989],[0,-0.483],[0.651,-4.035],[2.752,-1.914],[0,0],[3.868,0.037],[3.533,1.971],[-5.913,1.004],[-0.354,0],[-0.502,-0.873],[-0.874,-0.279],[-0.39,0],[-0.781,0.279],[-0.401,0.539],[0,0.335],[0.539,1.543],[0.892,0.168],[2.175,0.186],[3.384,0.409],[2.213,1.655],[0,4.11],[-3.013,2.287],[-0.484,0.019],[-3.96,0],[-3.979,-2.213]],"v":[[-18.13,8.242],[-5.384,7.349],[-2.929,10.836],[0.223,11.338],[2.594,11.003],[4.518,9.413],[5.411,6.93],[5.187,5.453],[1.199,2.887],[-0.308,2.831],[-2.176,2.887],[-4.574,2.943],[-4.909,2.943],[-13.64,-0.488],[-17.07,-7.322],[-16.931,-8.828],[-11.269,-18.144],[-1.283,-21.016],[-1.061,-21.016],[9.762,-18.172],[17.237,-10.306],[5.578,-8.8],[4.797,-8.856],[2.314,-10.78],[-0.391,-11.199],[-1.59,-11.115],[-3.361,-9.887],[-3.961,-8.019],[-3.766,-6.959],[-0.697,-3.891],[3.124,-3.417],[7.279,-3.026],[14.81,-0.572],[18.13,7.265],[13.137,17.195],[1.757,20.988],[0.334,21.016],[-10.488,18.534]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[305.059,253.114],"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":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.57],[0,0],[0,2.901],[3.57,0],[1.115,-0.223]],"o":[[0,0],[4.239,0],[-0.223,-2.677],[-1.115,0],[0.669,1.785]],"v":[[-3.515,-1.144],[-1.841,-1.144],[4.518,-5.495],[-1.172,-9.511],[-4.519,-9.177]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[2.008,-0.298],[0,4.016],[1.116,9.148],[0,0],[-2.678,-5.132],[0,-1.785],[2.678,-2.009],[6.47,0],[0,0],[0,0],[0.446,-4.426],[1.154,0],[0.743,-0.074]],"o":[[0.223,-4.202],[0,-9.148],[0,0],[5.132,0],[1.115,2.232],[0,2.678],[-3.57,2.677],[0,0],[0,0],[0,3.347],[-1.302,-0.149],[-0.818,0],[-2.231,0]],"v":[[-16.233,19.887],[-15.898,7.558],[-17.572,-19.887],[4.183,-19.887],[15.898,-12.189],[17.572,-6.165],[13.555,0.865],[-1.506,4.881],[-3.18,4.881],[-3.18,7.893],[-3.85,19.552],[-7.532,19.328],[-9.874,19.44]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[268.198,253.686],"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":"Group 3","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.569,0],[-0.222,3.588],[0,0],[4.017,0],[0,-4.854],[-0.223,-1.284]],"o":[[3.348,-0.261],[0,0],[0,-5.373],[-4.017,0.261],[0,1.022],[0.67,3.588]],"v":[[0.335,8.535],[5.689,2.762],[5.689,0.083],[-0.335,-7.977],[-6.359,-0.306],[-6.025,3.152]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.116,13.389],[0,0],[-3.123,3.348],[-4.463,0],[0,0],[-3.348,-3.347],[0,-5.801],[0,0],[10.487,0],[0,0]],"o":[[0,0],[0,-5.802],[2.902,-3.347],[0,0],[4.686,0.224],[3.569,3.57],[0,0],[-0.222,13.835],[0,0],[-10.71,-0.223]],"v":[[-17.07,0.167],[-17.07,-1.84],[-12.385,-15.564],[-1.339,-20.585],[-0.335,-20.585],[11.715,-15.229],[17.07,-1.172],[17.07,-0.168],[1.005,20.585],[0.669,20.585]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[231.28,253.266],"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":"Group 4","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.569,0],[-0.222,3.588],[0,0],[4.017,0],[0,-4.854],[-0.223,-1.284]],"o":[[3.348,-0.261],[0,0],[0,-5.373],[-4.017,0.261],[0,1.022],[0.67,3.588]],"v":[[0.335,8.535],[5.689,2.762],[5.689,0.083],[-0.335,-7.977],[-6.359,-0.306],[-6.025,3.152]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.116,13.389],[0,0],[-3.123,3.348],[-4.463,0],[0,0],[-3.348,-3.347],[0,-5.801],[0,0],[10.487,0],[0,0]],"o":[[0,0],[0,-5.802],[2.902,-3.347],[0,0],[4.686,0.224],[3.569,3.57],[0,0],[-0.222,13.835],[0,0],[-10.71,-0.223]],"v":[[-17.07,0.167],[-17.07,-1.84],[-12.385,-15.564],[-1.339,-20.585],[-0.335,-20.585],[11.715,-15.229],[17.07,-1.172],[17.07,-0.168],[1.005,20.585],[0.669,20.585]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[194.865,253.266],"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":"Group 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.032,0],[1.06,-1.025],[0,0],[-2.162,-2.236],[0,0],[-2.234,2.162],[0,0],[2.162,2.235],[0,0],[1.507,0.025]],"o":[[-1.471,0],[0,0],[-2.236,2.162],[0,0],[2.162,2.236],[0,0],[2.236,-2.162],[0,0],[-1.047,-1.083],[-0.033,-0.001]],"v":[[2.336,-107.939],[-1.582,-106.352],[-105.635,-5.741],[-105.769,2.234],[-5.157,106.289],[2.818,106.42],[106.871,5.81],[107.005,-2.165],[6.393,-106.219],[2.433,-107.938]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[3.294,0],[0.073,0.002],[2.348,2.428],[0,0],[-5.01,4.845],[0,0],[-3.416,-0.07],[-2.347,-2.427],[0,0],[0.057,-3.376],[2.428,-2.346],[0,0]],"o":[[-0.072,0],[-3.376,-0.056],[0,0],[-4.845,-5.011],[0,0],[2.426,-2.347],[3.376,0.057],[0,0],[2.348,2.427],[-0.057,3.375],[0,0],[-2.375,2.295]],"v":[[-1.097,115.007],[-1.315,115.004],[-10.19,111.154],[-110.801,7.1],[-110.501,-10.773],[-6.448,-111.384],[2.551,-114.936],[11.426,-111.085],[112.037,-7.031],[115.589,1.967],[111.736,10.842],[7.684,111.454]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.102000000898,0.102000000898,0.102000000898,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":[256.221,255.966],"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":"Group 6","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.315,-8.041],[0,0],[-8.04,-8.316],[0,0],[-8.315,8.041],[0,0],[8.041,8.316],[0,0]],"o":[[0,0],[-8.315,8.04],[0,0],[8.041,8.315],[0,0],[8.316,-8.04],[0,0],[-8.04,-8.315]],"v":[[-12.856,-116.331],[-115.834,-16.759],[-116.332,12.856],[-16.76,115.833],[12.855,116.331],[115.832,16.759],[116.331,-12.856],[16.758,-115.833]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.905999995213,0.776000019148,0.226999993418,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":[256.839,256],"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":"Group 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":450,"st":0,"bm":0}],"markers":[]} --------------------------------------------------------------------------------