From 9572a50570db31a5dd06e5dcb20578ef3e10113c Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Mon, 7 Dec 2020 15:32:52 +0100 Subject: [PATCH 01/18] gradle: updated libraries and using new version of ViewBinding --- ads/build.gradle | 14 +- app/build.gradle | 70 ++--- .../activities/DynamicFeatureProgress.kt | 38 +-- .../activities/LauncherActivity.kt | 50 ++-- .../activities/MainActivity.kt | 27 +- .../activities/PrivacyTermsActivity.kt | 15 +- .../activities/base/BaseFragmentActivity.kt | 6 +- .../activities/base/BaseFragmentView.kt | 4 +- .../base/SplitCompatBaseActivity.kt | 5 +- ...FragmentView.kt => ViewBindingInflater.kt} | 8 +- .../activities/support/ActionBarBase.kt | 9 +- .../diseases/DiseaseDescriptionFragment.kt | 39 ++- .../fragments/diseases/DiseaseExpandedView.kt | 16 +- .../DiseaseExtraInformationFragment.kt | 23 +- .../fragments/diseases/DiseasesFragment.kt | 103 ++++--- .../views/fragments/news/NewsFragment.kt | 56 ++-- .../views/fragments/news/adapter/News.kt | 2 +- .../settings/TimePickerPreference.kt | 2 + .../fragments/washinghands/FirstSlide.kt | 25 +- .../fragments/washinghands/SliderView.kt | 60 +++-- .../washinghands/WashingHandsFragment.kt | 43 +-- .../application/HandwashingApplication.kt | 2 +- .../graphics/RecyclingImageView.kt | 2 +- app/src/main/res/layout/main_disease_view.xml | 7 +- app/src/main/res/layout/refreshing_layout.xml | 4 +- app/src/main/res/layout/splash_screen.xml | 2 +- appintro/build.gradle | 11 +- .../appintro/IntroActivity.kt | 11 +- .../appintro/fragments/AnimatedAppIntro.kt | 17 +- .../appintro/fragments/IFragmentBinder.kt | 26 ++ .../appintro/fragments/ImageFragment.kt | 25 ++ .../appintro/fragments/SlidePolicyFragment.kt | 27 +- .../fragments/TimeConfigIntroFragment.kt | 39 +-- .../appintro/fragments/TimeContainer.kt | 21 ++ .../appintro/timeconfig/TimeConfigActivity.kt | 27 +- .../appintro/timeconfig/TimeConfigItem.kt | 2 +- .../src/main/res/layout/time_card_view.xml | 254 +++++++++--------- build.gradle | 17 +- bundledemoji/build.gradle | 11 +- extras/23920-error-state-dog.json | 1 + 40 files changed, 696 insertions(+), 425 deletions(-) rename app/src/main/java/com/javinator9889/handwashingreminder/activities/base/{BaseVisibleFragmentView.kt => ViewBindingInflater.kt} (81%) create mode 100644 appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/IFragmentBinder.kt create mode 100644 appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/ImageFragment.kt create mode 100644 appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/fragments/TimeContainer.kt create mode 100644 extras/23920-error-state-dog.json diff --git a/ads/build.gradle b/ads/build.gradle index e279d01..afab42c 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 + 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..5b0ea29 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 + targetSdkVersion 30 versionCode 137 - versionName "1.2.0-${gitCommitHash}" + 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' + api 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + 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' // 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/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt index 0d5c794..abe4033 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,14 @@ 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.databinding.DynamicContentPbBinding 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 timber.log.Timber -class DynamicFeatureProgress : SplitCompatBaseActivity(), +class DynamicFeatureProgress : SplitCompatBaseActivity(), SplitInstallStateUpdatedListener { companion object { const val MODULES = "modules" @@ -60,6 +60,7 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val layout = inflateLayout() splitInstallManager.registerListener(this) with(FirebaseAnalytics.getInstance(this)) { setCurrentScreen( @@ -85,7 +86,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 +115,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 +141,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 +190,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..de6cdce 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() } 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..39238cd 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,8 @@ 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 kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -48,7 +47,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 +59,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 @@ -136,15 +140,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) } } @@ -201,4 +205,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..15591dc 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 @@ -38,6 +40,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 +49,16 @@ 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 { @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 var viewCreated = false private val newsAdapter = ItemAdapter() private val newsViewModel: NewsViewModel by viewModels() @@ -64,17 +67,17 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { init { lifecycleScope.launch { whenStarted { - loading.visibility = View.VISIBLE - refreshLayout.isEnabled = false + loadingRecyclerView.loading.visibility = View.VISIBLE + binding.refreshLayout.isEnabled = false newsViewModel.newsData.observe(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 + binding.refreshLayout.isEnabled = true return@observe - } else errorScreen.visibility = View.INVISIBLE + } else loadingRecyclerView.errorScreen.visibility = View.INVISIBLE if (it.id !in activeItems) { val newsObject = News( title = it.title, @@ -87,9 +90,9 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { 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,6 +100,17 @@ 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() @@ -105,7 +119,7 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { val 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 +135,7 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { } } } - with(view.container) { + with(loadingRecyclerView.container) { layoutManager = rvManager adapter = fastAdapter itemAnimator = DefaultItemAnimator() @@ -130,8 +144,8 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { fastAdapter.addEventHooks(listOf(NewsClickHook(), ShareClickHook())) fastAdapter.withSavedInstanceState(savedInstanceState) viewCreated = savedInstanceState == null - refreshLayout.setOnRefreshListener { - refreshLayout.isRefreshing = true + binding.refreshLayout.setOnRefreshListener { + binding.refreshLayout.isRefreshing = true newsAdapter.clear() activeItems.clear() footerAdapter.clear() @@ -139,12 +153,12 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { lifecycleScope.launch { newsViewModel.populateData(language = UserProperties.language) }.invokeOnCompletion { - refreshLayout.isRefreshing = false + binding.refreshLayout.isRefreshing = false scrollListener.enable() scrollListener.resetPageCount() } - container.visibility = View.INVISIBLE - errorScreen.visibility = View.INVISIBLE + loadingRecyclerView.container.visibility = View.INVISIBLE + loadingRecyclerView.errorScreen.visibility = View.INVISIBLE } } @@ -158,7 +172,7 @@ class NewsFragment : BaseFragmentView(), LayoutVisibilityChange { override fun getVerticalSnapPreference(): Int = SNAP_TO_START } smoothScroller.targetPosition = 0 - container.layoutManager?.startSmoothScroll(smoothScroller) + loadingRecyclerView.container.layoutManager?.startSmoothScroll(smoothScroller) } private inner class NewsClickHook : ClickEventHook() { 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..25ebee9 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 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/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/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/res/layout/main_disease_view.xml b/app/src/main/res/layout/main_disease_view.xml index cc32275..d8b169d 100644 --- a/app/src/main/res/layout/main_disease_view.xml +++ b/app/src/main/res/layout/main_disease_view.xml @@ -15,7 +15,8 @@ android:background="@android:color/white" android:orientation="vertical"> - + @@ -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/refreshing_layout.xml b/app/src/main/res/layout/refreshing_layout.xml index a7d5744..6fc7757 100644 --- a/app/src/main/res/layout/refreshing_layout.xml +++ b/app/src/main/res/layout/refreshing_layout.xml @@ -4,6 +4,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + \ 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 @@ 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) @@ -334,7 +333,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..cde2931 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.20' + 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..5d07c87 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 + 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 From 071766fc85ac1af0661652c0570b3a7afc1da776 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Mon, 7 Dec 2020 16:05:32 +0100 Subject: [PATCH 02/18] Kotlin: updated Kt version up-to 1.4.21 In addition, some errors during release build were solved by upgrading Gradle plugin up-to 6.7.1 --- app/build.gradle | 4 ++-- build.gradle | 2 +- gradle.properties | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5b0ea29..d480b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -117,7 +117,7 @@ dependencies { api 'androidx.core:core-ktx:1.3.2' api 'androidx.legacy:legacy-support-v4:1.0.0' api 'androidx.constraintlayout:constraintlayout:2.0.4' - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.1' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' @@ -191,7 +191,7 @@ dependencies { 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:4.0' // https://github.com/square/okhttp/tree/okhttp_3.12.x diff --git a/build.gradle b/build.gradle index cde2931..0d21a1e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.20' + ext.kotlin_version = '1.4.21' ext.latestAboutLibsRelease = '8.6.3' ext.latestFastAdapterRelease = '5.3.2' repositories { diff --git a/gradle.properties b/gradle.properties index a89146e..ad72b0b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,6 @@ # org.gradle.parallel=true #Thu Jun 04 12:03:50 CEST 2020 android.enableJetifier=true -android.enableR8.fullMode=true android.useAndroidX=true kotlin.code.style=official org.gradle.caching=true From dbedb5e5af6fa50429c20d4287d7aeff0dcb9f72 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 11:03:09 +0100 Subject: [PATCH 03/18] Settings: load icons when screen orientation changes --- .../activities/views/fragments/settings/SettingsView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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( From 878dddd523e633f4ee6d08d97fd760556c776745 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 11:15:55 +0100 Subject: [PATCH 04/18] News: double click scrolls directly to position --- .../activities/views/fragments/news/NewsFragment.kt | 10 ++++++++-- .../activities/views/viewmodels/NewsViewModel.kt | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) 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 15591dc..fe96b4b 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 @@ -69,7 +69,7 @@ class NewsFragment : BaseFragmentView(), whenStarted { loadingRecyclerView.loading.visibility = View.VISIBLE binding.refreshLayout.isEnabled = false - newsViewModel.newsData.observe(viewLifecycleOwner) { + newsViewModel.newsData.observe(owner = viewLifecycleOwner) { if (::footerAdapter.isInitialized) footerAdapter.clear() if (it.hasError && newsAdapter.adapterItemCount == 0) { @@ -172,7 +172,13 @@ class NewsFragment : BaseFragmentView(), override fun getVerticalSnapPreference(): Int = SNAP_TO_START } smoothScroller.targetPosition = 0 - loadingRecyclerView.container.layoutManager?.startSmoothScroll(smoothScroller) + loadingRecyclerView.container.layoutManager?.let { + if (it.isSmoothScrolling) + it.scrollToPosition(0) + else + it.startSmoothScroll(smoothScroller) + } + } private inner class NewsClickHook : ClickEventHook() { 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") From 3b2b5cd19d2df0e49fd6c9f80128707cd6965495 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 11:40:41 +0100 Subject: [PATCH 05/18] News: error screen now shows a "Click to reload" button --- .../views/fragments/news/NewsFragment.kt | 41 +++++++++++-------- app/src/main/res/drawable/ic_update_black.xml | 5 +++ .../main/res/layout/loading_recycler_view.xml | 22 +++++++++- app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 app/src/main/res/drawable/ic_update_black.xml 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 fe96b4b..d290115 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 @@ -33,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 @@ -53,12 +54,13 @@ import kotlinx.coroutines.launch import timber.log.Timber class NewsFragment : BaseFragmentView(), - LayoutVisibilityChange { + 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() @@ -116,7 +118,7 @@ class NewsFragment : BaseFragmentView(), footerAdapter = ItemAdapter.items() fastAdapter = FastAdapter.with(listOf(newsAdapter, footerAdapter)) val rvManager = LinearLayoutManager(context) - val scrollListener = + scrollListener = object : EndlessRecyclerOnScrollListener(footerAdapter) { override fun onLoadMore(currentPage: Int) { loadingRecyclerView.container.post { @@ -144,21 +146,9 @@ class NewsFragment : BaseFragmentView(), fastAdapter.addEventHooks(listOf(NewsClickHook(), ShareClickHook())) fastAdapter.withSavedInstanceState(savedInstanceState) viewCreated = savedInstanceState == null - binding.refreshLayout.setOnRefreshListener { - 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 + binding.refreshLayout.setOnRefreshListener(this) + binding.loadingView.reloadText.setOnClickListener { + onRefresh() } } @@ -249,4 +239,21 @@ class NewsFragment : BaseFragmentView(), } } } + + 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/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" /> + + 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..a448b2f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -325,4 +325,5 @@ Edit notification settings Just washed them! Hurray! 🙌😱 + Click here to try again From fb983a2fbcb88d37a79a69806bee543c455178b4 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 12:02:36 +0100 Subject: [PATCH 06/18] News: try to load the icon directly from the web --- .../views/fragments/news/NewsFragment.kt | 2 ++ .../views/fragments/news/adapter/News.kt | 17 ++++++++++++++--- app/src/main/res/layout/news_card_view.xml | 8 +++++--- 3 files changed, 21 insertions(+), 6 deletions(-) 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 d290115..8128f22 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 @@ -88,9 +88,11 @@ class NewsFragment : BaseFragmentView(), discoverDate = it.discoverDate, imageUrl = it.elements?.url, website = it.website?.name, + websiteHostname = it.website?.hostName, websiteImageUrl = it.website?.iconURL, lifecycleOwner = this@NewsFragment ) + Timber.d(newsObject.toString()) newsAdapter.add(newsObject) loadingRecyclerView.loading.visibility = View.INVISIBLE loadingRecyclerView.container.visibility = View.VISIBLE 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 25ebee9..aa4f23a 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 @@ -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,19 @@ 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) +// websiteName.text = item.website?.let { +// if (it.length > 20) "${it.take(20)}…" else it +// } ?: 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/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 @@ Date: Thu, 10 Dec 2020 12:07:18 +0100 Subject: [PATCH 07/18] News: removed unused code --- .idea/.gitignore | 3 +++ .../activities/views/fragments/news/adapter/News.kt | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 .idea/.gitignore 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/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 aa4f23a..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 @@ -93,9 +93,6 @@ data class News( } websiteName.text = item.website ?: context.getString(R.string.no_website) -// websiteName.text = item.website?.let { -// if (it.length > 20) "${it.take(20)}…" else it -// } ?: context.getString(R.string.no_website) publishDate.text = item.discoverDate?.let { formatter.format(it) } ?: context.getString(R.string.no_date) From 1797d3eb8815fb43d7390f8f71d6ae44f54b5a9f Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 13:52:00 +0100 Subject: [PATCH 08/18] Alarms: created new notification system for activity alarms Due to the restriction of time, some alarms were lost and never get notified. With this updates, lost activity events are enqueued and saved so later on they can be notified as well --- .../activities/LauncherActivity.kt | 8 +-- .../data/SettingsLoader.kt | 7 +++ .../gms/activity/ActivityHandler.kt | 7 +++ .../gms/activity/ActivityReceiver.kt | 57 +++++++++++-------- .../jobs/alarms/AlarmHandler.kt | 13 +++++ .../jobs/alarms/AlarmReceiver.kt | 3 +- .../handwashingreminder/jobs/alarms/Alarms.kt | 31 ++++++++-- .../workers/PendingActivityAlarmWorker.kt | 57 +++++++++++++++++++ .../workers/ScheduledNotificationWorker.kt | 29 ++++++---- .../utils/calendar/Calendar.kt | 16 ++++++ app/src/main/res/values-es/strings.xml | 6 ++ app/src/main/res/values/strings.xml | 6 ++ 12 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/PendingActivityAlarmWorker.kt 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 de6cdce..18ab4d5 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt @@ -309,13 +309,13 @@ class LauncherActivity : BaseFragmentActivity() { 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/data/SettingsLoader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt index 8892291..edae6d9 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.* @@ -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..780a81b 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 @@ -23,12 +23,14 @@ import android.content.Context import android.content.Intent import androidx.annotation.StringRes 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.alarms.AlarmHandler +import com.javinator9889.handwashingreminder.jobs.alarms.Alarms import com.javinator9889.handwashingreminder.notifications.NotificationsHandler import com.javinator9889.handwashingreminder.utils.ACTIVITY_CHANNEL_ID import com.javinator9889.handwashingreminder.utils.Preferences @@ -38,11 +40,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 +73,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 +97,28 @@ 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) { + } + val notificationContent = when (event.activityType) { DetectedActivity.WALKING -> NotificationContent( R.string.activity_notification_walk, @@ -129,7 +140,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) @@ -150,11 +161,7 @@ class ActivityReceiver : BroadcastReceiver() { notificationId = 2 ) } - withContext(Dispatchers.IO) { - DataOutputStream(FileOutputStream(timeFile)).use { - it.writeLong(Calendar.getInstance().timeInMillis) - } - } + latestNotificationTime = CalendarUtils.now } } 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..4ffeee8 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 @@ -47,18 +47,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 @@ -90,11 +92,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 +102,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/calendar/Calendar.kt b/app/src/main/java/com/javinator9889/handwashingreminder/utils/calendar/Calendar.kt index 58f2a1d..4bc041f 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) = + 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/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 47aea94..6bc1bbe 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -69,8 +69,14 @@ las manos 😉💦👏 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! 🍱 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a448b2f..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 From 9ce0c1154df3901a937fb4dfe29397ac2deb7771 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 14:09:35 +0100 Subject: [PATCH 09/18] ActivityRecognition: showing actions as well as in scheduled notifications --- .../views/fragments/news/NewsFragment.kt | 1 - .../gms/activity/ActivityReceiver.kt | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) 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 8128f22..4c40eb3 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 @@ -92,7 +92,6 @@ class NewsFragment : BaseFragmentView(), websiteImageUrl = it.website?.iconURL, lifecycleOwner = this@NewsFragment ) - Timber.d(newsObject.toString()) newsAdapter.add(newsObject) loadingRecyclerView.loading.visibility = View.INVISIBLE loadingRecyclerView.container.visibility = View.VISIBLE 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 780a81b..f8d33a0 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,10 +18,12 @@ */ 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 com.google.android.gms.location.ActivityTransition import com.google.android.gms.location.ActivityTransitionEvent @@ -29,8 +31,12 @@ 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.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 @@ -108,7 +114,7 @@ class ActivityReceiver : BroadcastReceiver() { latestNotificationTime ) Timber.d("$timeDifference - $timeInBetweenNotifications") - if (timeDifference <= timeInBetweenNotifications) { + if (timeDifference < timeInBetweenNotifications) { pendingActivities.add(event) if (!alarmScheduled) { with(AlarmHandler(context)) { @@ -118,6 +124,7 @@ class ActivityReceiver : BroadcastReceiver() { } return } + latestNotificationTime = CalendarUtils.now val notificationContent = when (event.activityType) { DetectedActivity.WALKING -> NotificationContent( @@ -151,6 +158,14 @@ 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 + }, + PendingIntent.FLAG_UPDATE_CURRENT + ) withContext(Dispatchers.Main) { notificationsHandler.createNotification( iconDrawable = R.drawable.ic_stat_handwashing, @@ -158,10 +173,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 + ) ) } - latestNotificationTime = CalendarUtils.now } } From 32d7932f3caaa685e94221b78d4d4a1d96cd1e53 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 14:37:15 +0100 Subject: [PATCH 10/18] v1.2.1: updated version code --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d480b4a..892006e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ android { applicationId "com.javinator9889.handwashingreminder" minSdkVersion 17 targetSdkVersion 30 - versionCode 137 + versionCode 140 versionName "1.2.1-${gitCommitHash}" multiDexEnabled true resConfigs "en", "es" From fd87bb1f8c771c5dfe52c7c87501cbc0a35955c0 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 15:00:40 +0100 Subject: [PATCH 11/18] code: removed deprecated methods --- app/build.gradle | 2 +- .../activities/DynamicFeatureProgress.kt | 11 +++---- .../activities/MainActivity.kt | 5 +-- .../data/SettingsLoader.kt | 2 +- .../handwashingreminder/utils/Analytics.kt | 32 +++++++++++++++++++ .../utils/calendar/Calendar.kt | 2 +- .../appintro/IntroActivity.kt | 5 ++- 7 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/javinator9889/handwashingreminder/utils/Analytics.kt diff --git a/app/build.gradle b/app/build.gradle index 892006e..7dc4e77 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,7 @@ android { defaultConfig { applicationId "com.javinator9889.handwashingreminder" - minSdkVersion 17 + minSdkVersion 16 targetSdkVersion 30 versionCode 140 versionName "1.2.1-${gitCommitHash}" 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 abe4033..dc32993 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt @@ -37,13 +37,11 @@ import com.javinator9889.handwashingreminder.BuildConfig import com.javinator9889.handwashingreminder.R import com.javinator9889.handwashingreminder.activities.base.SplitCompatBaseActivity import com.javinator9889.handwashingreminder.databinding.DynamicContentPbBinding -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 com.javinator9889.handwashingreminder.utils.* import timber.log.Timber -class DynamicFeatureProgress : SplitCompatBaseActivity(), +class DynamicFeatureProgress : + SplitCompatBaseActivity(), SplitInstallStateUpdatedListener { companion object { const val MODULES = "modules" @@ -64,7 +62,8 @@ class DynamicFeatureProgress : SplitCompatBaseActivity( splitInstallManager.registerListener(this) with(FirebaseAnalytics.getInstance(this)) { setCurrentScreen( - this@DynamicFeatureProgress, "Dynamic module", null + "Dynamic module", + this@DynamicFeatureProgress::class ) } modules = 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 39238cd..c959732 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt @@ -39,6 +39,7 @@ 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 com.javinator9889.handwashingreminder.utils.setCurrentScreen import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -78,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) { @@ -163,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) } 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 edae6d9..fc55104 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/data/SettingsLoader.kt @@ -507,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 } 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 4bc041f..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 @@ -59,7 +59,7 @@ object CalendarUtils { from: Long = today.timeInMillis ): Long = from - to - fun timeIn(amount: Int, unit: TimeUnit) = + fun timeIn(amount: Int, unit: TimeUnit): Calendar = with(Calendar.getInstance()) { when (unit) { TimeUnit.MILLISECONDS -> this[Calendar.MILLISECOND] += amount 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 8e1d14a..7f35f22 100644 --- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt +++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt @@ -78,7 +78,7 @@ 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...") @@ -254,9 +254,8 @@ class IntroActivity : AppIntro2(), ) if (!policySlide.firebaseAnalytics.isChecked) { firebaseAnalytics.setCurrentScreen( - this@IntroActivity, null, - null + this@IntroActivity::class ) firebaseAnalytics.setAnalyticsCollectionEnabled(false) } From 80a343821637f251c31b631a09305f190dddc4dc Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 15:01:14 +0100 Subject: [PATCH 12/18] v1.2.1-rc1 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7dc4e77..5a8e317 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ android { applicationId "com.javinator9889.handwashingreminder" minSdkVersion 16 targetSdkVersion 30 - versionCode 140 + versionCode 141 versionName "1.2.1-${gitCommitHash}" multiDexEnabled true resConfigs "en", "es" From f894c347bca2c7ea7d24fb131e73184052ae6031 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 15:20:10 +0100 Subject: [PATCH 13/18] v1.2.1-rc1: remove old channels and updated boot receiver permissions --- app/src/main/AndroidManifest.xml | 5 +++- .../jobs/BootCompletedJob.kt | 7 +++-- .../jobs/UpdateReceiver.kt | 26 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) 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/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/UpdateReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt index 6627b93..3c27ace 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 == 141 && 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 From bec975098bfa932a377e192da50b8ea8d7aac738 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 15:43:00 +0100 Subject: [PATCH 14/18] v1.2.1-rc1: updated minimum version --- ads/build.gradle | 2 +- appintro/build.gradle | 2 +- bundledemoji/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ads/build.gradle b/ads/build.gradle index afab42c..ff843f9 100644 --- a/ads/build.gradle +++ b/ads/build.gradle @@ -11,7 +11,7 @@ android { compileSdkVersion 30 defaultConfig { - minSdkVersion 17 + minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/appintro/build.gradle b/appintro/build.gradle index 1a71802..e20781f 100644 --- a/appintro/build.gradle +++ b/appintro/build.gradle @@ -12,7 +12,7 @@ android { android.defaultConfig.vectorDrawables.useSupportLibrary = true defaultConfig { - minSdkVersion 17 + minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/bundledemoji/build.gradle b/bundledemoji/build.gradle index 5d07c87..51a3752 100644 --- a/bundledemoji/build.gradle +++ b/bundledemoji/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { - minSdkVersion 17 + minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.0" From d4b2266f30d882eb49b322eee3bc61111f90b9e5 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 21:36:27 +0100 Subject: [PATCH 15/18] Notifications: cancel notification by ID --- .../handwashingreminder/gms/activity/ActivityReceiver.kt | 2 ++ .../handwashingreminder/jobs/HandsWashedReceiver.kt | 9 +++++++-- .../jobs/workers/ScheduledNotificationWorker.kt | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) 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 f8d33a0..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 @@ -34,6 +34,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 @@ -163,6 +164,7 @@ class ActivityReceiver : BroadcastReceiver() { HANDS_WASHED_CODE, Intent(context, HandsWashedReceiver::class.java).apply { action = HANDS_WASHED_ACTION + putExtra(NOTIFICATION_ID_KEY, 2) }, PendingIntent.FLAG_UPDATE_CURRENT ) 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/workers/ScheduledNotificationWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/ScheduledNotificationWorker.kt index 4ffeee8..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 @@ -74,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 ) From fe248f32204ce963c9bd7241786ec9d09885066e Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 21:36:49 +0100 Subject: [PATCH 16/18] gradle: aggressive optimizations for release builds --- app/build.gradle | 2 +- gradle.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5a8e317..53a82a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ android { applicationId "com.javinator9889.handwashingreminder" minSdkVersion 16 targetSdkVersion 30 - versionCode 141 + versionCode 142 versionName "1.2.1-${gitCommitHash}" multiDexEnabled true resConfigs "en", "es" diff --git a/gradle.properties b/gradle.properties index ad72b0b..94605a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,7 @@ #Thu Jun 04 12:03:50 CEST 2020 android.enableJetifier=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" From a1ca37b2fa3707f7654ab20ba5a9c0992c888765 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 21:42:06 +0100 Subject: [PATCH 17/18] News: hide loading bar when error screen is displayed --- .../activities/views/fragments/news/NewsFragment.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 4c40eb3..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 @@ -77,9 +77,13 @@ class NewsFragment : BaseFragmentView(), if (it.hasError && newsAdapter.adapterItemCount == 0) { loadingRecyclerView.errorScreen.visibility = View.VISIBLE loadingRecyclerView.container.visibility = View.INVISIBLE + loadingRecyclerView.loading.visibility = View.INVISIBLE binding.refreshLayout.isEnabled = true return@observe - } else loadingRecyclerView.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, From 048e0e900e13489676faf1dba19cef38b93c9ad0 Mon Sep 17 00:00:00 2001 From: Javinator9889 Date: Thu, 10 Dec 2020 21:44:49 +0100 Subject: [PATCH 18/18] UpdateReceiver: delete notification channels if new version is 141 or 142 --- .../javinator9889/handwashingreminder/jobs/UpdateReceiver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3c27ace..7c747a1 100644 --- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt +++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt @@ -38,7 +38,7 @@ class UpdateReceiver : BroadcastReceiver() { } // Here, we need to remove all the notifications channels // previously created as they have changed - if (BuildConfig.VERSION_CODE == 141 && isAtLeast(AndroidVersion.O)) { + if (BuildConfig.VERSION_CODE in 141..142 && isAtLeast(AndroidVersion.O)) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager for (id in setOf(