diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 33526b5a2..6b6dd1988 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -234,6 +234,7 @@ dependencies { googleplayImplementation("com.google.firebase:firebase-analytics:${Versions.analytics}") { exclude("com.google.android.gms", "play-services-ads-identifier") } + googleplayImplementation("com.google.firebase:firebase-config-ktx:${Versions.remote_config}") googleplayImplementation("com.google.android.gms:play-services-location:19.0.1") googleplayImplementation("com.google.android.gms:play-services-maps:18.0.2") googleplayImplementation("com.android.billingclient:billing-ktx:3.0.3") diff --git a/app/src/generic/java/org/tasks/analytics/Firebase.kt b/app/src/generic/java/org/tasks/analytics/Firebase.kt index ca80e4a92..5f550bdc2 100644 --- a/app/src/generic/java/org/tasks/analytics/Firebase.kt +++ b/app/src/generic/java/org/tasks/analytics/Firebase.kt @@ -7,5 +7,7 @@ import javax.inject.Inject class Firebase @Inject constructor() { fun reportException(t: Throwable) = Timber.e(t) + fun updateRemoteConfig() {} + fun logEvent(event: Int, vararg params: Pair) {} } \ No newline at end of file diff --git a/app/src/googleplay/java/org/tasks/analytics/Firebase.kt b/app/src/googleplay/java/org/tasks/analytics/Firebase.kt index 3a16f0aaa..cbe0481cd 100644 --- a/app/src/googleplay/java/org/tasks/analytics/Firebase.kt +++ b/app/src/googleplay/java/org/tasks/analytics/Firebase.kt @@ -5,10 +5,14 @@ import android.os.Bundle import androidx.annotation.StringRes import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.google.firebase.remoteconfig.FirebaseRemoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.R +import org.tasks.jobs.WorkManager import org.tasks.preferences.Preferences import timber.log.Timber +import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -20,6 +24,7 @@ class Firebase @Inject constructor( private var crashlytics: FirebaseCrashlytics? = null private var analytics: FirebaseAnalytics? = null + private var remoteConfig: FirebaseRemoteConfig? = null fun reportException(t: Throwable) { Timber.e(t) @@ -35,6 +40,12 @@ class Firebase @Inject constructor( ) } + fun updateRemoteConfig() { + remoteConfig?.fetchAndActivate()?.addOnSuccessListener { + Timber.d(it.toString()) + } + } + fun logEvent(@StringRes event: Int, vararg p: Pair) { analytics?.logEvent(context.getString(event), Bundle().apply { p.forEach { @@ -48,6 +59,9 @@ class Firebase @Inject constructor( }) } + val reviewCooldown: Long + get() = remoteConfig?.getLong("review_cooldown") ?: 14L + init { if (preferences.isTrackingEnabled) { analytics = FirebaseAnalytics.getInstance(context).apply { @@ -56,6 +70,13 @@ class Firebase @Inject constructor( crashlytics = FirebaseCrashlytics.getInstance().apply { setCrashlyticsCollectionEnabled(true) } + remoteConfig = FirebaseRemoteConfig.getInstance().apply { + setConfigSettingsAsync(remoteConfigSettings { + minimumFetchIntervalInSeconds = + TimeUnit.HOURS.toSeconds(WorkManager.REMOTE_CONFIG_INTERVAL_HOURS) + }) + setDefaultsAsync(R.xml.remote_config_defaults) + } } } } \ No newline at end of file diff --git a/app/src/googleplay/java/org/tasks/play/PlayServices.kt b/app/src/googleplay/java/org/tasks/play/PlayServices.kt index 6f5680479..d775015c2 100644 --- a/app/src/googleplay/java/org/tasks/play/PlayServices.kt +++ b/app/src/googleplay/java/org/tasks/play/PlayServices.kt @@ -9,6 +9,7 @@ import com.google.android.play.core.ktx.requestReview import com.google.android.play.core.review.ReviewManagerFactory import com.todoroo.andlib.utility.DateUtilities.now import dagger.hilt.android.qualifiers.ApplicationContext +import org.tasks.analytics.Firebase import org.tasks.preferences.Preferences import org.tasks.time.DateTimeUtils.printTimestamp import timber.log.Timber @@ -18,16 +19,22 @@ import javax.inject.Inject class PlayServices @Inject constructor( @ApplicationContext private val context: Context, private val preferences: Preferences, + private val firebase: Firebase, ) { fun isAvailable() = getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS suspend fun requestReview(activity: Activity) { val now = now() - val installCutoff = preferences.installDate + INSTALL_COOLDOWN val reviewCutoff = preferences.lastReviewRequest + REVIEW_COOLDOWN + if (reviewCutoff > now) { + Timber.d("review cooldown: ${printTimestamp(reviewCutoff)}") + return + } + val installCutoff = + preferences.installDate + TimeUnit.DAYS.toMillis(firebase.reviewCooldown) if (installCutoff > now || reviewCutoff > now) { - Timber.d("wait for review request: install=${printTimestamp(installCutoff)} review=${printTimestamp(reviewCutoff)}") + Timber.d("install cooldown: ${printTimestamp(installCutoff)}") return } try { @@ -42,7 +49,6 @@ class PlayServices @Inject constructor( } companion object { - private val INSTALL_COOLDOWN = TimeUnit.DAYS.toMillis(14) private val REVIEW_COOLDOWN = TimeUnit.DAYS.toMillis(30) } } \ No newline at end of file diff --git a/app/src/googleplay/res/xml/remote_config_defaults.xml b/app/src/googleplay/res/xml/remote_config_defaults.xml new file mode 100644 index 000000000..775081335 --- /dev/null +++ b/app/src/googleplay/res/xml/remote_config_defaults.xml @@ -0,0 +1,8 @@ + + + + review_cooldown + 14 + + diff --git a/app/src/main/java/org/tasks/Tasks.kt b/app/src/main/java/org/tasks/Tasks.kt index f7d334e2b..3ce8bd45d 100644 --- a/app/src/main/java/org/tasks/Tasks.kt +++ b/app/src/main/java/org/tasks/Tasks.kt @@ -82,6 +82,7 @@ class Tasks : Application(), Configuration.Provider { updateBackgroundSync() scheduleMidnightRefresh() scheduleBackup() + scheduleConfigRefresh() OpenTaskContentObserver.registerObserver(context, contentObserver.get()) updatePurchases() } diff --git a/app/src/main/java/org/tasks/jobs/RemoteConfigWork.kt b/app/src/main/java/org/tasks/jobs/RemoteConfigWork.kt new file mode 100644 index 000000000..02827e905 --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/RemoteConfigWork.kt @@ -0,0 +1,21 @@ +package org.tasks.jobs + +import android.content.Context +import androidx.hilt.work.HiltWorker +import androidx.work.WorkerParameters +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import org.tasks.analytics.Firebase +import org.tasks.injection.BaseWorker + +@HiltWorker +class RemoteConfigWork @AssistedInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + firebase: Firebase) : BaseWorker(context, workerParams, firebase) { + + override suspend fun run(): Result { + firebase.updateRemoteConfig() + return Result.success() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/WorkManager.kt b/app/src/main/java/org/tasks/jobs/WorkManager.kt index 7d1a47c94..4dee4dd79 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManager.kt +++ b/app/src/main/java/org/tasks/jobs/WorkManager.kt @@ -2,6 +2,7 @@ package org.tasks.jobs import android.net.Uri import com.todoroo.astrid.data.Task +import org.tasks.BuildConfig import org.tasks.data.CaldavAccount import org.tasks.data.Place @@ -35,6 +36,8 @@ interface WorkManager { fun scheduleBackup() + fun scheduleConfigRefresh() + fun scheduleDriveUpload(uri: Uri, purge: Boolean) fun cancelNotifications() @@ -42,6 +45,7 @@ interface WorkManager { fun updatePurchases() companion object { + val REMOTE_CONFIG_INTERVAL_HOURS = if (BuildConfig.DEBUG) 1 else 12.toLong() const val MAX_CLEANUP_LENGTH = 500 const val TAG_BACKUP = "tag_backup" const val TAG_REFRESH = "tag_refresh" @@ -54,6 +58,7 @@ interface WorkManager { const val TAG_BACKGROUND_SYNC_CALDAV = "tag_background_sync_caldav" const val TAG_BACKGROUND_SYNC_ETEBASE = "tag_background_sync_etebase" const val TAG_BACKGROUND_SYNC_OPENTASKS = "tag_background_sync_opentasks" + const val TAG_REMOTE_CONFIG = "tag_remote_config" const val TAG_MIGRATE_LOCAL = "tag_migrate_local" const val TAG_UPDATE_PURCHASES = "tag_update_purchases" } diff --git a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt index 3f686cebe..6f0e50c40 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt +++ b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt @@ -39,6 +39,7 @@ import org.tasks.jobs.MigrateLocalWork.Companion.EXTRA_ACCOUNT import org.tasks.jobs.SyncWork.Companion.EXTRA_BACKGROUND import org.tasks.jobs.SyncWork.Companion.EXTRA_IMMEDIATE import org.tasks.jobs.WorkManager.Companion.MAX_CLEANUP_LENGTH +import org.tasks.jobs.WorkManager.Companion.REMOTE_CONFIG_INTERVAL_HOURS import org.tasks.jobs.WorkManager.Companion.TAG_BACKGROUND_SYNC_CALDAV import org.tasks.jobs.WorkManager.Companion.TAG_BACKGROUND_SYNC_ETEBASE import org.tasks.jobs.WorkManager.Companion.TAG_BACKGROUND_SYNC_GOOGLE_TASKS @@ -47,6 +48,7 @@ import org.tasks.jobs.WorkManager.Companion.TAG_BACKUP import org.tasks.jobs.WorkManager.Companion.TAG_MIDNIGHT_REFRESH import org.tasks.jobs.WorkManager.Companion.TAG_MIGRATE_LOCAL import org.tasks.jobs.WorkManager.Companion.TAG_REFRESH +import org.tasks.jobs.WorkManager.Companion.TAG_REMOTE_CONFIG import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_CALDAV import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETEBASE import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_GOOGLE_TASKS @@ -225,6 +227,18 @@ class WorkManagerImpl constructor( .millis .coerceAtMost(midnight())) + override fun scheduleConfigRefresh() { + throttle.run { + workManager.enqueueUniquePeriodicWork( + TAG_REMOTE_CONFIG, + ExistingPeriodicWorkPolicy.KEEP, + PeriodicWorkRequest.Builder( + RemoteConfigWork::class.java, REMOTE_CONFIG_INTERVAL_HOURS, TimeUnit.HOURS) + .setConstraints(networkConstraints) + .build()) + } + } + override fun scheduleDriveUpload(uri: Uri, purge: Boolean) { if (!preferences.getBoolean(R.string.p_google_drive_backup, false)) { return diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index e19199c0c..2e883813d 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -424,7 +424,6 @@ whats_new display_rate display_subscribe - user_no_churn user_pro click billing_flow_result diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b5a92d407..66caeb60e 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -8,6 +8,7 @@ object Versions { const val room = "2.4.2" const val crashlytics = "18.2.8" const val analytics = "20.1.0" + const val remote_config = "21.0.1" const val okhttp = "4.9.3" const val flipper = "0.136.0" const val mockito = "3.9.0" diff --git a/deps_googleplay.txt b/deps_googleplay.txt index 1768a5c76..5b06fdcaf 100644 --- a/deps_googleplay.txt +++ b/deps_googleplay.txt @@ -201,6 +201,31 @@ +| +--- com.google.android.gms:play-services-basement:18.0.0 (*) +| +--- com.google.android.gms:play-services-measurement-base:20.1.0 (*) +| \--- com.google.android.gms:play-services-measurement-impl:20.1.0 (*) +++--- com.google.firebase:firebase-config-ktx:21.0.1 ++| +--- androidx.annotation:annotation:1.1.0 -> 1.3.0 ++| +--- com.google.firebase:firebase-abt:21.0.0 ++| | +--- com.google.android.gms:play-services-basement:17.0.0 -> 18.0.0 (*) ++| | +--- com.google.firebase:firebase-common:20.0.0 (*) ++| | +--- com.google.firebase:firebase-components:17.0.0 (*) ++| | \--- com.google.firebase:firebase-measurement-connector:18.0.0 -> 19.0.0 (*) ++| +--- com.google.firebase:firebase-common:20.0.0 (*) ++| +--- com.google.firebase:firebase-common-ktx:20.0.0 ++| | +--- androidx.annotation:annotation:1.1.0 -> 1.3.0 ++| | +--- com.google.firebase:firebase-common:20.0.0 (*) ++| | +--- com.google.firebase:firebase-components:17.0.0 (*) ++| | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72 -> 1.6.10 (*) ++| +--- com.google.firebase:firebase-components:17.0.0 (*) ++| +--- com.google.firebase:firebase-config:21.0.1 ++| | +--- com.google.android.gms:play-services-tasks:17.0.2 -> 18.0.1 (*) ++| | +--- com.google.firebase:firebase-abt:21.0.0 (*) ++| | +--- com.google.firebase:firebase-common:20.0.0 (*) ++| | +--- com.google.firebase:firebase-components:17.0.0 (*) ++| | +--- com.google.firebase:firebase-installations:17.0.0 (*) ++| | +--- com.google.firebase:firebase-installations-interop:17.0.0 (*) ++| | \--- com.google.firebase:firebase-measurement-connector:18.0.0 -> 19.0.0 (*) ++| +--- com.google.firebase:firebase-installations:17.0.0 (*) ++| +--- com.google.firebase:firebase-installations-interop:17.0.0 (*) ++| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72 -> 1.6.10 (*) ++--- com.google.android.gms:play-services-location:19.0.1 +| +--- com.google.android.gms:play-services-base:18.0.1 +| | +--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)