diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/ads/build.gradle b/ads/build.gradle index e279d01..ff843f9 100644 --- a/ads/build.gradle +++ b/ads/build.gradle @@ -1,14 +1,18 @@ apply plugin: 'com.android.dynamic-feature' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' android { - compileSdkVersion 29 - + buildFeatures { + viewBinding true + dataBinding true + } + + compileSdkVersion 30 defaultConfig { - minSdkVersion 17 - targetSdkVersion 29 + minSdkVersion 16 + targetSdkVersion 30 versionCode 1 versionName "1.0" } @@ -37,7 +41,7 @@ dependencies { implementation project(':app') // https://firebase.google.com/docs/admob/android/quick-start#import_the_mobile_ads_sdk - implementation 'com.google.firebase:firebase-ads:19.2.0' + implementation 'com.google.firebase:firebase-ads:19.6.0' } repositories { mavenCentral() diff --git a/app/build.gradle b/app/build.gradle index 3db0149..53a82a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply plugin: 'com.google.firebase.firebase-perf' apply plugin: 'com.google.firebase.crashlytics' @@ -32,16 +31,19 @@ def gitCommitHash = { -> } android { - buildFeatures.viewBinding = true - compileSdkVersion 29 + buildFeatures { + viewBinding true + dataBinding true + } + compileSdkVersion 30 buildToolsVersion "29.0.3" defaultConfig { applicationId "com.javinator9889.handwashingreminder" - minSdkVersion 17 - targetSdkVersion 29 - versionCode 137 - versionName "1.2.0-${gitCommitHash}" + minSdkVersion 16 + targetSdkVersion 30 + versionCode 142 + versionName "1.2.1-${gitCommitHash}" multiDexEnabled true resConfigs "en", "es" vectorDrawables.useSupportLibrary = true @@ -111,20 +113,22 @@ dependencies { def room_version = "2.2.5" implementation fileTree(dir: 'libs', include: ['*.jar']) api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - api 'androidx.appcompat:appcompat:1.1.0' - api 'androidx.core:core-ktx:1.3.0' + api 'androidx.appcompat:appcompat:1.2.0' + api 'androidx.core:core-ktx:1.3.2' api 'androidx.legacy:legacy-support-v4:1.0.0' - api 'androidx.constraintlayout:constraintlayout:1.1.3' - testImplementation 'junit:junit:4.13' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + // https://stackoverflow.com/questions/64247956/why-does-im-getting-service-not-registered-exception-even-though-i-not-used + api 'com.google.android.gms:play-services-basement:17.5.0' // https://github.com/Javinator9889/LocaleManager api 'com.github.javinator9889:localemanager:1.1X' // https://material.io/develop/android/docs/getting-started/ - api 'com.google.android.material:material:1.1.0' + api 'com.google.android.material:material:1.2.1' // https://developers.google.com/android/guides/setup - implementation 'com.google.android.gms:play-services-location:17.0.0' + implementation 'com.google.android.gms:play-services-location:17.1.0' // https://developer.android.com/jetpack/androidx/releases/annotation api 'androidx.annotation:annotation:1.1.0' // https://developer.android.com/jetpack/androidx/releases/cardview @@ -134,32 +138,32 @@ dependencies { // https://developer.android.com/studio/build/multidex api 'androidx.multidex:multidex:2.0.1' // https://github.com/mikepenz/Android-Iconics - api 'com.mikepenz:iconics-core:5.0.3' - api 'com.mikepenz:iconics-views:5.0.3' + api 'com.mikepenz:iconics-core:5.2.1' + api 'com.mikepenz:iconics-views:5.2.1' //noinspection GradleDependency - api 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar' - api 'com.mikepenz:ionicons-typeface:2.0.1.5-kotlin@aar' + api 'com.mikepenz:google-material-typeface:3.0.1.6.original-kotlin@aar' + api 'com.mikepenz:ionicons-typeface:2.0.1.7-kotlin@aar' // https://github.com/mikepenz/AboutLibraries implementation "com.mikepenz:aboutlibraries-core:$latestAboutLibsRelease" implementation "com.mikepenz:aboutlibraries:$latestAboutLibsRelease" // https://developer.android.com/kotlin/ktx#play-core - api 'com.google.android.play:core:1.7.3' - api 'com.google.android.play:core-ktx:1.7.0' + api 'com.google.android.play:core:1.9.0' + api 'com.google.android.play:core-ktx:1.8.1' // https://developer.android.com/kotlin/ktx#collection implementation 'androidx.collection:collection-ktx:1.1.0' // https://kotlinlang.org/docs/reference/reflection.html implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // https://firebase.google.com/docs/android/setup#add-sdks - api 'com.google.firebase:firebase-common-ktx:19.3.0' - api 'com.google.firebase:firebase-analytics:17.4.3' - api 'com.google.firebase:firebase-crashlytics:17.1.0' - api 'com.google.firebase:firebase-perf:19.0.7' - implementation 'com.google.firebase:firebase-auth:19.3.1' + api 'com.google.firebase:firebase-common-ktx:19.4.0' + api 'com.google.firebase:firebase-analytics:18.0.0' + api 'com.google.firebase:firebase-crashlytics:17.3.0' + api 'com.google.firebase:firebase-perf:19.0.10' + implementation 'com.google.firebase:firebase-auth:20.0.1' // http://airbnb.io/lottie/#/android?id=getting-started - api 'com.airbnb.android:lottie:3.4.1' + api 'com.airbnb.android:lottie:3.5.0' // https://firebase.google.com/docs/remote-config/use-config-android - implementation 'com.google.firebase:firebase-config:19.1.4' - implementation 'com.google.firebase:firebase-config-ktx:19.1.4' + implementation 'com.google.firebase:firebase-config:20.0.2' + implementation 'com.google.firebase:firebase-config-ktx:20.0.2' // https://mvnrepository.com/artifact/androidx.emoji/emoji/ api 'androidx.emoji:emoji:1.1.0' api 'androidx.emoji:emoji-appcompat:1.1.0' @@ -184,16 +188,16 @@ dependencies { // https://github.com/afollestad/material-dialogs/ implementation 'com.afollestad.material-dialogs:core:3.3.0' // https://developer.android.com/google/play/billing/billing_library_overview - implementation 'com.android.billingclient:billing:3.0.0' - implementation 'com.android.billingclient:billing-ktx:3.0.0' + implementation 'com.android.billingclient:billing:3.0.2' + implementation 'com.android.billingclient:billing-ktx:3.0.2' // https://github.com/cbeust/klaxon - implementation 'com.beust:klaxon:5.2' + implementation 'com.beust:klaxon:5.4' // https://github.com/SufficientlySecure/html-textview - implementation 'org.sufficientlysecure:html-textview:3.9' + implementation 'org.sufficientlysecure:html-textview:4.0' // https://github.com/square/okhttp/tree/okhttp_3.12.x - implementation 'com.squareup.okhttp3:okhttp:3.12.11' + implementation 'com.squareup.okhttp3:okhttp:3.12.12' // https://square.github.io/okio/#releases - implementation 'com.squareup.okio:okio:2.6.0' + implementation 'com.squareup.okio:okio:2.9.0' // https://github.com/deano2390/MaterialShowcaseView implementation 'com.github.deano2390:MaterialShowcaseView:1.3.4' // https://github.com/PhilJay/MPAndroidChart @@ -202,6 +206,8 @@ dependencies { implementation "androidx.room:room-ktx:$room_version" kapt "androidx.room:room-compiler:$room_version" // https://coil-kt.github.io/coil/ - api "io.coil-kt:coil:0.11.0" + api "io.coil-kt:coil:1.1.0" + // https://developer.android.com/jetpack/androidx/releases/work#declaring_dependencies + implementation "androidx.work:work-runtime-ktx:2.4.0" } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7558e6a..1cb8f50 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,9 +73,12 @@ + android:enabled="true" + android:exported="true"> + + diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt index 0d5c794..dc32993 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt @@ -36,14 +36,12 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.javinator9889.handwashingreminder.BuildConfig import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.SplitCompatBaseActivity -import com.javinator9889.handwashingreminder.utils.AndroidVersion -import com.javinator9889.handwashingreminder.utils.CONFIRMATION_REQUEST_CODE -import com.javinator9889.handwashingreminder.utils.filterNotEmpty -import com.javinator9889.handwashingreminder.utils.isAtLeast -import kotlinx.android.synthetic.main.dynamic_content_pb.* +import com.javinator9889.handwashingreminder.databinding.DynamicContentPbBinding +import com.javinator9889.handwashingreminder.utils.* import timber.log.Timber -class DynamicFeatureProgress : SplitCompatBaseActivity(), +class DynamicFeatureProgress : + SplitCompatBaseActivity(), SplitInstallStateUpdatedListener { companion object { const val MODULES = "modules" @@ -60,10 +58,12 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val layout = inflateLayout() splitInstallManager.registerListener(this) with(FirebaseAnalytics.getInstance(this)) { setCurrentScreen( - this@DynamicFeatureProgress, "Dynamic module", null + "Dynamic module", + this@DynamicFeatureProgress::class ) } modules = @@ -85,7 +85,7 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), } } if (moduleCount > 0 && modules.isNotEmpty()) { - setContentView(R.layout.dynamic_content_pb) + setContentView(layout.root) overridePendingTransition(android.R.anim.fade_in, 0) val installRequest = installRequestBuilder.build() splitInstallManager.startInstall(installRequest) @@ -114,8 +114,8 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), } @SuppressLint("SetTextI18n") - override fun onStateUpdate(state: SplitInstallSessionState?) { - when (state?.status()) { + override fun onStateUpdate(state: SplitInstallSessionState) { + when (state.status()) { SplitInstallSessionStatus.FAILED -> { Toast.makeText( this, getString( @@ -140,36 +140,36 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), finish() } SplitInstallSessionStatus.PENDING -> { - install_progress.isIndeterminate = true - percentage.text = getString(R.string.preparing) + binding.installProgress.isIndeterminate = true + binding.percentage.text = getString(R.string.preparing) } SplitInstallSessionStatus.DOWNLOADING -> { val downloadedBytes = Formatter.formatFileSize(this, state.bytesDownloaded) val bytesToDownload = Formatter.formatFileSize(this, state.totalBytesToDownload) - bytesInfo.text = "$downloadedBytes / $bytesToDownload" - install_progress.isIndeterminate = false - install_progress.max = state.totalBytesToDownload.toInt() + binding.bytesInfo.text = "$downloadedBytes / $bytesToDownload" + binding.installProgress.isIndeterminate = false + binding.installProgress.max = state.totalBytesToDownload.toInt() val progress = state.bytesDownloaded.toInt() if (isAtLeast(AndroidVersion.N)) - install_progress.setProgress(progress, true) + binding.installProgress.setProgress(progress, true) else - install_progress.progress = progress + binding.installProgress.progress = progress val currentPercentage = (state.bytesDownloaded * 100 / state.totalBytesToDownload) .toInt() - percentage.text = "$currentPercentage %" + binding.percentage.text = "$currentPercentage %" } SplitInstallSessionStatus.INSTALLING -> { - install_progress.isIndeterminate = true - bytesInfo.text = "" - percentage.text = getString(R.string.installing) + binding.installProgress.isIndeterminate = true + binding.bytesInfo.text = "" + binding.percentage.text = getString(R.string.installing) } SplitInstallSessionStatus.INSTALLED -> { SplitInstallHelper.updateAppInfo(this) if (++currentModule >= moduleCount) { - dynamic_content_title.text = getString(R.string.done) + binding.dynamicContentTitle.text = getString(R.string.done) setResultWithIntent(Activity.RESULT_OK) finish() } @@ -189,4 +189,7 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), finish() } } + + override fun inflateLayout(): DynamicContentPbBinding = + DynamicContentPbBinding.inflate(layoutInflater).also { binding = it } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt index 915bbb2..18ab4d5 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt @@ -21,10 +21,9 @@ package com.javinator9889.handwashingreminder.activities import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Bundle import android.view.animation.Animation import android.view.animation.AnimationUtils -import androidx.appcompat.app.AppCompatActivity +import androidx.annotation.LayoutRes import androidx.lifecycle.lifecycleScope import androidx.lifecycle.whenCreated import androidx.lifecycle.whenStarted @@ -40,8 +39,10 @@ import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings import com.google.firebase.remoteconfig.ktx.remoteConfig import com.javinator9889.handwashingreminder.BuildConfig import com.javinator9889.handwashingreminder.R +import com.javinator9889.handwashingreminder.activities.base.BaseFragmentActivity import com.javinator9889.handwashingreminder.application.HandwashingApplication import com.javinator9889.handwashingreminder.data.UserProperties +import com.javinator9889.handwashingreminder.databinding.SplashScreenBinding import com.javinator9889.handwashingreminder.gms.activity.ActivityHandler import com.javinator9889.handwashingreminder.gms.ads.AdLoader import com.javinator9889.handwashingreminder.gms.ads.AdsEnabler @@ -55,7 +56,6 @@ import com.javinator9889.handwashingreminder.utils.RemoteConfig.SPECIAL_EVENT import com.javinator9889.handwashingreminder.utils.threading.await import com.mikepenz.iconics.Iconics import javinator9889.localemanager.utils.languagesupport.LanguagesSupport.Language -import kotlinx.android.synthetic.main.splash_screen.* import kotlinx.coroutines.* import timber.log.Timber import com.javinator9889.handwashingreminder.utils.Firebase as FirebaseConf @@ -63,7 +63,7 @@ import com.javinator9889.handwashingreminder.utils.Firebase as FirebaseConf internal const val FAST_START_KEY = "intent:fast_start" internal const val PENDING_INTENT_CODE = 201 -class LauncherActivity : AppCompatActivity() { +class LauncherActivity : BaseFragmentActivity() { private val deferreds = mutableSetOf>() private var launchOnInstall = false private var launchFromNotification = false @@ -73,6 +73,9 @@ class LauncherActivity : AppCompatActivity() { private val dynamicFeatureDeferred = CompletableDeferred() private val activityIntentDeferred = CompletableDeferred() private val splitInstallManager = SplitInstallManagerFactory.create(app) + private lateinit var binding: SplashScreenBinding + @get:LayoutRes + override val layoutId: Int = R.layout.splash_screen init { lifecycleScope.launch { @@ -82,7 +85,7 @@ class LauncherActivity : AppCompatActivity() { deferreds.add(async { initVariables() }) } whenStarted { - progressBar.show() + binding.progressBar.show() val welcomeScreenJob = showWelcomeScreenAsync() deferreds.add(installRequiredModulesAsync()) activityIntentDeferred.await().run { @@ -90,7 +93,9 @@ class LauncherActivity : AppCompatActivity() { if (launchFromNotification) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + Timber.d("Joining welcome screen...") welcomeScreenJob.join() + Timber.d("Start") startActivity(this) overridePendingTransition(0, android.R.anim.fade_out) finish() @@ -99,17 +104,17 @@ class LauncherActivity : AppCompatActivity() { } } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.splash_screen) - } + override fun inflateLayout(): SplashScreenBinding = + SplashScreenBinding.inflate(layoutInflater).also { binding = it } private fun showWelcomeScreenAsync() = lifecycleScope.launch(Dispatchers.Main) { + Timber.d("Awaiting Firebase initialization...") app.firebaseInitDeferred.await() val isThereAnySpecialEvent = with(Firebase.remoteConfig) { getBoolean(SPECIAL_EVENT) && !launchFromNotification } + Timber.d("Is any special event? $isThereAnySpecialEvent") var sleepDuration = 0L val animationLoaded = CompletableDeferred() val fadeInAnimation = AnimationUtils.loadAnimation( @@ -119,24 +124,29 @@ class LauncherActivity : AppCompatActivity() { fadeInAnimation.duration = 300L fadeInAnimation.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) {} + override fun onAnimationStart(animation: Animation?) { + Timber.d("Animation started!") + } override fun onAnimationRepeat(animation: Animation?) {} override fun onAnimationEnd(animation: Animation?) { + Timber.d("Animation is completed") animationLoaded.complete(true) - logo.playAnimation() + binding.logo.playAnimation() } }) - if (isThereAnySpecialEvent) { - logo.setAnimation(AnimatedResources.STAY_SAFE_STAY_HOME.res) - logo.addLottieOnCompositionLoadedListener { - logo.startAnimation(fadeInAnimation) - sleepDuration = logo.duration + if (isThereAnySpecialEvent && !isDebuggable()) { + Timber.d("Starting custom animation...") + binding.logo.setAnimation(AnimatedResources.STAY_SAFE_STAY_HOME.res) + binding.logo.addLottieOnCompositionLoadedListener { + Timber.d("Animation loaded!") + binding.logo.startAnimation(fadeInAnimation) + sleepDuration = binding.logo.duration } animationLoaded.await() delay(sleepDuration) } else { - logo.setImageResource(R.drawable.handwashing_app_logo) - logo.startAnimation(fadeInAnimation) + binding.logo.setImageResource(R.drawable.handwashing_app_logo) + binding.logo.startAnimation(fadeInAnimation) } } @@ -173,7 +183,7 @@ class LauncherActivity : AppCompatActivity() { } Timber.d("Created launch intent $launchIntent") activityIntentDeferred.complete(launchIntent) - } + } ?: Timber.e("Data was empty!") } else { Timber.d("Created launch intent at MainActivity") activityIntentDeferred.complete( @@ -185,7 +195,7 @@ class LauncherActivity : AppCompatActivity() { override fun finish() { Timber.d("Calling finish") - progressBar.hide() + binding.progressBar.hide() runBlocking(Dispatchers.Default) { deferreds.awaitAll() } super.finish() } @@ -299,13 +309,13 @@ class LauncherActivity : AppCompatActivity() { Timber.d("Adding periodic notifications if not enqueued yet") Timber.d("Creating alarms notification channels...") for (alarm in Alarms.values()) { - Timber.d("Creating notification channel for ${alarm.identifier}") + Timber.d("Creating notification channel for ${alarm.groupId}") NotificationsHandler( context = this, channelId = alarm.channelId, - channelName = getString(R.string.time_notification_channel_name), - channelDesc = getString(R.string.time_notification_channel_desc), - groupId = alarm.identifier, + channelName = getString(alarm.channelName), + channelDesc = getString(alarm.channelDesc), + groupId = alarm.groupId, groupName = getString(alarm.groupName) ) } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt index d5f91ba..c959732 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt @@ -37,9 +37,9 @@ import com.javinator9889.handwashingreminder.activities.views.fragments.news.New import com.javinator9889.handwashingreminder.activities.views.fragments.washinghands.WashingHandsFragment import com.javinator9889.handwashingreminder.custom.libraries.AppRate import com.javinator9889.handwashingreminder.data.MainActivityDataHandler +import com.javinator9889.handwashingreminder.databinding.ActivityMainBinding import com.javinator9889.handwashingreminder.firebase.Auth -import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.how_to_wash_hands_layout.* +import com.javinator9889.handwashingreminder.utils.setCurrentScreen import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -48,7 +48,7 @@ import timber.log.Timber import uk.co.deanwild.materialshowcaseview.MaterialShowcaseSequence -class MainActivity : ActionBarBase(), +class MainActivity : ActionBarBase(), BottomNavigationView.OnNavigationItemSelectedListener, BottomNavigationView.OnNavigationItemReselectedListener { override val layoutId: Int = R.layout.activity_main @@ -60,9 +60,14 @@ class MainActivity : ActionBarBase(), lifecycleScope.launch { whenCreated { with(Firebase.remoteConfig) { fetchAndActivate() } - launch { dataHandler.setMenuIcons(menu, this@MainActivity) } - menu.setOnNavigationItemSelectedListener(this@MainActivity) - menu.setOnNavigationItemReselectedListener(this@MainActivity) + launch { + dataHandler.setMenuIcons( + binding.menu, + this@MainActivity + ) + } + binding.menu.setOnNavigationItemSelectedListener(this@MainActivity) + binding.menu.setOnNavigationItemReselectedListener(this@MainActivity) deferredShowcase = dataHandler.loadShowcaseAsync( activity = this@MainActivity, lifecycleOwner = this@MainActivity @@ -74,7 +79,7 @@ class MainActivity : ActionBarBase(), } whenResumed { with(FirebaseAnalytics.getInstance(this@MainActivity)) { - setCurrentScreen(this@MainActivity, "Main view", null) + setCurrentScreen("Main view", this@MainActivity::class) } deferredShowcase.await()?.let { withContext(Dispatchers.Main) { @@ -136,15 +141,15 @@ class MainActivity : ActionBarBase(), R.id.handwashing -> { val washingHandsFragment = dataHandler.activeFragment as WashingHandsFragment - if (washingHandsFragment.pager.currentItem != 0) - washingHandsFragment.pager.currentItem-- + if (washingHandsFragment.binding.pager.currentItem != 0) + washingHandsFragment.binding.pager.currentItem-- else { - menu.selectedItemId = R.id.diseases + binding.menu.selectedItemId = R.id.diseases onItemSelected(R.id.diseases) } } else -> { - menu.selectedItemId = R.id.diseases + binding.menu.selectedItemId = R.id.diseases onItemSelected(R.id.diseases) } } @@ -159,7 +164,7 @@ class MainActivity : ActionBarBase(), else -> "Main view" } with(FirebaseAnalytics.getInstance(this)) { - setCurrentScreen(this@MainActivity, screenTitle, null) + setCurrentScreen(screenTitle, this@MainActivity::class) } return onItemSelected(item.itemId) } @@ -201,4 +206,7 @@ class MainActivity : ActionBarBase(), disallowAddToBackStack() } } + + override fun inflateLayout(): ActivityMainBinding = + ActivityMainBinding.inflate(layoutInflater).also { binding = it } } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/PrivacyTermsActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/PrivacyTermsActivity.kt index 11dfdfa..59afb11 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/PrivacyTermsActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/PrivacyTermsActivity.kt @@ -26,9 +26,10 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.support.ActionBarBase import com.javinator9889.handwashingreminder.collections.PrivacyTermsCollectionAdapter -import kotlinx.android.synthetic.main.disease_view_expanded.* +import com.javinator9889.handwashingreminder.databinding.PrivacyTermsBinding -class PrivacyTermsActivity : ActionBarBase() { + +class PrivacyTermsActivity : ActionBarBase() { override val layoutId: Int = R.layout.privacy_terms override fun onCreate(savedInstanceState: Bundle?) { @@ -44,8 +45,11 @@ class PrivacyTermsActivity : ActionBarBase() { } val adapter = PrivacyTermsCollectionAdapter(this) - pager.adapter = adapter - TabLayoutMediator(diseaseInfoTab, pager) { tab, position -> + binding.pager.adapter = adapter + TabLayoutMediator( + binding.diseaseInfoTab, + binding.pager + ) { tab, position -> when (position) { 0 -> tab.text = getString(R.string.privacy_policy_title) 1 -> tab.text = getString(R.string.tos_title) @@ -57,4 +61,7 @@ class PrivacyTermsActivity : ActionBarBase() { super.onBackPressed() finish() } + + override fun inflateLayout(): PrivacyTermsBinding = + PrivacyTermsBinding.inflate(layoutInflater).also { binding = it } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentActivity.kt index e25613d..ed65c80 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentActivity.kt @@ -20,14 +20,16 @@ package com.javinator9889.handwashingreminder.activities.base import android.os.Bundle import androidx.annotation.LayoutRes +import androidx.viewbinding.ViewBinding import javinator9889.localemanager.fragment.BaseFragmentActivity -abstract class BaseFragmentActivity : BaseFragmentActivity() { +abstract class BaseFragmentActivity : BaseFragmentActivity(), ViewBindingInflater { @get:LayoutRes protected abstract val layoutId: Int override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(layoutId) + val layout = inflateLayout() + setContentView(layout.root) } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentView.kt index 5a72d86..3afde57 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseFragmentView.kt @@ -23,11 +23,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes +import androidx.viewbinding.ViewBinding import javinator9889.localemanager.fragment.BaseFragment -abstract class BaseFragmentView : BaseFragment() { +abstract class BaseFragmentView : BaseFragment() { @get:LayoutRes protected abstract val layoutId: Int + internal lateinit var binding: T override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/SplitCompatBaseActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/SplitCompatBaseActivity.kt index 90f8850..bde451f 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/SplitCompatBaseActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/SplitCompatBaseActivity.kt @@ -20,13 +20,16 @@ package com.javinator9889.handwashingreminder.activities.base import android.content.Context import android.os.Bundle +import androidx.viewbinding.ViewBinding import com.google.android.play.core.splitcompat.SplitCompat import com.google.android.play.core.splitinstall.SplitInstallManager import com.google.android.play.core.splitinstall.SplitInstallManagerFactory import javinator9889.localemanager.activity.BaseAppCompatActivity -abstract class SplitCompatBaseActivity : BaseAppCompatActivity() { +abstract class SplitCompatBaseActivity : + BaseAppCompatActivity(), ViewBindingInflater { protected lateinit var splitInstallManager: SplitInstallManager + protected lateinit var binding: T override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseVisibleFragmentView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/ViewBindingInflater.kt similarity index 81% rename from app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseVisibleFragmentView.kt rename to app/src/main/java/com/javinator9889/handwashingreminder/activities/base/ViewBindingInflater.kt index 113bf33..9881f57 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/BaseVisibleFragmentView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/base/ViewBindingInflater.kt @@ -14,10 +14,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/. * - * Created by Javinator9889 on 11/06/20 - Handwashing reminder. + * Created by Javinator9889 on 7/12/20 - Handwashing reminder. */ package com.javinator9889.handwashingreminder.activities.base -abstract class BaseVisibleFragmentView : BaseFragmentView() { - abstract fun onVisibilityChanged(visibility: Int) +import androidx.viewbinding.ViewBinding + +interface ViewBindingInflater { + fun inflateLayout(): T } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/support/ActionBarBase.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/support/ActionBarBase.kt index 20e34f4..35b7b63 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/support/ActionBarBase.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/support/ActionBarBase.kt @@ -20,16 +20,21 @@ package com.javinator9889.handwashingreminder.activities.support import android.os.Bundle import androidx.annotation.LayoutRes +import androidx.viewbinding.ViewBinding import com.javinator9889.handwashingreminder.R +import com.javinator9889.handwashingreminder.activities.base.ViewBindingInflater import javinator9889.localemanager.activity.BaseAppCompatActivity -abstract class ActionBarBase : BaseAppCompatActivity() { +abstract class ActionBarBase : BaseAppCompatActivity(), + ViewBindingInflater { @get:LayoutRes protected abstract val layoutId: Int + protected lateinit var binding: T override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(layoutId) + val layout = inflateLayout() + setContentView(layout.root) setSupportActionBar(findViewById(R.id.toolbar)) } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseDescriptionFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseDescriptionFragment.kt index 7630a49..c82cd00 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseDescriptionFragment.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseDescriptionFragment.kt @@ -19,11 +19,14 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.diseases import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.annotation.LayoutRes import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView import com.javinator9889.handwashingreminder.data.ParsedHTMLText -import kotlinx.android.synthetic.main.disease_description.* +import com.javinator9889.handwashingreminder.databinding.DiseaseDescriptionBinding import kotlin.properties.Delegates internal const val ARG_TITLE = "bundle:title" @@ -34,7 +37,7 @@ internal const val ARG_WEBSITE = "bundle:website" internal const val ARG_ANIMATION_ID = "bundle:animation:id" internal const val ARG_HTML_TEXT = "bundle:text:html" -class DiseaseDescriptionFragment : BaseFragmentView() { +class DiseaseDescriptionFragment : BaseFragmentView() { @get:LayoutRes override val layoutId: Int = R.layout.disease_description private lateinit var parsedHTMLText: ParsedHTMLText @@ -45,13 +48,23 @@ class DiseaseDescriptionFragment : BaseFragmentView() { retainInstance = true } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = DiseaseDescriptionBinding.bind(view) + return view + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putCharSequence(ARG_TITLE, title.text) - outState.putCharSequence(ARG_SDESC, shortDescription.text) - outState.putCharSequence(ARG_LDESC, longDescription.text) - outState.putCharSequence(ARG_PROVIDER, provider.text) - outState.putCharSequence(ARG_WEBSITE, website.text) + outState.putCharSequence(ARG_TITLE, binding.title.text) + outState.putCharSequence(ARG_SDESC, binding.shortDescription.text) + outState.putCharSequence(ARG_LDESC, binding.longDescription.text) + outState.putCharSequence(ARG_PROVIDER, binding.provider.text) + outState.putCharSequence(ARG_WEBSITE, binding.website.text) outState.putParcelable(ARG_HTML_TEXT, parsedHTMLText) outState.putInt(ARG_ANIMATION_ID, animId) } @@ -62,15 +75,15 @@ class DiseaseDescriptionFragment : BaseFragmentView() { val data = (savedInstanceState ?: arguments)!! parsedHTMLText = data.getParcelable(ARG_HTML_TEXT)!! animId = data.getInt(ARG_ANIMATION_ID) - animatedView.setAnimation(animId) - title.text = data.getCharSequence(ARG_TITLE) ?: parsedHTMLText.name - shortDescription.text = data.getCharSequence(ARG_SDESC) + binding.animatedView.setAnimation(animId) + binding.title.text = data.getCharSequence(ARG_TITLE) ?: parsedHTMLText.name + binding.shortDescription.text = data.getCharSequence(ARG_SDESC) ?: parsedHTMLText.shortDescription - longDescription.text = data.getCharSequence(ARG_LDESC) + binding.longDescription.text = data.getCharSequence(ARG_LDESC) ?: parsedHTMLText.longDescription - provider.text = data.getCharSequence(ARG_PROVIDER) + binding.provider.text = data.getCharSequence(ARG_PROVIDER) ?: getString(R.string.written_by, parsedHTMLText.provider) - website.text = data.getCharSequence(ARG_WEBSITE) + binding.website.text = data.getCharSequence(ARG_WEBSITE) ?: getString(R.string.available_at, parsedHTMLText.website) } } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExpandedView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExpandedView.kt index 026a918..f1548ef 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExpandedView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExpandedView.kt @@ -25,20 +25,20 @@ import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.support.ActionBarBase import com.javinator9889.handwashingreminder.collections.DiseaseTextAdapter import com.javinator9889.handwashingreminder.data.ParsedHTMLText -import kotlinx.android.synthetic.main.disease_view_expanded.* +import com.javinator9889.handwashingreminder.databinding.DiseaseViewExpandedBinding import kotlin.properties.Delegates internal const val ARG_ANIMATION = "card:animation" internal const val ARG_PARSED_TEXT = "text:HTML:parsed" -class DiseaseExpandedView : ActionBarBase() { +class DiseaseExpandedView : ActionBarBase() { override val layoutId: Int = R.layout.disease_view_expanded private var animId by Delegates.notNull() private var parsedHTMLText: ParsedHTMLText? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - toolbar.setTitleTextColor(Color.BLACK) + binding.toolbar.setTitleTextColor(Color.BLACK) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(true) @@ -48,8 +48,11 @@ class DiseaseExpandedView : ActionBarBase() { parsedHTMLText = data.getParcelable(ARG_PARSED_TEXT) if (parsedHTMLText != null) { val adapter = DiseaseTextAdapter(this, animId, parsedHTMLText!!) - pager.adapter = adapter - TabLayoutMediator(diseaseInfoTab, pager) { tab, position -> + binding.pager.adapter = adapter + TabLayoutMediator( + binding.diseaseInfoTab, + binding.pager + ) { tab, position -> when (position) { 0 -> tab.text = getText(R.string.description) 1 -> tab.text = getText(R.string.symptoms) @@ -70,4 +73,7 @@ class DiseaseExpandedView : ActionBarBase() { super.onBackPressed() finish() } + + override fun inflateLayout(): DiseaseViewExpandedBinding = + DiseaseViewExpandedBinding.inflate(layoutInflater).also { binding = it } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExtraInformationFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExtraInformationFragment.kt index f42e9e7..e1d0996 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExtraInformationFragment.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseaseExtraInformationFragment.kt @@ -19,18 +19,21 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.diseases import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.annotation.LayoutRes import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView import com.javinator9889.handwashingreminder.data.ParsedHTMLText -import kotlinx.android.synthetic.main.simple_text_view.* +import com.javinator9889.handwashingreminder.databinding.SimpleTextViewBinding import kotlin.properties.Delegates internal const val ARG_SYMPTOMS = "bundle:symptoms" internal const val ARG_PREVENTION = "bundle:prevention" internal const val ARG_POSITION = "bundle:item:position" -class DiseaseExtraInformationFragment : BaseFragmentView() { +class DiseaseExtraInformationFragment : BaseFragmentView() { @get:LayoutRes override val layoutId: Int = R.layout.simple_text_view private var position by Delegates.notNull() @@ -41,12 +44,22 @@ class DiseaseExtraInformationFragment : BaseFragmentView() { retainInstance = true } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = SimpleTextViewBinding.bind(view) + return view + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) if (position == 1) - outState.putCharSequence(ARG_SYMPTOMS, text.text) + outState.putCharSequence(ARG_SYMPTOMS, binding.text.text) else if (position == 2) - outState.putCharSequence(ARG_PREVENTION, text.text) + outState.putCharSequence(ARG_PREVENTION, binding.text.text) outState.putInt(ARG_POSITION, position) outState.putParcelable(ARG_HTML_TEXT, parsedHTMLText) } @@ -57,7 +70,7 @@ class DiseaseExtraInformationFragment : BaseFragmentView() { val data = (savedInstanceState ?: arguments)!! position = data.getInt(ARG_POSITION) parsedHTMLText = data.getParcelable(ARG_HTML_TEXT)!! - text.text = when (position) { + binding.text.text = when (position) { 1 -> data.getCharSequence(ARG_SYMPTOMS) ?: parsedHTMLText.symptoms 2 -> data.getCharSequence(ARG_PREVENTION) diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseasesFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseasesFragment.kt index b15d819..48d1726 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseasesFragment.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/diseases/DiseasesFragment.kt @@ -20,7 +20,9 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.disease import android.content.Intent import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.LinearLayout import androidx.annotation.StringRes import androidx.core.app.ActivityCompat @@ -48,6 +50,9 @@ import com.javinator9889.handwashingreminder.activities.views.viewmodels.SavedVi import com.javinator9889.handwashingreminder.data.ParsedHTMLText import com.javinator9889.handwashingreminder.data.room.entities.Handwashing import com.javinator9889.handwashingreminder.data.viewmodels.HandwashingViewModel +import com.javinator9889.handwashingreminder.databinding.HandwashCountBinding +import com.javinator9889.handwashingreminder.databinding.LoadingRecyclerViewBinding +import com.javinator9889.handwashingreminder.databinding.MainDiseaseViewBinding import com.javinator9889.handwashingreminder.emoji.EmojiLoader import com.javinator9889.handwashingreminder.utils.calendar.CalendarUtils import com.javinator9889.handwashingreminder.utils.toBarEntry @@ -55,17 +60,13 @@ import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.GenericItem import com.mikepenz.fastadapter.adapters.ItemAdapter import com.mikepenz.fastadapter.listeners.ClickEventHook -import kotlinx.android.synthetic.main.handwash_count.* -import kotlinx.android.synthetic.main.handwash_count.view.* -import kotlinx.android.synthetic.main.loading_recycler_view.* -import kotlinx.android.synthetic.main.loading_recycler_view.view.* -import kotlinx.android.synthetic.main.main_disease_view.view.* import kotlinx.coroutines.Deferred import kotlinx.coroutines.launch import timber.log.Timber -class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, +class DiseasesFragment : BaseFragmentView(), + LayoutVisibilityChange, View.OnClickListener { override val layoutId: Int = R.layout.main_disease_view @@ -73,6 +74,8 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, private lateinit var fastAdapter: FastAdapter private lateinit var emojiLoader: Deferred private lateinit var behavior: BottomSheetBehavior + private lateinit var handwashBinding: HandwashCountBinding + private lateinit var loadingRecyclerViewBinding: LoadingRecyclerViewBinding private val upperAdsAdapter: ItemAdapter = ItemAdapter() private val lowerAdsAdapter: ItemAdapter = ItemAdapter() private val diseasesAdapter: ItemAdapter = ItemAdapter() @@ -84,8 +87,8 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, init { lifecycleScope.launchWhenStarted { - loading.visibility = View.VISIBLE - countLoader.visibility = View.VISIBLE + loadingRecyclerViewBinding.loading.visibility = View.VISIBLE + handwashBinding.countLoader.visibility = View.VISIBLE informationViewModel.parsedHTMLText.observe(viewLifecycleOwner) { if (it.isEmpty()) return@observe @@ -103,8 +106,8 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, if (diseasesAdapter.getAdapterPosition(disease) == -1) diseasesAdapter.add(disease) } - loading.visibility = View.INVISIBLE - container.visibility = View.VISIBLE + loadingRecyclerViewBinding.loading.visibility = View.INVISIBLE + loadingRecyclerViewBinding.container.visibility = View.VISIBLE } } handwashingViewModel.allData.observe(viewLifecycleOwner) { @@ -119,12 +122,12 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, valueFormatter = IntFormatter() dataSet = this } - countChart.data = BarData(dataSet) - countChart.notifyDataSetChanged() - countChart.fitScreen() - countChart.moveViewToX(0F) - countChart.setVisibleXRangeMaximum(7F) - countChart.invalidate() + handwashBinding.countChart.data = BarData(dataSet) + handwashBinding.countChart.notifyDataSetChanged() + handwashBinding.countChart.fitScreen() + handwashBinding.countChart.moveViewToX(0F) + handwashBinding.countChart.setVisibleXRangeMaximum(7F) + handwashBinding.countChart.invalidate() val todayAmount = handwashingViewModel.getAsync(CalendarUtils.today.time) val weeklyAmount = @@ -132,27 +135,39 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, val monthlyAmount = handwashingViewModel.getMonthlyCountAsync() setCountText( - countDailyTextView, + handwashBinding.countDailyTextView, R.string.today_washed, todayAmount.await()?.amount ?: 0 ) setCountText( - countWeeklyTextView, + handwashBinding.countWeeklyTextView, R.string.week_washed, weeklyAmount.await() ) setCountText( - countMonthlyTextView, + handwashBinding.countMonthlyTextView, R.string.month_washed, monthlyAmount.await() ) - countLoader.visibility = View.INVISIBLE - scrollView.visibility = View.VISIBLE + handwashBinding.countLoader.visibility = View.INVISIBLE + handwashBinding.scrollView.visibility = View.VISIBLE } } } } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = MainDiseaseViewBinding.bind(view) + handwashBinding = binding.handwashingCount + loadingRecyclerViewBinding = binding.lrv + return view + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) onViewCreatedAsync(view, savedInstanceState) @@ -171,7 +186,7 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, } } try { - container.adapter = null + loadingRecyclerViewBinding.container.adapter = null diseasesAdapter.clear() } catch (e: Exception) { Timber.w(e, "Exception when calling 'onBackPressed'") @@ -184,7 +199,7 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, override fun onClick(v: View?) { when (v) { - countUpButton -> lifecycleScope.launch { + handwashBinding.countUpButton -> lifecycleScope.launch { val createdItem = handwashingViewModel.getAsync(CalendarUtils.today.time) .await() @@ -196,11 +211,11 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, ) ) handwashingViewModel.increment(CalendarUtils.today.time) - leaves.visibility = View.VISIBLE - if (!leaves.isAnimating) - leaves.playAnimation() + handwashBinding.leaves.visibility = View.VISIBLE + if (!handwashBinding.leaves.isAnimating) + handwashBinding.leaves.playAnimation() } - countDownButton -> lifecycleScope.launch { + handwashBinding.countDownButton -> lifecycleScope.launch { val createdItem = handwashingViewModel.getAsync(CalendarUtils.today.time) .await() @@ -240,52 +255,52 @@ class DiseasesFragment : BaseFragmentView(), LayoutVisibilityChange, listOf(upperAdsAdapter, diseasesAdapter, lowerAdsAdapter) fastAdapter = FastAdapter.with(adapters) val rvManager = LinearLayoutManager(context) - with(view.container) { + with(loadingRecyclerViewBinding.container) { layoutManager = rvManager adapter = fastAdapter } fastAdapter.addEventHook(DiseaseClickEventHook()) fastAdapter.withSavedInstanceState(savedInstanceState) - behavior = BottomSheetBehavior.from(view.contentLayout) + behavior = BottomSheetBehavior.from(binding.contentLayout) behavior.addBottomSheetCallback(BottomSheetStateCallback()) - view.countChart.setDrawGridBackground(false) - view.countChart.axisLeft.apply { + handwashBinding.countChart.setDrawGridBackground(false) + handwashBinding.countChart.axisLeft.apply { setDrawGridLines(false) valueFormatter = IntFormatter() isGranularityEnabled = true granularity = 1F axisMinimum = 0F } - view.countChart.axisRight.apply { + handwashBinding.countChart.axisRight.apply { setDrawGridLines(false) valueFormatter = IntFormatter() isGranularityEnabled = true granularity = 1F axisMinimum = 0F } - view.countChart.xAxis.apply { + handwashBinding.countChart.xAxis.apply { setDrawGridLines(false) valueFormatter = IntFormatter() isGranularityEnabled = true granularity = 1F axisMaximum = 0F } - view.countChart.setVisibleXRangeMaximum(7F) - view.countChart.isAutoScaleMinMaxEnabled = true - view.countChart.legend.isEnabled = false - view.countChart.description.isEnabled = false - view.countChart.invalidate() - view.countUpButton.setOnClickListener(this@DiseasesFragment) - view.countDownButton.setOnClickListener(this@DiseasesFragment) + handwashBinding.countChart.setVisibleXRangeMaximum(7F) + handwashBinding.countChart.isAutoScaleMinMaxEnabled = true + handwashBinding.countChart.legend.isEnabled = false + handwashBinding.countChart.description.isEnabled = false + handwashBinding.countChart.invalidate() + handwashBinding.countUpButton.setOnClickListener(this@DiseasesFragment) + handwashBinding.countDownButton.setOnClickListener(this@DiseasesFragment) val countUpText = getText(R.string.add_another) val countDownText = getText(R.string.reduce_count) val emojiCompat = emojiLoader.await() try { - countUpButton.text = emojiCompat.process(countUpText) - countDownButton.text = emojiCompat.process(countDownText) + handwashBinding.countUpButton.text = emojiCompat.process(countUpText) + handwashBinding.countDownButton.text = emojiCompat.process(countDownText) } catch (_: IllegalStateException) { - countUpButton.text = countUpText - countDownButton.text = countDownText + handwashBinding.countUpButton.text = countUpText + handwashBinding.countDownButton.text = countDownText } } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt index aeebd52..94505b7 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt @@ -21,7 +21,9 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.news import android.content.Intent import android.net.Uri import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -31,6 +33,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.afollestad.materialdialogs.MaterialDialog import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView @@ -38,6 +41,8 @@ import com.javinator9889.handwashingreminder.activities.base.LayoutVisibilityCha import com.javinator9889.handwashingreminder.activities.views.fragments.news.adapter.News import com.javinator9889.handwashingreminder.activities.views.viewmodels.NewsViewModel import com.javinator9889.handwashingreminder.data.UserProperties +import com.javinator9889.handwashingreminder.databinding.LoadingRecyclerViewBinding +import com.javinator9889.handwashingreminder.databinding.RefreshingLayoutBinding import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.GenericItem import com.mikepenz.fastadapter.adapters.GenericItemAdapter @@ -45,17 +50,17 @@ import com.mikepenz.fastadapter.adapters.ItemAdapter import com.mikepenz.fastadapter.listeners.ClickEventHook import com.mikepenz.fastadapter.scroll.EndlessRecyclerOnScrollListener import com.mikepenz.fastadapter.ui.items.ProgressItem -import kotlinx.android.synthetic.main.loading_recycler_view.* -import kotlinx.android.synthetic.main.loading_recycler_view.view.* -import kotlinx.android.synthetic.main.refreshing_layout.* import kotlinx.coroutines.launch import timber.log.Timber -class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { +class NewsFragment : BaseFragmentView(), + LayoutVisibilityChange, SwipeRefreshLayout.OnRefreshListener { @LayoutRes override val layoutId: Int = R.layout.refreshing_layout private lateinit var fastAdapter: FastAdapter private lateinit var footerAdapter: GenericItemAdapter + private lateinit var loadingRecyclerView: LoadingRecyclerViewBinding + private lateinit var scrollListener: EndlessRecyclerOnScrollListener private var viewCreated = false private val newsAdapter = ItemAdapter() private val newsViewModel: NewsViewModel by viewModels() @@ -64,17 +69,21 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { init { lifecycleScope.launch { whenStarted { - loading.visibility = View.VISIBLE - refreshLayout.isEnabled = false - newsViewModel.newsData.observe(viewLifecycleOwner) { + loadingRecyclerView.loading.visibility = View.VISIBLE + binding.refreshLayout.isEnabled = false + newsViewModel.newsData.observe(owner = viewLifecycleOwner) { if (::footerAdapter.isInitialized) footerAdapter.clear() if (it.hasError && newsAdapter.adapterItemCount == 0) { - errorScreen.visibility = View.VISIBLE - container.visibility = View.INVISIBLE - refreshLayout.isEnabled = true + loadingRecyclerView.errorScreen.visibility = View.VISIBLE + loadingRecyclerView.container.visibility = View.INVISIBLE + loadingRecyclerView.loading.visibility = View.INVISIBLE + binding.refreshLayout.isEnabled = true return@observe - } else errorScreen.visibility = View.INVISIBLE + } else { + loadingRecyclerView.errorScreen.visibility = View.INVISIBLE + loadingRecyclerView.loading.visibility = View.VISIBLE + } if (it.id !in activeItems) { val newsObject = News( title = it.title, @@ -83,13 +92,14 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { discoverDate = it.discoverDate, imageUrl = it.elements?.url, website = it.website?.name, + websiteHostname = it.website?.hostName, websiteImageUrl = it.website?.iconURL, lifecycleOwner = this@NewsFragment ) newsAdapter.add(newsObject) - loading.visibility = View.INVISIBLE - container.visibility = View.VISIBLE - refreshLayout.isEnabled = true + loadingRecyclerView.loading.visibility = View.INVISIBLE + loadingRecyclerView.container.visibility = View.VISIBLE + binding.refreshLayout.isEnabled = true activeItems.add(it.id) } } @@ -97,15 +107,26 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { } } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = RefreshingLayoutBinding.bind(view) + loadingRecyclerView = binding.loadingView + return view + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) footerAdapter = ItemAdapter.items() fastAdapter = FastAdapter.with(listOf(newsAdapter, footerAdapter)) val rvManager = LinearLayoutManager(context) - val scrollListener = + scrollListener = object : EndlessRecyclerOnScrollListener(footerAdapter) { override fun onLoadMore(currentPage: Int) { - view.container.post { + loadingRecyclerView.container.post { footerAdapter.clear() Timber.d("Loading more") val progressItem = ProgressItem() @@ -121,7 +142,7 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { } } } - with(view.container) { + with(loadingRecyclerView.container) { layoutManager = rvManager adapter = fastAdapter itemAnimator = DefaultItemAnimator() @@ -130,21 +151,9 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { fastAdapter.addEventHooks(listOf(NewsClickHook(), ShareClickHook())) fastAdapter.withSavedInstanceState(savedInstanceState) viewCreated = savedInstanceState == null - refreshLayout.setOnRefreshListener { - refreshLayout.isRefreshing = true - newsAdapter.clear() - activeItems.clear() - footerAdapter.clear() - scrollListener.disable() - lifecycleScope.launch { - newsViewModel.populateData(language = UserProperties.language) - }.invokeOnCompletion { - refreshLayout.isRefreshing = false - scrollListener.enable() - scrollListener.resetPageCount() - } - container.visibility = View.INVISIBLE - errorScreen.visibility = View.INVISIBLE + binding.refreshLayout.setOnRefreshListener(this) + binding.loadingView.reloadText.setOnClickListener { + onRefresh() } } @@ -158,7 +167,13 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { override fun getVerticalSnapPreference(): Int = SNAP_TO_START } smoothScroller.targetPosition = 0 - container.layoutManager?.startSmoothScroll(smoothScroller) + loadingRecyclerView.container.layoutManager?.let { + if (it.isSmoothScrolling) + it.scrollToPosition(0) + else + it.startSmoothScroll(smoothScroller) + } + } private inner class NewsClickHook : ClickEventHook() { @@ -229,4 +244,21 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { } } } + + override fun onRefresh() { + binding.refreshLayout.isRefreshing = true + newsAdapter.clear() + activeItems.clear() + footerAdapter.clear() + scrollListener.disable() + lifecycleScope.launch { + newsViewModel.populateData(language = UserProperties.language) + }.invokeOnCompletion { + binding.refreshLayout.isRefreshing = false + scrollListener.enable() + scrollListener.resetPageCount() + } + loadingRecyclerView.container.visibility = View.INVISIBLE + loadingRecyclerView.errorScreen.visibility = View.INVISIBLE + } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/adapter/News.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/adapter/News.kt index 97e04a2..ce3ae42 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/adapter/News.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/adapter/News.kt @@ -24,7 +24,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope -import coil.api.load +import coil.load import coil.size.Scale import com.airbnb.lottie.LottieAnimationView import com.google.android.material.card.MaterialCardView @@ -44,6 +44,7 @@ data class News( val discoverDate: Date?, val imageUrl: String?, val website: String?, + val websiteHostname: String?, val websiteImageUrl: String?, val lifecycleOwner: LifecycleOwner, override val layoutRes: Int = R.layout.news_card_view, @@ -82,9 +83,16 @@ data class News( scale(Scale.FILL) lifecycle(item.lifecycleOwner) } - } else websiteLogo.visibility = View.GONE - websiteName.text = item.website - ?: context.getString(R.string.no_website) + } else { + if (item.websiteHostname != null) { + websiteLogo.load("https://www.google.com/s2/favicons?domain=${item.websiteHostname}") { + scale(Scale.FILL) + lifecycle(item.lifecycleOwner) + } + } else websiteLogo.visibility = View.GONE + } + websiteName.text = + item.website ?: context.getString(R.string.no_website) publishDate.text = item.discoverDate?.let { formatter.format(it) } ?: context.getString(R.string.no_date) diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/SettingsView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/SettingsView.kt index 69e75e7..b6cdb7b 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/SettingsView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/SettingsView.kt @@ -40,7 +40,7 @@ class SettingsView : PreferenceFragmentCompat(), lateinit var adsPreference: WeakReference lateinit var donationsPreference: WeakReference lateinit var billingService: BillingService - private val loader = SettingsLoader(view = this, lifecycleOwner = this) + private val loader = SettingsLoader(view = this, lifecycleOwner = this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -57,6 +57,7 @@ class SettingsView : PreferenceFragmentCompat(), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) billingService = BillingService(view.context) + savedInstanceState?.let { loader.loadViews() } } override fun onPreferenceChange( diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/TimePickerPreference.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/TimePickerPreference.kt index f265205..b73e7a7 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/TimePickerPreference.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/TimePickerPreference.kt @@ -26,6 +26,7 @@ import androidx.preference.EditTextPreference import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.jobs.alarms.Alarms import com.javinator9889.handwashingreminder.utils.formatTime +import timber.log.Timber class TimePickerPreference : EditTextPreference, TimePickerDialog.OnTimeSetListener { @@ -55,6 +56,7 @@ class TimePickerPreference : EditTextPreference, } fun updateSummary(time: String? = null) { + Timber.d("Updating summary of $this to $time or $text") setSummary(time ?: text) } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/FirstSlide.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/FirstSlide.kt index 996c162..a6c4f44 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/FirstSlide.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/FirstSlide.kt @@ -18,21 +18,36 @@ */ package com.javinator9889.handwashingreminder.activities.views.fragments.washinghands +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView -import kotlinx.android.synthetic.main.wash_your_hands_first_slide.* +import com.javinator9889.handwashingreminder.databinding.WashYourHandsFirstSlideBinding -class FirstSlide : BaseFragmentView() { + +class FirstSlide : BaseFragmentView() { override val layoutId: Int = R.layout.wash_your_hands_first_slide + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = WashYourHandsFirstSlideBinding.bind(view) + return view + } + override fun onPause() { super.onPause() - animation.pauseAnimation() + binding.animation.pauseAnimation() } override fun onResume() { super.onResume() - animation.playAnimation() - image.setImageResource(R.drawable.handwashing_app_logo) + binding.animation.playAnimation() + binding.image.setImageResource(R.drawable.handwashing_app_logo) } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/SliderView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/SliderView.kt index 27dc1a3..2ff8a6b 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/SliderView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/SliderView.kt @@ -21,17 +21,19 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.washing import android.media.MediaPlayer import android.net.Uri import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.core.view.doOnLayout import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.lifecycle.whenStarted -import coil.api.load +import coil.load import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView import com.javinator9889.handwashingreminder.activities.views.viewmodels.* -import kotlinx.android.synthetic.main.wash_your_hands_demo.* +import com.javinator9889.handwashingreminder.databinding.WashYourHandsDemoBinding import kotlinx.coroutines.launch import timber.log.Timber import java.io.File @@ -41,7 +43,7 @@ import kotlin.properties.Delegates private const val WAITING_ITEMS_COUNT = 4 internal const val ARG_POSITION = "bundle:position" -class SliderView : BaseFragmentView() { +class SliderView : BaseFragmentView() { override val layoutId: Int = R.layout.wash_your_hands_demo private lateinit var videoURI: Uri private var drawableId by Delegates.notNull() @@ -59,7 +61,7 @@ class SliderView : BaseFragmentView() { init { lifecycleScope.launch { whenStarted { - loading.visibility = View.VISIBLE + binding.loading.visibility = View.VISIBLE viewModel.videos.observe(viewLifecycleOwner, Observer { with(File(requireContext().cacheDir, it)) { videoURI = Uri.fromFile(this) @@ -69,23 +71,23 @@ class SliderView : BaseFragmentView() { }) washingHandsModel.image.observe(viewLifecycleOwner, Observer { try { - image.load(it) + binding.image.load(it) } catch (e: Exception) { Timber.e(e, "Error while loading Glide view") - image.setImageResource(it) + binding.image.setImageResource(it) } drawableId = it Timber.d("Image finished loading") incrementCounter() }) washingHandsModel.title.observe(viewLifecycleOwner, Observer { - title.text = it + binding.title.text = it Timber.d("Title finished loading") incrementCounter() }) washingHandsModel.description.observe(viewLifecycleOwner, Observer { - description.text = it + binding.description.text = it Timber.d("Description finished loading") incrementCounter() }) @@ -93,6 +95,16 @@ class SliderView : BaseFragmentView() { } } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = WashYourHandsDemoBinding.bind(view) + return view + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val args = arguments ?: savedInstanceState ?: throw @@ -104,8 +116,8 @@ class SliderView : BaseFragmentView() { override fun onPause() { Timber.d("Slide paused") - video.requestFocus() - video.pause() + binding.video.requestFocus() + binding.video.pause() super.onPause() } @@ -119,20 +131,20 @@ class SliderView : BaseFragmentView() { override fun onResume() { Timber.d("Slide resumed") - video.requestFocus() - video.start() + binding.video.requestFocus() + binding.video.start() try { - image.load(drawableId) + binding.image.load(drawableId) } catch (e: Exception) { Timber.e(e, "Error while loading Glide view") - image.setImageResource(drawableId) + binding.image.setImageResource(drawableId) } super.onResume() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - image.doOnLayout { + binding.image.doOnLayout { washingHandsModel.setImageSize(it.measuredWidth, it.measuredHeight) } } @@ -150,16 +162,16 @@ class SliderView : BaseFragmentView() { } private fun showContent() { - loading.visibility = View.GONE - video.setVideoURI(videoURI) - title.visibility = View.VISIBLE - video.visibility = View.VISIBLE - video.requestFocus() - video.start() - video.setOnPreparedListener { mp: MediaPlayer? -> + binding.loading.visibility = View.GONE + binding.video.setVideoURI(videoURI) + binding.title.visibility = View.VISIBLE + binding.video.visibility = View.VISIBLE + binding.video.requestFocus() + binding.video.start() + binding.video.setOnPreparedListener { mp: MediaPlayer? -> mp?.isLooping = true } - description.visibility = View.VISIBLE - image.visibility = View.VISIBLE + binding.description.visibility = View.VISIBLE + binding.image.visibility = View.VISIBLE } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/WashingHandsFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/WashingHandsFragment.kt index f02bd10..9d422e9 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/WashingHandsFragment.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/washinghands/WashingHandsFragment.kt @@ -19,7 +19,9 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.washinghands import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter @@ -28,51 +30,60 @@ import com.google.android.material.tabs.TabLayoutMediator import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView import com.javinator9889.handwashingreminder.activities.base.LayoutVisibilityChange -import kotlinx.android.synthetic.main.how_to_wash_hands_layout.view.* -import kotlinx.android.synthetic.main.privacy_terms.* +import com.javinator9889.handwashingreminder.databinding.HowToWashHandsLayoutBinding import timber.log.Timber import java.lang.ref.WeakReference internal const val NUM_PAGES = 8 -class WashingHandsFragment : BaseFragmentView(), LayoutVisibilityChange { +class WashingHandsFragment : BaseFragmentView(), LayoutVisibilityChange { override val layoutId: Int = R.layout.how_to_wash_hands_layout private val items = arrayOfNulls>(NUM_PAGES) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view = super.onCreateView(inflater, container, savedInstanceState) + binding = HowToWashHandsLayoutBinding.bind(view) + return view + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val adapter = FragmentAdapter(requireActivity()) - view.pager.adapter = adapter - TabLayoutMediator(view.tabPager, view.pager) { _, _ -> }.attach() - view.pager.registerOnPageChangeCallback(object : + binding.pager.adapter = adapter + TabLayoutMediator(binding.tabPager, binding.pager) { _, _ -> }.attach() + binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) Timber.d("Current position: $position") when (position) { 0 -> { - view.previousButton.visibility = View.INVISIBLE - view.nextButton.visibility = View.VISIBLE + binding.previousButton.visibility = View.INVISIBLE + binding.nextButton.visibility = View.VISIBLE } NUM_PAGES - 1 -> { - view.nextButton.visibility = View.INVISIBLE - view.previousButton.visibility = View.VISIBLE + binding.nextButton.visibility = View.INVISIBLE + binding.previousButton.visibility = View.VISIBLE } else -> { - view.previousButton.visibility = View.VISIBLE - view.nextButton.visibility = View.VISIBLE + binding.previousButton.visibility = View.VISIBLE + binding.nextButton.visibility = View.VISIBLE } } } }) - view.previousButton.setOnClickListener { view.pager.currentItem-- } - view.nextButton.setOnClickListener { view.pager.currentItem++ } + binding.previousButton.setOnClickListener { binding.pager.currentItem-- } + binding.nextButton.setOnClickListener { binding.pager.currentItem++ } } override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) Timber.d("Visibility changed: $hidden") - items[pager.currentItem]?.get()?.onHiddenChanged(hidden) + items[binding.pager.currentItem]?.get()?.onHiddenChanged(hidden) } private inner class FragmentAdapter(fa: FragmentActivity) : @@ -86,7 +97,7 @@ class WashingHandsFragment : BaseFragmentView(), LayoutVisibilityChange { with(SliderView()) { val args = Bundle(1) args.putInt(ARG_POSITION, position - 1) - this.arguments = args + arguments = args items[position] = WeakReference(this) return this } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/NewsViewModel.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/NewsViewModel.kt index 5a0f430..74a24bf 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/NewsViewModel.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/NewsViewModel.kt @@ -50,7 +50,7 @@ class NewsViewModel : ViewModel() { fieldConverter(KlaxonDate::class, dateConverter) fieldConverter(KlaxonElements::class, elementConverter) } - var requestReader: Reader? = null + var requestReader: Reader? Auth.init() val token = Auth.token() Timber.d("Auth token: $token") diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt b/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt index ab2c123..ba1bf55 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt @@ -51,7 +51,7 @@ class HandwashingApplication : BaseApplication() { get() = instance.scope } - override fun attachBaseContext(base: Context?) { + override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(base) SplitCompat.install(base) diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt index 8892291..fc55104 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt @@ -44,6 +44,7 @@ import com.javinator9889.handwashingreminder.application.HandwashingApplication import com.javinator9889.handwashingreminder.emoji.EmojiLoader import com.javinator9889.handwashingreminder.gms.ads.AdsEnabler import com.javinator9889.handwashingreminder.gms.splitservice.SplitInstallService +import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.jobs.alarms.Alarms import com.javinator9889.handwashingreminder.listeners.OnPurchaseFinishedListener import com.javinator9889.handwashingreminder.utils.* @@ -506,7 +507,7 @@ class SettingsLoader( with(FirebaseAnalytics.getInstance(view.requireContext())) { setAnalyticsCollectionEnabled(enabled) if (!enabled) - setCurrentScreen(view.requireActivity(), null, null) + setCurrentScreen(null, view.requireActivity()::class) } true } @@ -590,6 +591,12 @@ class SettingsLoader( ) preference.summary = view.getString(R.string.minimum_time_summ, minutes) + + // Cancel the old alarm and schedule a new one with the updated + // time + with(AlarmHandler(view.requireContext())) { + scheduleAlarm(Alarms.PENDING_ACTIVITY_ALARM) + } true }.getOrElse { Timber.w(it); false } else -> true diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityHandler.kt b/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityHandler.kt index 47902c0..abdb126 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityHandler.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityHandler.kt @@ -22,12 +22,14 @@ import android.app.PendingIntent import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.content.Context import android.content.Intent +import androidx.preference.PreferenceManager import com.google.android.gms.location.ActivityRecognition import com.google.android.gms.location.ActivityTransition import com.google.android.gms.location.ActivityTransition.ACTIVITY_TRANSITION_EXIT import com.google.android.gms.location.ActivityTransitionRequest import com.google.android.gms.location.DetectedActivity import com.google.android.gms.tasks.Task +import com.javinator9889.handwashingreminder.utils.Preferences import timber.log.Timber internal const val ACTIVITY_REQUEST_CODE = 64 @@ -97,6 +99,11 @@ class ActivityHandler private constructor(private val context: Context) { private fun createPendingIntent(): PendingIntent = with(Intent(context, ActivityReceiver::class.java)) { action = TRANSITIONS_RECEIVER_ACTION + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + putExtra( + Preferences.ACTIVITY_MINIMUM_TIME, + prefs.getString(Preferences.ACTIVITY_MINIMUM_TIME, "15")!!.toInt() + ) PendingIntent.getBroadcast( context, ACTIVITY_REQUEST_CODE, diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityReceiver.kt index d7b2845..d176828 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityReceiver.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/gms/activity/ActivityReceiver.kt @@ -18,17 +18,26 @@ */ package com.javinator9889.handwashingreminder.gms.activity +import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import androidx.annotation.StringRes +import androidx.core.app.NotificationCompat import androidx.emoji.text.EmojiCompat -import androidx.preference.PreferenceManager import com.google.android.gms.location.ActivityTransition +import com.google.android.gms.location.ActivityTransitionEvent import com.google.android.gms.location.ActivityTransitionResult import com.google.android.gms.location.DetectedActivity import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.emoji.EmojiLoader +import com.javinator9889.handwashingreminder.jobs.HANDS_WASHED_ACTION +import com.javinator9889.handwashingreminder.jobs.HANDS_WASHED_CODE +import com.javinator9889.handwashingreminder.jobs.HandsWashedReceiver +import com.javinator9889.handwashingreminder.jobs.NOTIFICATION_ID_KEY +import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler +import com.javinator9889.handwashingreminder.jobs.alarms.Alarms +import com.javinator9889.handwashingreminder.notifications.Action import com.javinator9889.handwashingreminder.notifications.NotificationsHandler import com.javinator9889.handwashingreminder.utils.ACTIVITY_CHANNEL_ID import com.javinator9889.handwashingreminder.utils.Preferences @@ -38,11 +47,16 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber -import java.io.* import java.util.* import java.util.concurrent.TimeUnit class ActivityReceiver : BroadcastReceiver() { + companion object ReceiverData { + val pendingActivities: Deque = ArrayDeque() + var latestNotificationTime: Long = 0L + var alarmScheduled: Boolean = false + } + /** * {@inheritDoc} */ @@ -66,14 +80,19 @@ class ActivityReceiver : BroadcastReceiver() { ), context.getString( R.string.activity_notification_channel_desc + ), + "alarms:pending_activities", + context.getString( + R.string.activity_notification_channel_name ) ) goAsync { putNotification( notificationHandler, emojiLoader, - event.activityType, - context + event, + context, + intent ) } break @@ -85,29 +104,29 @@ class ActivityReceiver : BroadcastReceiver() { private suspend fun putNotification( notificationsHandler: NotificationsHandler, emojiLoader: Deferred, - detectedActivity: Int, - context: Context + event: ActivityTransitionEvent, + context: Context, + intent: Intent ) { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) val timeInBetweenNotifications = - prefs.getString(Preferences.ACTIVITY_MINIMUM_TIME, "15")!!.toInt() - val timeFile = File(context.cacheDir, "activity.time") - var latestNotificationTime = 0L - withContext(Dispatchers.IO) { - if (timeFile.exists()) { - DataInputStream(FileInputStream(timeFile)).use { - latestNotificationTime = it.readLong() - } - } - } + intent.getIntExtra(Preferences.ACTIVITY_MINIMUM_TIME, 15) val timeDifference = CalendarUtils.timeBetweenIn( TimeUnit.MINUTES, latestNotificationTime ) Timber.d("$timeDifference - $timeInBetweenNotifications") - if (timeDifference <= timeInBetweenNotifications) + if (timeDifference < timeInBetweenNotifications) { + pendingActivities.add(event) + if (!alarmScheduled) { + with(AlarmHandler(context)) { + scheduleAlarm(Alarms.PENDING_ACTIVITY_ALARM) + } + alarmScheduled = true + } return - val notificationContent = when (detectedActivity) { + } + latestNotificationTime = CalendarUtils.now + val notificationContent = when (event.activityType) { DetectedActivity.WALKING -> NotificationContent( R.string.activity_notification_walk, @@ -129,7 +148,7 @@ class ActivityReceiver : BroadcastReceiver() { R.string.activity_notification_vehicle_content ) else -> throw IllegalArgumentException( - "Activity not recognized - $detectedActivity" + "Activity not recognized - $event" ) } var title = context.getText(notificationContent.title) @@ -140,6 +159,15 @@ class ActivityReceiver : BroadcastReceiver() { content = emojiCompat.process(content) } catch (_: IllegalStateException) { } + val washedPendingIntent = PendingIntent.getBroadcast( + context, + HANDS_WASHED_CODE, + Intent(context, HandsWashedReceiver::class.java).apply { + action = HANDS_WASHED_ACTION + putExtra(NOTIFICATION_ID_KEY, 2) + }, + PendingIntent.FLAG_UPDATE_CURRENT + ) withContext(Dispatchers.Main) { notificationsHandler.createNotification( iconDrawable = R.drawable.ic_stat_handwashing, @@ -147,14 +175,15 @@ class ActivityReceiver : BroadcastReceiver() { title = title, content = content, longContent = content, - notificationId = 2 + notificationId = 2, + priority = NotificationCompat.PRIORITY_MAX, + action = Action( + R.drawable.ic_stat_handwashing, + context.getText(R.string.just_washed), + washedPendingIntent + ) ) } - withContext(Dispatchers.IO) { - DataOutputStream(FileOutputStream(timeFile)).use { - it.writeLong(Calendar.getInstance().timeInMillis) - } - } } } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/graphics/RecyclingImageView.kt b/app/src/main/java/com/javinator9889/handwashingreminder/graphics/RecyclingImageView.kt index 64a3818..37b9915 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/graphics/RecyclingImageView.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/graphics/RecyclingImageView.kt @@ -25,7 +25,7 @@ import android.util.AttributeSet import android.view.View import androidx.annotation.DrawableRes import androidx.appcompat.widget.AppCompatImageView -import coil.api.load +import coil.load class RecyclingImageView : AppCompatImageView { constructor(context: Context) : super(context) diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt index b0ecbe3..a3d43ff 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt @@ -27,11 +27,14 @@ import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.utils.Preferences import timber.log.Timber +const val ACTION_QBP = "android.intent.action.QUICKBOOT_POWERON" + class BootCompletedJob : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - if (intent.action == Intent.ACTION_BOOT_COMPLETED) { + if (intent.action in setOf(Intent.ACTION_BOOT_COMPLETED, ACTION_QBP)) { val activityHandler = ActivityHandler.getInstance(context) - val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val preferences = + PreferenceManager.getDefaultSharedPreferences(context) if (preferences.getBoolean( Preferences.ACTIVITY_TRACKING_ENABLED, false diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/HandsWashedReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/HandsWashedReceiver.kt index 08b0fbd..40f3101 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/HandsWashedReceiver.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/HandsWashedReceiver.kt @@ -36,6 +36,8 @@ import kotlinx.coroutines.withContext internal const val HANDS_WASHED_CODE = 128 internal const val HANDS_WASHED_ACTION = "com.javinator9889.handwashingreminder.HANDSWASHED_EVENT" +internal const val NOTIFICATION_ID_KEY = + "com.javinator9889.handwashingreminder.NID" class HandsWashedReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -44,8 +46,11 @@ class HandsWashedReceiver : BroadcastReceiver() { with(HandwashingDatabase.getDatabase(context).handwashingDao()) { HandwashingRepository(this) } - with(NotificationManagerCompat.from(context)) { - cancel(1) + intent.getIntExtra(NOTIFICATION_ID_KEY, -1).let { + if (it != -1) + with(NotificationManagerCompat.from(context)) { + cancel(it) + } } goAsync { val createdItem = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt index 6627b93..7c747a1 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt @@ -18,10 +18,15 @@ */ package com.javinator9889.handwashingreminder.jobs +import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import com.javinator9889.handwashingreminder.BuildConfig import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler +import com.javinator9889.handwashingreminder.utils.ACTIVITY_CHANNEL_ID +import com.javinator9889.handwashingreminder.utils.AndroidVersion +import com.javinator9889.handwashingreminder.utils.isAtLeast import timber.log.Timber class UpdateReceiver : BroadcastReceiver() { @@ -31,6 +36,27 @@ class UpdateReceiver : BroadcastReceiver() { with(AlarmHandler(context)) { scheduleAllAlarms() } + // Here, we need to remove all the notifications channels + // previously created as they have changed + if (BuildConfig.VERSION_CODE in 141..142 && isAtLeast(AndroidVersion.O)) { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + for (id in setOf( + "notifications:breakfast", + "notifications:lunch", + "notifications:dinner", + ACTIVITY_CHANNEL_ID + )) { + notificationManager.deleteNotificationChannel(id) + } + for (id in setOf( + "alarms:breakfast", + "alarms:lunch", + "alarms:dinner" + )) { + notificationManager.deleteNotificationChannelGroup(id) + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmHandler.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmHandler.kt index e1c9da6..34f1282 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmHandler.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmHandler.kt @@ -26,8 +26,11 @@ import android.content.Intent import androidx.annotation.IntRange import androidx.core.app.AlarmManagerCompat import androidx.preference.PreferenceManager +import com.javinator9889.handwashingreminder.utils.calendar.CalendarUtils import com.javinator9889.handwashingreminder.utils.timeAt import timber.log.Timber +import java.util.* +import java.util.concurrent.TimeUnit internal const val IDENTIFIER = "intent:id" @@ -74,6 +77,16 @@ class AlarmHandler(private val context: Context) { private fun getTimeForAlarm(alarm: Alarms): ScheduleTimeData { val preferences = PreferenceManager.getDefaultSharedPreferences(context) + if (alarm == Alarms.PENDING_ACTIVITY_ALARM) { + val elapsedTimeAlarm = + preferences.getString(alarm.preferenceKey, "15")!!.toInt() + val nextAlarm = + CalendarUtils.timeIn(elapsedTimeAlarm, TimeUnit.MINUTES) + return ScheduleTimeData( + nextAlarm[Calendar.HOUR_OF_DAY], + nextAlarm[Calendar.MINUTE] + ) + } val savedTime = preferences.getString(alarm.preferenceKey, "") if (savedTime.isNullOrBlank()) throw IllegalStateException("Time value cannot be null") diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmReceiver.kt index 70b6bfa..4058276 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmReceiver.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/AlarmReceiver.kt @@ -24,6 +24,7 @@ import android.content.Intent import com.javinator9889.handwashingreminder.jobs.workers.BreakfastNotificationWorker import com.javinator9889.handwashingreminder.jobs.workers.DinnerNotificationWorker import com.javinator9889.handwashingreminder.jobs.workers.LunchNotificationWorker +import com.javinator9889.handwashingreminder.jobs.workers.PendingActivityAlarmWorker import com.javinator9889.handwashingreminder.utils.goAsync class AlarmReceiver : BroadcastReceiver() { @@ -33,7 +34,7 @@ class AlarmReceiver : BroadcastReceiver() { BreakfastNotificationWorker(context) Alarms.LUNCH_ALARM.identifier -> LunchNotificationWorker(context) Alarms.DINNER_ALARM.identifier -> DinnerNotificationWorker(context) - else -> return + else -> PendingActivityAlarmWorker(context) } goAsync { worker.doWork() } } diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt index 3e0593e..eee4ef6 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/alarms/Alarms.kt @@ -20,34 +20,57 @@ package com.javinator9889.handwashingreminder.jobs.alarms import androidx.annotation.StringRes import com.javinator9889.handwashingreminder.R +import com.javinator9889.handwashingreminder.utils.ACTIVITY_CHANNEL_ID import com.javinator9889.handwashingreminder.utils.Preferences enum class Alarms( val identifier: String, val code: Int, val preferenceKey: String, + val groupId: String, @StringRes val groupName: Int, - val channelId: String + val channelId: String, + @StringRes val channelName: Int, + @StringRes val channelDesc: Int ) { BREAKFAST_ALARM( "alarms:breakfast", 0, Preferences.BREAKFAST_TIME, + "alarms:scheduled", + R.string.time_notification_channel_name, + "notifications:breakfast", R.string.breakfast_notifications, - "notifications:breakfast" + R.string.breakfast_description ), LUNCH_ALARM( "alarms:lunch", 1, Preferences.LUNCH_TIME, + "alarms:scheduled", + R.string.time_notification_channel_name, + "notifications:lunch", R.string.lunch_notifications, - "notifications:lunch" + R.string.lunch_description ), DINNER_ALARM( "alarms:dinner", 2, Preferences.DINNER_TIME, + "alarms:scheduled", + R.string.time_notification_channel_name, + "notifications:dinner", R.string.dinner_notifications, - "notifications:dinner" + R.string.dinner_description + ), + PENDING_ACTIVITY_ALARM( + "alarms:activities", + 3, + Preferences.ACTIVITY_MINIMUM_TIME, + "alarms:pending_activities", + R.string.activity_notification_channel_name, + ACTIVITY_CHANNEL_ID, + R.string.activity_notification_channel_name, + R.string.activity_notification_channel_desc ) } \ No newline at end of file diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/PendingActivityAlarmWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/PendingActivityAlarmWorker.kt new file mode 100644 index 0000000..b022c76 --- /dev/null +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/PendingActivityAlarmWorker.kt @@ -0,0 +1,57 @@ +/* + * Copyright © 2020 - present | Handwashing reminder by Javinator9889 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + * + * Created by Javinator9889 on 10/12/20 - Handwashing reminder. + */ +package com.javinator9889.handwashingreminder.jobs.workers + +import android.content.Context +import com.google.android.gms.location.DetectedActivity +import com.javinator9889.handwashingreminder.R +import com.javinator9889.handwashingreminder.gms.activity.ActivityReceiver +import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler +import com.javinator9889.handwashingreminder.jobs.alarms.Alarms + +class PendingActivityAlarmWorker(context: Context) : + ScheduledNotificationWorker(context) { + private val notificationContent: Pair + get() = when (ActivityReceiver.pendingActivities.poll()?.activityType) { + DetectedActivity.WALKING -> + R.string.activity_notification_walk to R.string.activity_notification_walk_content + DetectedActivity.RUNNING -> + R.string.activity_notification_run to R.string.activity_notifications_run_content + DetectedActivity.ON_BICYCLE -> + R.string.activity_notification_cycling to R.string.activity_notification_cycling_content + DetectedActivity.IN_VEHICLE -> + R.string.activity_notification_vehicle to R.string.activity_notification_vehicle_content + else -> -1 to -1 + } + override val alarm: Alarms = Alarms.PENDING_ACTIVITY_ALARM + override val titleRes: Int = notificationContent.first + override val contentsRes: Int = notificationContent.second + + override fun onFinish(result: Result) { + with(AlarmHandler(context)) { + if (ActivityReceiver.pendingActivities.size == 0) cancelAlarm(alarm) + else scheduleAlarm(alarm) + ActivityReceiver.alarmScheduled = + ActivityReceiver.pendingActivities.size != 0 + } + if (result.isFailure) { + super.onFinish(result) + } + } +} diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt index ac7f67e..e4cf5ad 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt @@ -30,6 +30,7 @@ import com.javinator9889.handwashingreminder.emoji.EmojiLoader import com.javinator9889.handwashingreminder.jobs.HANDS_WASHED_ACTION import com.javinator9889.handwashingreminder.jobs.HANDS_WASHED_CODE import com.javinator9889.handwashingreminder.jobs.HandsWashedReceiver +import com.javinator9889.handwashingreminder.jobs.NOTIFICATION_ID_KEY import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.jobs.alarms.Alarms import com.javinator9889.handwashingreminder.notifications.Action @@ -47,18 +48,20 @@ abstract class ScheduledNotificationWorker(context: Context) { protected abstract val contentsRes: Int suspend fun doWork() = coroutineScope { + var result: Result = Result.success(Unit) try { - val startTime = System.currentTimeMillis() val emojiLoader = EmojiLoader.loadAsync(context) val notificationsHandler = NotificationsHandler( context = context, channelId = alarm.channelId, - channelName = getString(R.string.time_notification_channel_name), - channelDesc = getString(R.string.time_notification_channel_desc), - groupId = alarm.identifier, + channelName = getString(alarm.channelName), + channelDesc = getString(alarm.channelDesc), + groupId = alarm.groupId, groupName = getString(alarm.groupName) ) val emojiCompat = emojiLoader.await() + if (titleRes == -1 && contentsRes == -1) + return@coroutineScope var title = getText(titleRes) var content = getStringArray(contentsRes).toList().random() as CharSequence @@ -72,6 +75,7 @@ abstract class ScheduledNotificationWorker(context: Context) { HANDS_WASHED_CODE, Intent(context, HandsWashedReceiver::class.java).apply { action = HANDS_WASHED_ACTION + putExtra(NOTIFICATION_ID_KEY, 1) }, PendingIntent.FLAG_UPDATE_CURRENT ) @@ -90,11 +94,7 @@ abstract class ScheduledNotificationWorker(context: Context) { ) ) } - Timber.d( - "Posting a notification took: ${System - .currentTimeMillis() - startTime}ms" - ) - } catch (e: Exception) { + } catch (e: Throwable) { with(HandwashingApplication.instance) { // Don't use so much resources, wait at most half a second until // Firebase initializes or continue with execution. @@ -104,14 +104,23 @@ abstract class ScheduledNotificationWorker(context: Context) { firebaseInitDeferred.await() } } - Timber.e(e, "Unhandled exception on worker class") + result = Result.failure(e) // We don't want to keep using CPU at this time if the request // fails so schedule next execution } finally { + onFinish(result) + } + } + + protected open fun onFinish(result: Result) { + if (result.isSuccess) { with(AlarmHandler(context)) { scheduleAlarm(alarm) } - } + } else Timber.e( + result.exceptionOrNull(), + "Unhandled exception on worker class" + ) } private fun getString(@StringRes resId: Int): String = diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/utils/Analytics.kt b/app/src/main/java/com/javinator9889/handwashingreminder/utils/Analytics.kt new file mode 100644 index 0000000..7fb1ab7 --- /dev/null +++ b/app/src/main/java/com/javinator9889/handwashingreminder/utils/Analytics.kt @@ -0,0 +1,32 @@ +/* + * Copyright © 2020 - present | Handwashing reminder by Javinator9889 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + * + * Created by Javinator9889 on 10/12/20 - Handwashing reminder. + */ +package com.javinator9889.handwashingreminder.utils + +import android.os.Bundle +import com.google.firebase.analytics.FirebaseAnalytics +import kotlin.reflect.KClass + + +fun FirebaseAnalytics.setCurrentScreen(name: String?, cls: KClass<*>?) { + with(Bundle(2)) { + putString(FirebaseAnalytics.Param.SCREEN_NAME, name) + putString(FirebaseAnalytics.Param.SCREEN_CLASS, cls?.simpleName) + logEvent(FirebaseAnalytics.Event.SCREEN_VIEW, this) + } +} diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/utils/calendar/Calendar.kt b/app/src/main/java/com/javinator9889/handwashingreminder/utils/calendar/Calendar.kt index 58f2a1d..eff8394 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/utils/calendar/Calendar.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/utils/calendar/Calendar.kt @@ -45,6 +45,9 @@ object CalendarUtils { return aMonthAgo } + val now: Long + get() = Calendar.getInstance().timeInMillis + fun timeBetweenIn( unit: TimeUnit, to: Long, @@ -55,4 +58,17 @@ object CalendarUtils { to: Long, from: Long = today.timeInMillis ): Long = from - to + + fun timeIn(amount: Int, unit: TimeUnit): Calendar = + with(Calendar.getInstance()) { + when (unit) { + TimeUnit.MILLISECONDS -> this[Calendar.MILLISECOND] += amount + TimeUnit.SECONDS -> this[Calendar.SECOND] += amount + TimeUnit.MINUTES -> this[Calendar.MINUTE] += amount + TimeUnit.HOURS -> this[Calendar.HOUR_OF_DAY] += amount + TimeUnit.DAYS -> this[Calendar.DAY_OF_MONTH] += amount + else -> Unit + } + this + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_update_black.xml b/app/src/main/res/drawable/ic_update_black.xml new file mode 100644 index 0000000..fdb2fca --- /dev/null +++ b/app/src/main/res/drawable/ic_update_black.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/loading_recycler_view.xml b/app/src/main/res/layout/loading_recycler_view.xml index 800d5f7..6426737 100644 --- a/app/src/main/res/layout/loading_recycler_view.xml +++ b/app/src/main/res/layout/loading_recycler_view.xml @@ -47,11 +47,31 @@ android:text="@string/news_error" android:textAppearance="@style/TextAppearance.AppCompat.Display1" android:textSize="18sp" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@+id/reloadText" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/errorAnimation" tools:text="Error while obtaining new data" /> + + - + @@ -64,7 +65,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/news_card_view.xml b/app/src/main/res/layout/news_card_view.xml index 46e81a7..e4ab83e 100644 --- a/app/src/main/res/layout/news_card_view.xml +++ b/app/src/main/res/layout/news_card_view.xml @@ -96,8 +96,9 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/splash_screen.xml b/app/src/main/res/layout/splash_screen.xml index b3e14dd..eda0a6e 100644 --- a/app/src/main/res/layout/splash_screen.xml +++ b/app/src/main/res/layout/splash_screen.xml @@ -19,7 +19,7 @@ Recordatorios en el desayuno + ¡Recibe una notificación cuando es la + hora del desayuno! ☕ Recordatorios en la comida + ¡Recibe una notificación cuando es + la hora de la comida! 🍱 Recordatorios en la cenas + ¡Recibe una notificación cuando es la hora + de la cena! 🥗 ¡Hora de comer y lavarse las manos! 🍱 @@ -297,4 +303,5 @@ Ajustes de notificaciones ¡Acabo de lavarlas! ¡Yupiiii! 🙌😱 + Pulsa aquí para intentarlo de nuevo diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5812a91..d257f50 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -70,6 +70,8 @@ hands 😉💦👏 Breakfast reminders + Get a notification when it\'s breakfast + time! ☕ Time to lunch and wash your hands! 🍱 @@ -81,6 +83,8 @@ wash your hands before having lunch 💦👏 Lunch reminders + Get a notification when it\'s lunch + time! 🍱 Let\'s have a clean dinner 🥗 The day is finishing and you can do it fully by @@ -91,6 +95,8 @@ more than 15 diseases? That\'s a lot 😮 Dinner reminders + Get a notification when it\'s dinner + time! 🥗 Let\'s learn how to wash our hands Join me in this trip @@ -325,4 +331,5 @@ Edit notification settings Just washed them! Hurray! 🙌😱 + Click here to try again diff --git a/appintro/build.gradle b/appintro/build.gradle index 9bfb7e5..e20781f 100644 --- a/appintro/build.gradle +++ b/appintro/build.gradle @@ -1,16 +1,19 @@ apply plugin: 'com.android.dynamic-feature' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' android { - buildFeatures.viewBinding = true - compileSdkVersion 29 + buildFeatures { + viewBinding true + dataBinding true + } + compileSdkVersion 30 android.defaultConfig.vectorDrawables.useSupportLibrary = true defaultConfig { - minSdkVersion 17 - targetSdkVersion 29 + minSdkVersion 16 + targetSdkVersion 30 versionCode 1 versionName "1.0" } diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt index 33c9e43..7f35f22 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt @@ -45,16 +45,12 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.perf.FirebasePerformance import com.javinator9889.handwashingreminder.activities.MainActivity import com.javinator9889.handwashingreminder.appintro.custom.SliderPageBuilder -import com.javinator9889.handwashingreminder.appintro.fragments.AnimatedAppIntro -import com.javinator9889.handwashingreminder.appintro.fragments.SlidePolicyFragment -import com.javinator9889.handwashingreminder.appintro.fragments.TimeConfigIntroFragment -import com.javinator9889.handwashingreminder.appintro.fragments.TimeContainer +import com.javinator9889.handwashingreminder.appintro.fragments.* import com.javinator9889.handwashingreminder.appintro.timeconfig.TimeConfigItem import com.javinator9889.handwashingreminder.appintro.utils.AnimatedResources import com.javinator9889.handwashingreminder.gms.activity.ActivityHandler import com.javinator9889.handwashingreminder.jobs.alarms.AlarmHandler import com.javinator9889.handwashingreminder.utils.* -import kotlinx.android.synthetic.main.animated_intro.* import kotlinx.coroutines.* import timber.log.Timber import com.javinator9889.handwashingreminder.appintro.R as RIntro @@ -82,9 +78,10 @@ class IntroActivity : AppIntro2(), with(FirebaseAnalytics.getInstance(this)) { logEvent(FirebaseAnalytics.Event.TUTORIAL_BEGIN, null) - setCurrentScreen(this@IntroActivity, "Intro", null) + setCurrentScreen("Intro", this@IntroActivity::class) } + Timber.d("Creating slides...") val firstSlide = SliderPageBuilder.Builder() .title(getString(RIntro.string.first_slide_title)) .description(getString(RIntro.string.first_slide_desc)) @@ -149,6 +146,7 @@ class IntroActivity : AppIntro2(), backButtonVisibilityWithDone = true setIndicatorColor(Color.DKGRAY, Color.GRAY) nextButton.setOnClickListener(this) + Timber.d("Finished activity creation") } override fun onSaveInstanceState(outState: Bundle) { @@ -181,6 +179,7 @@ class IntroActivity : AppIntro2(), sharedPreferences.edit { timeConfigSlide.itemAdapter.adapterItems.forEach { item -> val time = "${item.hours}:${item.minutes}" + Timber.d("Saving $item with ID ${item.id} and time $time") when (item.id) { TimeConfig.BREAKFAST_ID -> putString(Preferences.BREAKFAST_TIME, time) @@ -255,9 +254,8 @@ class IntroActivity : AppIntro2(), ) if (!policySlide.firebaseAnalytics.isChecked) { firebaseAnalytics.setCurrentScreen( - this@IntroActivity, null, - null + this@IntroActivity::class ) firebaseAnalytics.setAnalyticsCollectionEnabled(false) } @@ -334,7 +332,7 @@ class IntroActivity : AppIntro2(), if (newFragment is AnimatedAppIntro || newFragment is SlidePolicyFragment ) - newFragment.image.playAnimation() + (newFragment as ImageFragment).image.playAnimation() super.onSlideChanged(oldFragment, newFragment) } diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/AnimatedAppIntro.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/AnimatedAppIntro.kt index bf73b9e..e04ab3f 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/AnimatedAppIntro.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/AnimatedAppIntro.kt @@ -38,12 +38,14 @@ import com.github.paolorotolo.appintro.util.TypefaceContainer import com.google.android.play.core.splitcompat.SplitCompat import com.javinator9889.handwashingreminder.appintro.R import com.javinator9889.handwashingreminder.appintro.custom.* +import com.javinator9889.handwashingreminder.appintro.databinding.AnimatedIntroBinding class AnimatedAppIntro : Fragment(), ISlideSelectionListener, - ISlideBackgroundColorHolder/*, - LottieOnCompositionLoadedListener*/ { + ISlideBackgroundColorHolder, + IFragmentBinder, + ImageFragment { private var drawable = 0 private var bgColor = 0 private var titleColor = 0 @@ -52,6 +54,11 @@ class AnimatedAppIntro : @get:LayoutRes protected val layoutId: Int = R.layout.animated_intro private var title: String? = null + override var _binding: AnimatedIntroBinding? = null + private lateinit var _image: LottieAnimationView + override var image: LottieAnimationView + get() = _image + set(value) {_image = value} @RawRes private var animatedDrawable: Int? = null @@ -64,7 +71,7 @@ class AnimatedAppIntro : override fun onAttach(context: Context) { super.onAttach(context) - SplitCompat.installActivity(activity) + activity?.let { SplitCompat.installActivity(it) } } override fun onCreate(savedInstanceState: Bundle?) { @@ -125,12 +132,12 @@ class AnimatedAppIntro : savedInstanceState: Bundle? ): View? { val view = inflater.inflate(layoutId, container, false) + _binding = AnimatedIntroBinding.bind(view) + image = binding.image val titleText = view.findViewById(R.id.title) val descriptionText = view.findViewById(R.id.description) val slideImage = view.findViewById(R.id.image) mainLayout = view.findViewById(R.id.main) -// slideImage.addLottieOnCompositionLoadedListener(this) -// slideImage.enableMergePathsForKitKatAndAbove(true) titleText.text = title if (titleColor != 0) { titleText.setTextColor(titleColor) diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/IFragmentBinder.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/IFragmentBinder.kt new file mode 100644 index 0000000..46d3cf6 --- /dev/null +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/IFragmentBinder.kt @@ -0,0 +1,26 @@ +/* + * Copyright © 2020 - present | Handwashing reminder by Javinator9889 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + * + * Created by Javinator9889 on 7/12/20 - Handwashing reminder. + */ +package com.javinator9889.handwashingreminder.appintro.fragments + +import androidx.viewbinding.ViewBinding + +interface IFragmentBinder { + var _binding: T? + val binding: T get() = _binding!! +} \ No newline at end of file diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/ImageFragment.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/ImageFragment.kt new file mode 100644 index 0000000..ad61de8 --- /dev/null +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/ImageFragment.kt @@ -0,0 +1,25 @@ +/* + * Copyright © 2020 - present | Handwashing reminder by Javinator9889 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + * + * Created by Javinator9889 on 7/12/20 - Handwashing reminder. + */ +package com.javinator9889.handwashingreminder.appintro.fragments + +import com.airbnb.lottie.LottieAnimationView + +interface ImageFragment { + var image: LottieAnimationView +} \ No newline at end of file diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/SlidePolicyFragment.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/SlidePolicyFragment.kt index 084e94d..92f437f 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/SlidePolicyFragment.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/SlidePolicyFragment.kt @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout +import com.airbnb.lottie.LottieAnimationView import com.github.paolorotolo.appintro.AppIntroBaseFragment import com.github.paolorotolo.appintro.ISlidePolicy import com.google.android.material.checkbox.MaterialCheckBox @@ -34,12 +35,13 @@ import com.google.android.play.core.splitcompat.SplitCompat import com.javinator9889.handwashingreminder.activities.PrivacyTermsActivity import com.javinator9889.handwashingreminder.appintro.R import com.javinator9889.handwashingreminder.appintro.custom.ARG_ANIM_DRAWABLE +import com.javinator9889.handwashingreminder.appintro.databinding.SlidePolicyBinding import com.javinator9889.handwashingreminder.appintro.utils.AnimatedResources import com.javinator9889.handwashingreminder.utils.notNull -import kotlinx.android.synthetic.main.slide_policy.view.* import com.javinator9889.handwashingreminder.R as RBase -class SlidePolicyFragment : AppIntroBaseFragment(), ISlidePolicy { +class SlidePolicyFragment : AppIntroBaseFragment(), ISlidePolicy, + IFragmentBinder, ImageFragment { companion object { const val FIREBASE_ANALYTICS_CHECKED = "switch:fa:status" const val FIREBASE_PERFORMANCE_CHECKED = "switch:fp:status" @@ -51,6 +53,11 @@ class SlidePolicyFragment : AppIntroBaseFragment(), ISlidePolicy { lateinit var firebasePerformance: SwitchMaterial private lateinit var slidePolicyCheckBox: MaterialCheckBox private var wasPolicyActivityLaunched = false + override var _binding: SlidePolicyBinding? = null + private lateinit var _image: LottieAnimationView + override var image: LottieAnimationView + get() = _image + set(value) {_image = value} var title: String? = null var titleColor: Int? = null var imageDrawable: Int? = null @@ -60,7 +67,7 @@ class SlidePolicyFragment : AppIntroBaseFragment(), ISlidePolicy { override fun onAttach(context: Context) { super.onAttach(context) - SplitCompat.installActivity(activity) + activity?.let { SplitCompat.installActivity(it) } } override fun onCreateView( @@ -69,13 +76,15 @@ class SlidePolicyFragment : AppIntroBaseFragment(), ISlidePolicy { savedInstanceState: Bundle? ): View? { val view = inflater.inflate(layoutId, container, false) - firebaseAnalytics = view.analyticsSwitch - firebasePerformance = view.performanceSwitch - slidePolicyCheckBox = view.policyCheckbox - layout = view.main + _binding = SlidePolicyBinding.bind(view) + image = binding.image + firebaseAnalytics = binding.analyticsSwitch + firebasePerformance = binding.performanceSwitch + slidePolicyCheckBox = binding.policyCheckbox + layout = binding.main - val image = view.image - val title = view.title + val image = binding.image + val title = binding.title this.title?.let { title.text = it } this.titleColor?.let { title.setTextColor(it) } bgColor?.let { layout.setBackgroundColor(it) } diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeConfigIntroFragment.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeConfigIntroFragment.kt index 0f6a5ef..64bef6e 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeConfigIntroFragment.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeConfigIntroFragment.kt @@ -34,6 +34,8 @@ import androidx.recyclerview.widget.RecyclerView import com.github.paolorotolo.appintro.AppIntroBaseFragment import com.javinator9889.handwashingreminder.appintro.R import com.javinator9889.handwashingreminder.appintro.TIME_CONFIG_REQUEST_CODE +import com.javinator9889.handwashingreminder.appintro.databinding.TimeCardViewBinding +import com.javinator9889.handwashingreminder.appintro.databinding.TimeConfigBinding import com.javinator9889.handwashingreminder.appintro.timeconfig.TimeConfigActivity import com.javinator9889.handwashingreminder.appintro.timeconfig.TimeConfigItem import com.javinator9889.handwashingreminder.utils.AndroidVersion @@ -43,8 +45,6 @@ import com.javinator9889.handwashingreminder.utils.isViewVisible import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.adapters.ItemAdapter import com.mikepenz.fastadapter.listeners.ClickEventHook -import kotlinx.android.synthetic.main.time_card_view.view.* -import kotlinx.android.synthetic.main.time_config.view.* class TimeConfigIntroFragment : AppIntroBaseFragment() { var bgColor: Int = Color.WHITE @@ -53,6 +53,7 @@ class TimeConfigIntroFragment : AppIntroBaseFragment() { lateinit var recyclerView: RecyclerView lateinit var fastAdapter: FastAdapter lateinit var itemAdapter: ItemAdapter + internal lateinit var binding: TimeConfigBinding init { propertyContainer[TimeConfig.BREAKFAST_ID.toInt()] = TimeContainer() @@ -95,13 +96,17 @@ class TimeConfigIntroFragment : AppIntroBaseFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View = inflater.inflate(layoutId, container, false) + ): View { + val view = inflater.inflate(layoutId, container, false) + binding = TimeConfigBinding.bind(view) + return view + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val manager = LinearLayoutManager(context) fastAdapter = FastAdapter.with(itemAdapter) - recyclerView = view.cardsView.apply { + recyclerView = binding.cardsView.apply { setHasFixedSize(true) layoutManager = manager adapter = fastAdapter @@ -120,15 +125,21 @@ class TimeConfigIntroFragment : AppIntroBaseFragment() { item: TimeConfigItem ) { val intent = Intent(context, TimeConfigActivity::class.java) + val cardBinding = TimeCardViewBinding.bind(v) val options = if (isAtLeast(AndroidVersion.LOLLIPOP)) { val pairs = mutableListOf>() val items = HashMap(6).apply { - this[TimeConfigActivity.VIEW_TITLE_NAME] = v.title - this[TimeConfigActivity.INFO_IMAGE_NAME] = v.infoImage - this[TimeConfigActivity.USER_TIME_ICON] = v.clockIcon - this[TimeConfigActivity.USER_TIME_HOURS] = v.hours - this[TimeConfigActivity.USER_DDOT] = v.ddot - this[TimeConfigActivity.USER_TIME_MINUTES] = v.minutes + this[TimeConfigActivity.VIEW_TITLE_NAME] = + cardBinding.title + this[TimeConfigActivity.INFO_IMAGE_NAME] = + cardBinding.infoImage + this[TimeConfigActivity.USER_TIME_ICON] = + cardBinding.clockIcon + this[TimeConfigActivity.USER_TIME_HOURS] = + cardBinding.hours + this[TimeConfigActivity.USER_DDOT] = cardBinding.ddot + this[TimeConfigActivity.USER_TIME_MINUTES] = + cardBinding.minutes } items.onEach { if (it.value.isViewVisible(recyclerView)) @@ -142,9 +153,9 @@ class TimeConfigIntroFragment : AppIntroBaseFragment() { null } intent.apply { - putExtra("title", v.title.text) - putExtra("hours", v.hours.text) - putExtra("minutes", v.minutes.text) + putExtra("title", cardBinding.title.text) + putExtra("hours", cardBinding.hours.text) + putExtra("minutes", cardBinding.minutes.text) putExtra("id", item.id) putExtra("position", position) } @@ -160,5 +171,3 @@ class TimeConfigIntroFragment : AppIntroBaseFragment() { override fun getLayoutId(): Int = R.layout.time_config } - -data class TimeContainer(val hours: String? = "", val minutes: String? = "") \ No newline at end of file diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeContainer.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeContainer.kt new file mode 100644 index 0000000..05ac05a --- /dev/null +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeContainer.kt @@ -0,0 +1,21 @@ +/* + * Copyright © 2020 - present | Handwashing reminder by Javinator9889 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + * + * Created by Javinator9889 on 7/12/20 - Handwashing reminder. + */ +package com.javinator9889.handwashingreminder.appintro.fragments + +data class TimeContainer(val hours: String? = "", val minutes: String? = "") diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigActivity.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigActivity.kt index 865c61f..e35e3d3 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigActivity.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigActivity.kt @@ -30,10 +30,11 @@ import android.widget.TextView import android.widget.TimePicker import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.ViewCompat -import coil.api.load +import coil.load import com.google.android.play.core.splitcompat.SplitCompat import com.javinator9889.handwashingreminder.activities.support.ActionBarBase import com.javinator9889.handwashingreminder.appintro.R +import com.javinator9889.handwashingreminder.appintro.databinding.TimeCardViewExpandedBinding import com.javinator9889.handwashingreminder.utils.AndroidVersion import com.javinator9889.handwashingreminder.utils.TimeConfig import com.javinator9889.handwashingreminder.utils.formatTime @@ -43,7 +44,7 @@ import java.util.* import kotlin.properties.Delegates class TimeConfigActivity : - ActionBarBase(), + ActionBarBase(), View.OnClickListener, TimePickerDialog.OnTimeSetListener { companion object Transitions { @@ -95,22 +96,28 @@ class TimeConfigActivity : ddot.setOnClickListener(this) minutes.setOnClickListener(this) clockIcon.setOnClickListener(this) - ViewCompat.setTransitionName(title, + ViewCompat.setTransitionName( + title, VIEW_TITLE_NAME ) - ViewCompat.setTransitionName(image, + ViewCompat.setTransitionName( + image, INFO_IMAGE_NAME ) - ViewCompat.setTransitionName(hours, + ViewCompat.setTransitionName( + hours, USER_TIME_HOURS ) - ViewCompat.setTransitionName(ddot, + ViewCompat.setTransitionName( + ddot, USER_DDOT ) - ViewCompat.setTransitionName(minutes, + ViewCompat.setTransitionName( + minutes, USER_TIME_MINUTES ) - ViewCompat.setTransitionName(clockIcon, + ViewCompat.setTransitionName( + clockIcon, USER_TIME_ICON ) @@ -207,4 +214,8 @@ class TimeConfigActivity : hours.text = formatTime(hourOfDay) minutes.text = formatTime(minute) } + + override fun inflateLayout(): TimeCardViewExpandedBinding = + TimeCardViewExpandedBinding.inflate(layoutInflater) + .also { binding = it } } \ No newline at end of file diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt index 59016de..a08a715 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/timeconfig/TimeConfigItem.kt @@ -23,7 +23,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.annotation.LayoutRes import androidx.cardview.widget.CardView -import coil.api.load +import coil.load import com.javinator9889.handwashingreminder.appintro.R import com.javinator9889.handwashingreminder.utils.TimeConfig import com.javinator9889.handwashingreminder.utils.notNull diff --git a/appintro/src/main/res/layout/time_card_view.xml b/appintro/src/main/res/layout/time_card_view.xml index 9b95689..77f3007 100644 --- a/appintro/src/main/res/layout/time_card_view.xml +++ b/appintro/src/main/res/layout/time_card_view.xml @@ -1,152 +1,146 @@ - + android:layout_margin="8dp" + android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:checkable="true" + android:clickable="true" + android:focusable="true" + android:minHeight="0dp" + android:padding="8dp" + android:visibility="visible" + app:cardCornerRadius="16dp" + app:cardElevation="4dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent"> - + android:layout_height="wrap_content"> + + - + android:layout_height="match_parent" + android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:layout_marginBottom="16dp" + android:orientation="horizontal" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/title"> - + + + android:layout_marginRight="16dp" + android:gravity="center_vertical"> - - - - - - - + android:layout_marginEnd="8dp" + android:tint="@color/dkgray" + app:iiv_color="@color/dkgray" + app:iiv_icon="ion_android_time" + app:iiv_size="16dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/hours" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - - - + - - + - + + - + - + - + diff --git a/build.gradle b/build.gradle index 68d64ad..0d21a1e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,26 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' - ext.latestAboutLibsRelease = '8.2.0' - ext.latestFastAdapterRelease = '5.1.0' + ext.kotlin_version = '1.4.21' + ext.latestAboutLibsRelease = '8.6.3' + ext.latestFastAdapterRelease = '5.3.2' repositories { google() jcenter() maven { url 'https://maven.fabric.io/public' } + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}" - classpath 'com.google.gms:google-services:4.3.3' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0' - classpath 'com.google.firebase:perf-plugin:1.3.1' // Performance Monitoring plugin + classpath 'com.google.gms:google-services:4.3.4' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' + classpath 'com.google.firebase:perf-plugin:1.3.4' // Performance Monitoring plugin // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/bundledemoji/build.gradle b/bundledemoji/build.gradle index 51644f3..51a3752 100644 --- a/bundledemoji/build.gradle +++ b/bundledemoji/build.gradle @@ -1,14 +1,19 @@ apply plugin: 'com.android.dynamic-feature' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' android { - compileSdkVersion 29 + buildFeatures { + viewBinding true + dataBinding true + } + + compileSdkVersion 30 defaultConfig { - minSdkVersion 17 - targetSdkVersion 29 + minSdkVersion 16 + targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/extras/23920-error-state-dog.json b/extras/23920-error-state-dog.json new file mode 100644 index 0000000..08f89b7 --- /dev/null +++ b/extras/23920-error-state-dog.json @@ -0,0 +1 @@ +{"v":"5.2.1","fr":60,"ip":0,"op":360,"w":1080,"h":1080,"nm":"Dog swimming","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Dog swimming Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,516,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-8.06,11.343],[-11.941,-35.223],[-16.119,-22.985],[28.488,-33.207],[17.611,8.657],[-5.075,48.953],[4.528,38.096]],"o":[[8.955,0.298],[11.939,35.222],[16.119,22.983],[-9.371,10.923],[-17.612,-8.656],[5.074,-48.954],[-3.178,-26.736]],"v":[[-39.699,-119.399],[9.852,-85.967],[47.163,-2.089],[47.163,91.937],[9.809,104.369],[-7.623,40.432],[-34.236,-72.736]],"c":true}],"e":[{"i":[[5.991,-0.491],[-11.941,-35.223],[-16.119,-22.985],[-3.121,-28.827],[-1.816,19.539],[36.665,51.678],[13.478,41.397]],"o":[[8.955,0.298],[11.939,35.222],[16.119,22.983],[4.709,43.498],[0.233,-2.509],[-8.038,-4.369],[-9.432,-28.969]],"v":[[-39.699,-119.399],[9.852,-85.967],[47.163,-2.089],[79.663,70.687],[38.559,92.869],[14.627,13.932],[-6.736,-71.236]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[5.991,-0.491],[-11.941,-35.223],[-16.119,-22.985],[-3.121,-28.827],[-1.816,19.539],[36.665,51.678],[13.478,41.397]],"o":[[8.955,0.298],[11.939,35.222],[16.119,22.983],[4.709,43.498],[0.233,-2.509],[-8.038,-4.369],[-9.432,-28.969]],"v":[[-39.699,-119.399],[9.852,-85.967],[47.163,-2.089],[79.663,70.687],[38.559,92.869],[14.627,13.932],[-6.736,-71.236]],"c":true}],"e":[{"i":[[-8.06,11.343],[-11.941,-35.223],[-16.119,-22.985],[28.488,-33.207],[17.611,8.657],[-5.075,48.953],[4.528,38.096]],"o":[[8.955,0.298],[11.939,35.222],[16.119,22.983],[-9.371,10.923],[-17.612,-8.656],[5.074,-48.954],[-3.178,-26.736]],"v":[[-39.699,-119.399],[9.852,-85.967],[47.163,-2.089],[47.163,91.937],[9.809,104.369],[-7.623,40.432],[-34.236,-72.736]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.552999997606,0.463000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[659.708,424.639],"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 14","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-18.507,15.785],[-12.701,0],[-6.895,-8.738],[29.619,0]],"o":[[6.23,-5.313],[12.7,0],[6.895,8.738],[-29.619,0]],"v":[[-63.756,-6.071],[-23.114,-10.863],[21.519,-1.561],[-20.348,16.48]],"c":true}],"e":[{"i":[[-35.58,30.35],[-24.418,0],[-13.255,-16.801],[56.945,0]],"o":[[11.977,-10.215],[24.417,0],[13.256,16.8],[-56.945,0]],"v":[[-78.101,-17.058],[0.037,-26.271],[85.847,-8.386],[5.355,26.298]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[-35.58,30.35],[-24.418,0],[-13.255,-16.801],[56.945,0]],"o":[[11.977,-10.215],[24.417,0],[13.256,16.8],[-56.945,0]],"v":[[-78.101,-17.058],[0.037,-26.271],[85.847,-8.386],[5.355,26.298]],"c":true}],"e":[{"i":[[-18.508,15.785],[-12.701,0],[-6.895,-8.738],[29.619,0]],"o":[[6.23,-5.313],[12.7,0],[6.895,8.738],[-29.619,0]],"v":[[-63.756,-6.071],[-23.114,-10.863],[21.519,-1.561],[-20.348,16.48]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6,0.677999997606,0.976000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[872.896,754.863],"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":60,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.44,10.815]],"o":[[0,0],[0,0]],"v":[[-0.026,10.194],[-2.72,-10.195]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156922583,0.580392156863,0.20000001496,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","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":[660.225,621.047],"e":[735.225,602.047],"to":[12.5,-3.16666674613953],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[735.225,602.047],"e":[660.225,621.047],"to":[0,0],"ti":[12.5,-3.16666674613953]},{"t":359}],"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,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.439,10.814]],"o":[[0,0],[0,0]],"v":[[-1.759,11.392],[-2.719,-11.393]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156922583,0.6,0.231372563979,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","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":[623.612,624.245],"e":[701.612,604.245],"to":[13,-3.33333325386047],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[701.612,604.245],"e":[623.612,624.245],"to":[0,0],"ti":[13,-3.33333325386047]},{"t":359}],"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":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-8.523,10.648]],"o":[[0,0],[0,0]],"v":[[-10.026,18.194],[-4.22,-6.195]],"c":false}],"e":[{"i":[[0,0],[5.439,10.815]],"o":[[0,0],[0,0]],"v":[[-0.026,10.194],[-2.72,-10.195]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[5.439,10.815]],"o":[[0,0],[0,0]],"v":[[-0.026,10.194],[-2.72,-10.195]],"c":false}],"e":[{"i":[[0,0],[-8.523,10.648]],"o":[[0,0],[0,0]],"v":[[-10.026,18.194],[-4.22,-6.195]],"c":false}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156922817,0.580392181873,0.200000017881,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","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":[457.742,613.547],"e":[515.242,603.047],"to":[9.58333301544189,-1.75],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[515.242,603.047],"e":[457.742,613.547],"to":[0,0],"ti":[9.58333301544189,-1.75]},{"t":359}],"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":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-7.409,10.147]],"o":[[0,0],[0,0]],"v":[[-69.76,13.392],[-65.72,-8.893]],"c":false}],"e":[{"i":[[0,0],[5.44,10.814]],"o":[[0,0],[0,0]],"v":[[-1.76,11.392],[-2.72,-11.393]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[5.44,10.814]],"o":[[0,0],[0,0]],"v":[[-1.76,11.392],[-2.72,-11.393]],"c":false}],"e":[{"i":[[0,0],[-7.409,10.147]],"o":[[0,0],[0,0]],"v":[[-69.76,13.392],[-65.72,-8.893]],"c":false}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.992156922817,0.580392181873,0.200000017881,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[481.629,604.245],"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":2,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[2.194,-16.08]],"o":[[0,0],[0,0]],"v":[[-101.191,1.64],[-102.597,32.36]],"c":false}],"e":[{"i":[[0,0],[2.194,-16.08]],"o":[[0,0],[0,0]],"v":[[0.309,-15.36],[-1.097,15.36]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[2.194,-16.08]],"o":[[0,0],[0,0]],"v":[[0.309,-15.36],[-1.097,15.36]],"c":false}],"e":[{"i":[[0,0],[2.194,-16.08]],"o":[[0,0],[0,0]],"v":[[-101.191,1.64],[-102.597,32.36]],"c":false}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.109999997008,0.19199999641,0.46699999641,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[627.113,496.521],"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":2,"cix":2,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-2.399,14.16],[-3.32,-7.2],[10.559,0]],"o":[[0.916,-5.401],[3.321,7.2],[-10.561,0]],"v":[[-121.101,-5.74],[-78.821,-5.74],[-96.38,19.94]],"c":true}],"e":[{"i":[[-2.399,14.16],[-3.32,-7.2],[10.559,0]],"o":[[0.916,-5.401],[3.321,7.2],[-10.561,0]],"v":[[-21.601,-9.24],[20.679,-9.24],[3.12,16.44]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[-2.399,14.16],[-3.32,-7.2],[10.559,0]],"o":[[0.916,-5.401],[3.321,7.2],[-10.561,0]],"v":[[-21.601,-9.24],[20.679,-9.24],[3.12,16.44]],"c":true}],"e":[{"i":[[-2.399,14.16],[-3.32,-7.2],[10.559,0]],"o":[[0.916,-5.401],[3.321,7.2],[-10.561,0]],"v":[[-121.101,-5.74],[-78.821,-5.74],[-96.38,19.94]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.109999997008,0.19199999641,0.46699999641,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[623.29,480.081],"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,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0.603,4.731],[5.963,2.379],[1.536,-7.998],[-5.111,0.295]],"o":[[-0.549,-4.31],[-5.962,-2.379],[-1.537,7.999],[5.111,-0.295]],"v":[[-44.114,3.099],[-61.539,-5.873],[-76.655,0.409],[-58.093,10.788]],"c":true}],"e":[{"i":[[-0.429,4.75],[5.311,3.607],[3.221,-7.48],[-5.055,-0.812]],"o":[[0.391,-4.327],[-5.31,-3.606],[-3.222,7.481],[5.055,0.812]],"v":[[17.016,5.899],[1.93,-6.612],[-14.184,-3.732],[1.709,10.399]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[-0.429,4.75],[5.311,3.607],[3.221,-7.48],[-5.055,-0.812]],"o":[[0.391,-4.327],[-5.31,-3.606],[-3.222,7.481],[5.055,0.812]],"v":[[17.016,5.899],[1.93,-6.612],[-14.184,-3.732],[1.709,10.399]],"c":true}],"e":[{"i":[[0.603,4.731],[5.963,2.379],[1.536,-7.998],[-5.111,0.295]],"o":[[-0.549,-4.31],[-5.962,-2.379],[-1.537,7.999],[5.111,-0.295]],"v":[[-44.114,3.099],[-61.539,-5.873],[-76.655,0.409],[-58.093,10.788]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.552999997606,0.463000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[639.903,387.207],"e":[640.403,398.707],"to":[0.08333333581686,1.91666662693024],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":190,"s":[640.403,398.707],"e":[639.903,387.207],"to":[0,0],"ti":[0.08333333581686,1.91666662693024]},{"t":200}],"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":2,"cix":2,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-0.898,5.186],[-7.112,1.874],[-1.489,-9.099],[6.003,0.992]],"o":[[0.818,-4.724],[7.112,-1.874],[1.489,9.1],[-6.005,-0.992]],"v":[[-86.955,3.485],[-66.089,-4.233],[-48.546,4.721],[-70.807,13.858]],"c":true}],"e":[{"i":[[1.06,5.155],[-5.935,4.344],[-4.713,-7.924],[5.95,-1.273]],"o":[[-0.966,-4.696],[5.934,-4.345],[4.714,7.925],[-5.952,1.273]],"v":[[-19.974,7.833],[-3.376,-6.981],[16.226,-5.064],[-1.151,11.582]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[1.06,5.155],[-5.935,4.344],[-4.713,-7.924],[5.95,-1.273]],"o":[[-0.966,-4.696],[5.934,-4.345],[4.714,7.925],[-5.952,1.273]],"v":[[-19.974,7.833],[-3.376,-6.981],[16.226,-5.064],[-1.151,11.582]],"c":true}],"e":[{"i":[[-0.898,5.186],[-7.112,1.874],[-1.489,-9.099],[6.003,0.992]],"o":[[0.818,-4.724],[7.112,-1.874],[1.489,9.1],[-6.005,-0.992]],"v":[[-86.955,3.485],[-66.089,-4.233],[-48.546,4.721],[-70.807,13.858]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.552999997606,0.463000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[538.372,387.533],"e":[538.372,401.033],"to":[0,2.25],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":190,"s":[538.372,401.033],"e":[538.372,387.533],"to":[0,0],"ti":[0,2.25]},{"t":200}],"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":2,"cix":2,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[-59.54,-3],[-72.5,9.96],[-85.46,-3],[-72.5,-15.96]],"c":true}],"e":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[12.96,0],[0,12.96],[-12.96,0],[0,-12.96]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[12.96,0],[0,12.96],[-12.96,0],[0,-12.96]],"c":true}],"e":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[-59.54,-3],[-72.5,9.96],[-85.46,-3],[-72.5,-15.96]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.109999997008,0.19199999641,0.46699999641,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[643.97,428.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":180,"s":[100,100],"e":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":190,"s":[100,0],"e":[100,100]},{"t":200}],"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":2,"cix":2,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[-51.54,-3.5],[-64.5,9.46],[-77.46,-3.5],[-64.5,-16.46]],"c":true}],"e":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[12.96,0],[0,12.96],[-12.96,0],[0,-12.96]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[12.96,0],[0,12.96],[-12.96,0],[0,-12.96]],"c":true}],"e":[{"i":[[0,-7.158],[7.158,0],[0,7.158],[-7.157,0]],"o":[[0,7.158],[-7.157,0],[0,-7.158],[7.158,0]],"v":[[-51.54,-3.5],[-64.5,9.46],[-77.46,-3.5],[-64.5,-16.46]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.109999997008,0.19199999641,0.46699999641,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[538.689,427.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":180,"s":[100,100],"e":[100,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":190,"s":[100,0],"e":[100,100]},{"t":200}],"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 11","np":2,"cix":2,"ix":12,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-11.81,22.259],[12.801,-37.76],[14.09,-26.593],[-18.19,-34.801],[-18.911,9.216],[-36.25,82.919],[1.92,28.8]],"o":[[-9.6,0.32],[-12.8,37.76],[-16.69,31.499],[8.201,15.69],[27.089,-13.201],[14.535,-33.249],[-1.919,-28.8]],"v":[[42.56,-128],[4.94,-76.66],[-15.06,-2.24],[-29.06,98.56],[9.161,111.46],[31,16.84],[37,-72.44]],"c":true}],"e":[{"i":[[8.641,12.16],[12.801,-37.76],[17.28,-24.64],[-34.88,-31.36],[-18.88,9.28],[5.44,52.48],[1.92,28.8]],"o":[[-9.6,0.32],[-12.8,37.76],[-17.279,24.64],[34.881,31.36],[18.879,-9.28],[-5.44,-52.48],[-1.919,-28.8]],"v":[[42.56,-128],[-10.56,-92.16],[-50.56,-2.24],[-50.56,98.56],[44.161,112.96],[80,35.84],[56,-77.44]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[8.641,12.16],[12.801,-37.76],[17.28,-24.64],[-34.88,-31.36],[-18.88,9.28],[5.44,52.48],[1.92,28.8]],"o":[[-9.6,0.32],[-12.8,37.76],[-17.279,24.64],[34.881,31.36],[18.879,-9.28],[-5.44,-52.48],[-1.919,-28.8]],"v":[[42.56,-128],[-10.56,-92.16],[-50.56,-2.24],[-50.56,98.56],[44.161,112.96],[80,35.84],[56,-77.44]],"c":true}],"e":[{"i":[[-11.81,22.259],[12.801,-37.76],[14.09,-26.593],[-18.19,-34.801],[-18.911,9.216],[-36.25,82.919],[1.92,28.8]],"o":[[-9.6,0.32],[-12.8,37.76],[-16.69,31.499],[8.201,15.69],[27.089,-13.201],[14.535,-33.249],[-1.919,-28.8]],"v":[[42.56,-128],[4.94,-76.66],[-15.06,-2.24],[-29.06,98.56],[9.161,111.46],[31,16.84],[37,-72.44]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.552999997606,0.463000009574,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[398.25,433.241],"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 12","np":2,"cix":2,"ix":13,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-44.209,1.799],[-17.28,-23.52],[-21.45,-57.281],[-1.39,-13.853],[59.37,-8.121],[6.971,4.199],[43.044,16.239],[-9.269,19.759],[-21.515,28.482]],"o":[[46.707,-1.901],[17.281,23.52],[6.024,16.085],[6.05,60.279],[-29.418,4.024],[-15.529,1.199],[-49.729,-18.761],[19.22,-40.97],[35.723,-9.784]],"v":[[-28.268,-140.65],[61.177,-106.789],[103.698,-23.609],[121.698,37.831],[33.378,141.23],[-35.223,129.91],[-121.023,137.62],[-135.483,28.85],[-123.443,-116.65]],"c":true}],"e":[{"i":[[-44.209,1.799],[-17.28,-23.52],[-2.16,-17.04],[-8.16,-11.28],[16.08,-24.96],[27.84,13.68],[45.36,7.68],[11.054,77.374],[-21.515,28.482]],"o":[[46.707,-1.901],[17.281,23.52],[2.16,17.04],[8.749,12.095],[-16.081,24.96],[-28.08,11.04],[-45.359,-7.68],[-6.399,-44.8],[35.723,-9.784]],"v":[[-28.268,-140.65],[73.677,-101.289],[103.198,-3.609],[121.198,33.831],[128.878,108.23],[58.777,125.91],[-38.523,134.87],[-138.483,11.35],[-123.443,-116.65]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[-44.209,1.799],[-17.28,-23.52],[-2.16,-17.04],[-8.16,-11.28],[16.08,-24.96],[27.84,13.68],[45.36,7.68],[11.054,77.374],[-21.515,28.482]],"o":[[46.707,-1.901],[17.281,23.52],[2.16,17.04],[8.749,12.095],[-16.081,24.96],[-28.08,11.04],[-45.359,-7.68],[-6.399,-44.8],[35.723,-9.784]],"v":[[-28.268,-140.65],[73.677,-101.289],[103.198,-3.609],[121.198,33.831],[128.878,108.23],[58.777,125.91],[-38.523,134.87],[-138.483,11.35],[-123.443,-116.65]],"c":true}],"e":[{"i":[[-44.209,1.799],[-17.28,-23.52],[-21.45,-57.281],[-1.39,-13.853],[59.37,-8.121],[6.971,4.199],[43.044,16.239],[-9.269,19.759],[-21.515,28.482]],"o":[[46.707,-1.901],[17.281,23.52],[6.024,16.085],[6.05,60.279],[-29.418,4.024],[-15.529,1.199],[-49.729,-18.761],[19.22,-40.97],[35.723,-9.784]],"v":[[-28.268,-140.65],[61.177,-106.789],[103.698,-23.609],[121.698,37.831],[33.378,141.23],[-35.223,129.91],[-121.023,137.62],[-135.483,28.85],[-123.443,-116.65]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.995999983245,0.760999971278,0.447000002394,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[564.252,421.89],"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 13","np":2,"cix":2,"ix":14,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-61.944,3.971],[-1.556,21.48],[-34.147,-23.348]],"c":true}],"e":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-71.488,-15.773],[8.894,20.108],[-48.691,-62.092]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":38,"s":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-71.488,-15.773],[8.894,20.108],[-48.691,-62.092]],"c":true}],"e":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-47.944,-9.029],[47.944,14.98],[-20.147,-36.348]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-47.944,-9.029],[47.944,14.98],[-20.147,-36.348]],"c":true}],"e":[{"i":[[0,0],[-61.7,32.678],[0,0]],"o":[[0,0],[-24.913,-44.001],[0,0]],"v":[[-61.944,3.971],[-1.556,21.48],[-34.147,-23.348]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078491211,0.678431372549,0.337254901961,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[489.267,540.931],"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 15","np":2,"cix":2,"ix":15,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[12.086,-16.079],[-11.965,-11.502],[-20.626,2.179],[5.38,41.823],[0,0]],"o":[[0,0],[-8.157,10.853],[11.965,11.502],[11.845,-1.251],[1.38,-22.677],[0,0]],"v":[[-57.6,-20],[-79.775,7.038],[-80.107,45.994],[-13.535,68.21],[3.93,17.636],[-0.32,-18.74]],"c":true}],"e":[{"i":[[0,0],[9.129,-15.522],[-12.749,-10.222],[-18.974,5.164],[9.108,37.182],[0,0]],"o":[[0,0],[-6.03,11.399],[12.749,10.223],[12.047,-4.432],[-5.465,-23.7],[0,0]],"v":[[-63.6,-66],[-71.581,7.68],[-68.465,45.91],[-1.22,61.816],[8.573,8.745],[-10.264,-55.234]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":38,"s":[{"i":[[0,0],[9.129,-15.522],[-12.749,-10.222],[-18.974,5.164],[9.108,37.182],[0,0]],"o":[[0,0],[-6.03,11.399],[12.749,10.223],[12.047,-4.432],[-5.465,-23.7],[0,0]],"v":[[-63.6,-66],[-71.581,7.68],[-68.465,45.91],[-1.22,61.816],[8.573,8.745],[-10.264,-55.234]],"c":true}],"e":[{"i":[[0,0],[-1.92,-13.44],[-15.68,-5.44],[-12.8,16.32],[23.039,19.839],[0,0]],"o":[[0,0],[1.92,13.44],[15.68,5.44],[12.8,-16.32],[-31.041,-27.52],[0,0]],"v":[[-57.6,-20],[-40.96,10.08],[-24.96,45.6],[44.8,37.92],[25.921,-24.48],[-40.32,-54.24]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[-1.92,-13.44],[-15.68,-5.44],[-12.8,16.32],[23.039,19.839],[0,0]],"o":[[0,0],[1.92,13.44],[15.68,5.44],[12.8,-16.32],[-31.041,-27.52],[0,0]],"v":[[-57.6,-20],[-40.96,10.08],[-24.96,45.6],[44.8,37.92],[25.921,-24.48],[-40.32,-54.24]],"c":true}],"e":[{"i":[[0,0],[12.086,-16.079],[-11.965,-11.502],[-20.626,2.179],[5.38,41.823],[0,0]],"o":[[0,0],[-8.157,10.853],[11.965,11.502],[11.845,-1.251],[1.38,-22.677],[0,0]],"v":[[-57.6,-20],[-79.775,7.038],[-80.107,45.994],[-13.535,68.21],[3.93,17.636],[-0.32,-18.74]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.995999983245,0.760999971278,0.447000002394,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[483.69,570.041],"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 16","np":2,"cix":2,"ix":16,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-80.019,35.909],[-0.49,19.04],[-24.311,-13.48],[-81.671,11.35],[-79.856,24.697]],"c":true}],"e":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-49.17,32.086],[31.212,15.583],[-8.157,-23.448],[-50.517,6.187],[-48.763,20.631]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84,"s":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-49.17,32.086],[31.212,15.583],[-8.157,-23.448],[-50.517,6.187],[-48.763,20.631]],"c":true}],"e":[{"i":[[0,0],[12.819,24.122],[13.172,6.655],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-13.767,28.38],[67.443,11.731],[7.792,-31.623],[-29.817,0.682],[-18.624,16.689]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[12.819,24.122],[13.172,6.655],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-13.767,28.38],[67.443,11.731],[7.792,-31.623],[-29.817,0.682],[-18.624,16.689]],"c":true}],"e":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-49.17,32.086],[31.212,15.583],[-8.157,-23.448],[-50.517,6.187],[-48.763,20.631]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":272,"s":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-49.17,32.086],[31.212,15.583],[-8.157,-23.448],[-50.517,6.187],[-48.763,20.631]],"c":true}],"e":[{"i":[[0,0],[12.819,24.122],[6.525,8.762],[0,0],[-0.759,-6.557]],"o":[[0,0],[-7.046,-13.258],[-50.502,2.503],[0,0],[0.82,7.085]],"v":[[-80.019,35.909],[-0.49,19.04],[-24.311,-13.48],[-81.671,11.35],[-79.856,24.697]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078491211,0.678431372549,0.337254901961,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[676.036,548.968],"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 17","np":2,"cix":2,"ix":17,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[10.787,-21.621],[-15.68,-5.44],[-12.799,16.32],[12.407,32.439],[0,0]],"o":[[0,0],[-6.061,12.148],[15.679,5.44],[12.8,-16.32],[-19.093,-41.561],[0,0]],"v":[[-57.6,-20],[-61.959,27.08],[-45.959,62.6],[23.8,54.92],[28.921,-1.48],[-16.32,-59.24]],"c":true}],"e":[{"i":[[0,0],[-1.92,-13.44],[-15.68,-5.44],[-12.799,16.32],[23.04,19.839],[0,0]],"o":[[0,0],[1.92,13.44],[15.679,5.44],[12.8,-16.32],[-31.041,-27.52],[0,0]],"v":[[-57.6,-20],[-40.959,10.08],[-24.959,45.6],[44.8,37.92],[25.921,-24.48],[-40.32,-54.24]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[-1.92,-13.44],[-15.68,-5.44],[-12.799,16.32],[23.04,19.839],[0,0]],"o":[[0,0],[1.92,13.44],[15.679,5.44],[12.8,-16.32],[-31.041,-27.52],[0,0]],"v":[[-57.6,-20],[-40.959,10.08],[-24.959,45.6],[44.8,37.92],[25.921,-24.48],[-40.32,-54.24]],"c":true}],"e":[{"i":[[0,0],[10.787,-21.621],[-15.68,-5.44],[-12.799,16.32],[12.407,32.439],[0,0]],"o":[[0,0],[-6.061,12.148],[15.679,5.44],[12.8,-16.32],[-19.093,-41.561],[0,0]],"v":[[-57.6,-20],[-61.959,27.08],[-45.959,62.6],[23.8,54.92],[28.921,-1.48],[-16.32,-59.24]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.995999983245,0.760999971278,0.447000002394,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","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":[651.672,580.041],"e":[703.672,570.041],"to":[8.66666698455811,-1.66666662693024],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[703.672,570.041],"e":[651.672,580.041],"to":[0,0],"ti":[8.66666698455811,-1.66666662693024]},{"t":359}],"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 18","np":2,"cix":2,"ix":18,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-38.631],[150.325,0],[0,38.63],[-150.325,0]],"o":[[0,38.63],[-150.325,0],[0,-38.631],[150.325,0]],"v":[[228.96,0],[-2.087,69.947],[-228.96,0],[-2.087,-69.947]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.886274569642,0.792156922583,0.925490255917,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[544.33,539.614],"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":70,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 19","np":2,"cix":2,"ix":19,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":0,"s":[{"i":[[0,0],[8.637,-160.553],[0,0],[-64.287,88.691],[-23.216,-2.405],[-74.974,-110.169],[3.114,11.391],[72.038,22.96],[0,0],[0,0],[21.175,-3.413],[0,0]],"o":[[0,0],[86.979,67.305],[0,0],[25.386,0.821],[98.964,10.251],[38.02,-41.759],[-12.841,-21.255],[-52.56,-12.96],[0,0],[0,0],[-16.918,7.478],[0,0]],"v":[[-131.05,-94.547],[-295.654,109.668],[-98.734,173.487],[-44.479,-83.844],[58.779,-85.268],[282.997,72.442],[300.23,-32.745],[191.787,-112.796],[102.622,-143.052],[-215.112,-122.818],[-243.595,-113.668],[-260.797,-107.167]],"c":true}],"e":[{"i":[[0,0],[8.637,-160.553],[0,0],[-64.287,88.691],[-20.53,-0.521],[-74.835,-109.965],[3.114,11.391],[72.038,22.96],[0,0],[0,0],[17.24,-6.036],[0,0]],"o":[[0,0],[91.888,44.724],[0,0],[25.586,0.827],[86.669,2.198],[5.129,-19.177],[-12.841,-21.255],[-52.559,-12.96],[0,0],[0,0],[-20.237,8.642],[0,0]],"v":[[-131.05,-94.547],[-212.69,152.868],[55.903,165.633],[26.212,-104.462],[94.965,-103.048],[305.579,19.424],[300.23,-32.745],[191.787,-112.796],[102.622,-143.052],[-232.058,-116.778],[-271.401,-100.686],[-301.84,-85.542]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":180,"s":[{"i":[[0,0],[8.637,-160.553],[0,0],[-64.287,88.691],[-20.53,-0.521],[-74.835,-109.965],[3.114,11.391],[72.038,22.96],[0,0],[0,0],[17.24,-6.036],[0,0]],"o":[[0,0],[91.888,44.724],[0,0],[25.586,0.827],[86.669,2.198],[5.129,-19.177],[-12.841,-21.255],[-52.559,-12.96],[0,0],[0,0],[-20.237,8.642],[0,0]],"v":[[-131.05,-94.547],[-212.69,152.868],[55.903,165.633],[26.212,-104.462],[94.965,-103.048],[305.579,19.424],[300.23,-32.745],[191.787,-112.796],[102.622,-143.052],[-232.058,-116.778],[-271.401,-100.686],[-301.84,-85.542]],"c":true}],"e":[{"i":[[0,0],[8.637,-160.553],[0,0],[-64.287,88.691],[-23.216,-2.405],[-74.974,-110.169],[3.114,11.391],[72.038,22.96],[0,0],[0,0],[21.175,-3.413],[0,0]],"o":[[0,0],[86.979,67.305],[0,0],[25.386,0.821],[98.964,10.251],[38.02,-41.759],[-12.841,-21.255],[-52.56,-12.96],[0,0],[0,0],[-16.918,7.478],[0,0]],"v":[[-131.05,-94.547],[-295.654,109.668],[-98.734,173.487],[-44.479,-83.844],[58.779,-85.268],[282.997,72.442],[300.23,-32.745],[191.787,-112.796],[102.622,-143.052],[-215.112,-122.818],[-243.595,-113.668],[-260.797,-107.167]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784373564,0.831372608858,0.925490255917,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[570.474,595.717],"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 20","np":2,"cix":2,"ix":20,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-78.933],[265.27,-18.473],[0,78.933],[-218.692,0]],"o":[[0,78.933],[-218.164,15.193],[0,-78.933],[218.692,0]],"v":[[333.09,0],[-4.508,170.902],[-333.089,0],[-3.035,-142.92]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.941000007181,0.964999988032,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[544.329,597.681],"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 21","np":2,"cix":2,"ix":21,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[67.975,-10.303],[-17.942,-26.69],[-29.782,-25.833],[22,-45.116],[-84.02,-12.631],[-185.573,-11.427],[-55.1,88.31],[97.238,38.459]],"o":[[0,0],[-41.762,6.33],[11.837,17.608],[29.782,25.832],[-12.356,25.339],[90.091,13.544],[82.174,5.06],[28.285,-45.333],[-151.821,-30.93]],"v":[[-154.897,-113.926],[-238.614,-107.75],[-297.989,-55.035],[-223.645,-4.175],[-275.76,80.579],[-166.787,140.15],[114.122,120.731],[328.584,127.573],[175.078,-9.212]],"c":true}],"e":[{"i":[[0,0],[67.975,-10.303],[-3.782,-31.937],[-29.782,-25.833],[28.065,-41.615],[-131.167,-8.582],[-185.573,-11.427],[-186.663,33.328],[97.238,38.459]],"o":[[0,0],[-41.762,6.33],[5.54,46.783],[29.782,25.832],[-28.065,41.616],[90.909,5.948],[82.174,5.06],[129.755,-23.167],[-151.821,-30.93]],"v":[[-154.897,-113.926],[-287.705,-119.531],[-380.461,-72.708],[-249.172,-0.248],[-340.56,92.361],[-211.95,199.059],[127.868,173.75],[452.293,239.5],[247.733,6.497]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[67.975,-10.303],[-3.782,-31.937],[-29.782,-25.833],[28.065,-41.615],[-131.167,-8.582],[-185.573,-11.427],[-186.663,33.328],[97.238,38.459]],"o":[[0,0],[-41.762,6.33],[5.54,46.783],[29.782,25.832],[-28.065,41.616],[90.909,5.948],[82.174,5.06],[129.755,-23.167],[-151.821,-30.93]],"v":[[-154.897,-113.926],[-287.705,-119.531],[-380.461,-72.708],[-249.172,-0.248],[-340.56,92.361],[-211.95,199.059],[127.868,173.75],[452.293,239.5],[247.733,6.497]],"c":true}],"e":[{"i":[[0,0],[67.975,-10.303],[-17.942,-26.69],[-29.782,-25.833],[22,-45.116],[-84.02,-12.631],[-185.573,-11.427],[-55.1,88.31],[97.238,38.459]],"o":[[0,0],[-41.762,6.33],[11.837,17.608],[29.782,25.832],[-12.356,25.339],[90.091,13.544],[82.174,5.06],[28.285,-45.333],[-151.821,-30.93]],"v":[[-154.897,-113.926],[-238.614,-107.75],[-297.989,-55.035],[-223.645,-4.175],[-275.76,80.579],[-166.787,140.15],[114.122,120.731],[328.584,127.572],[175.078,-9.212]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6,0.677999997606,0.976000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[417.716,703.099],"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 22","np":2,"cix":2,"ix":22,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-17.076,-2.883],[19.016,-22.102],[79.816,0]],"o":[[0,0],[17.075,2.883],[-19.016,22.102],[13.671,-43.836]],"v":[[-31.792,-43.208],[32.631,-39.312],[52.422,6.67],[-74.569,26.194]],"c":true}],"e":[{"i":[[0,0],[-31.68,-5.349],[35.281,-41.005],[148.079,0]],"o":[[0,0],[31.679,5.349],[-35.28,41.006],[25.363,-81.327]],"v":[[-56.079,-66.772],[63.442,-59.544],[100.161,25.766],[-135.442,61.987]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[0,0],[-31.68,-5.349],[35.281,-41.005],[148.079,0]],"o":[[0,0],[31.679,5.349],[-35.28,41.006],[25.363,-81.327]],"v":[[-56.079,-66.772],[63.442,-59.544],[100.161,25.766],[-135.442,61.987]],"c":true}],"e":[{"i":[[0,0],[-17.076,-2.883],[19.016,-22.102],[79.816,0]],"o":[[0,0],[17.075,2.883],[-19.016,22.102],[13.671,-43.836]],"v":[[-31.792,-43.208],[32.631,-39.312],[52.422,6.67],[-74.569,26.194]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6,0.677999997606,0.976000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[889.848,627.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":"Transform"}],"nm":"Group 23","np":2,"cix":2,"ix":23,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-16.14,13.767],[-11.077,0],[-6.013,-7.621],[25.832,0]],"o":[[5.433,-4.634],[11.076,0],[6.013,7.621],[-25.832,0]],"v":[[-77.904,-12.821],[-42.459,-17],[-3.533,-8.888],[-40.046,6.846]],"c":true}],"e":[{"i":[[-35.58,30.349],[-24.418,0],[-13.255,-16.8],[56.945,0]],"o":[[11.977,-10.216],[24.417,0],[13.256,16.8],[-56.945,0]],"v":[[-78.101,-17.057],[0.037,-26.27],[85.847,-8.386],[5.355,26.298]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[{"i":[[-35.58,30.349],[-24.418,0],[-13.255,-16.8],[56.945,0]],"o":[[11.977,-10.216],[24.417,0],[13.256,16.8],[-56.945,0]],"v":[[-78.101,-17.057],[0.037,-26.27],[85.847,-8.386],[5.355,26.298]],"c":true}],"e":[{"i":[[-16.14,13.767],[-11.077,0],[-6.013,-7.621],[25.832,0]],"o":[[5.433,-4.634],[11.076,0],[6.013,7.621],[-25.832,0]],"v":[[-77.904,-12.821],[-42.459,-17],[-3.533,-8.888],[-40.046,6.846]],"c":true}]},{"t":359}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6,0.677999997606,0.976000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[239.047,528.05],"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":60,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 24","np":2,"cix":2,"ix":24,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":368,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index a89146e..94605a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,8 +12,8 @@ # org.gradle.parallel=true #Thu Jun 04 12:03:50 CEST 2020 android.enableJetifier=true -android.enableR8.fullMode=true android.useAndroidX=true +android.enableR8.fullMode=true kotlin.code.style=official org.gradle.caching=true org.gradle.jvmargs=-Xms512M -Xmx2048M -XX\:MaxPermSize\=512M -XX\:MaxMetaspaceSize\=512M -Dkotlin.daemon.jvm.options\="-Xmx2048M"