From 58881a24d418eca029131461950457233845f0c2 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Thu, 23 Mar 2023 09:40:42 +0530 Subject: [PATCH 01/81] Adds: dependency for In app updates --- WordPress/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/build.gradle b/WordPress/build.gradle index c9102ba86a94..b589334c1bfa 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -322,6 +322,9 @@ static def addBuildConfigFieldsFromPrefixedProperties(variant, properties, prefi } dependencies { + implementation 'com.google.android.play:app-update:2.0.1' + implementation 'com.google.android.play:app-update-ktx:2.0.1' + compileOnly project(path: ':libs:annotations') kapt project(':libs:processors') implementation (project(path:':libs:networking')) { From 23adf5ccda6be801d0d405c2d540cdf2cdf58e83 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Thu, 23 Mar 2023 09:44:00 +0530 Subject: [PATCH 02/81] Adds: App update checker class to the project --- .../org/wordpress/android/AppInitializer.kt | 4 ++ .../org/wordpress/android/AppUpdateChecker.kt | 56 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt diff --git a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt index aaa8d0031726..71891351eccc 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt @@ -226,6 +226,9 @@ class AppInitializer @Inject constructor( @Inject lateinit var jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper + @Inject + lateinit var appUpdateChecker: AppUpdateChecker + private lateinit var applicationLifecycleMonitor: ApplicationLifecycleMonitor lateinit var storyNotificationTrackerProvider: StoryNotificationTrackerProvider private set @@ -282,6 +285,7 @@ class AppInitializer @Inject constructor( fun init() { dispatcher.register(this) appConfig.init(appScope) + appUpdateChecker.init(application) // Upload any encrypted logs that were queued but not yet uploaded encryptedLogging.start() diff --git a/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt b/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt new file mode 100644 index 000000000000..5579bed45c8f --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt @@ -0,0 +1,56 @@ +package org.wordpress.android + +import android.app.Application +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability +import javax.inject.Singleton + +private const val MAXIMUM_THRESHHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 + +@Singleton +class AppUpdateChecker { + + private lateinit var appUpdateManager: AppUpdateManager + + fun init(application: Application) { + AppUpdateManagerFactory.create(application) + } + + private fun checkPlayStoreUpdate() { + // Returns an intent object that you use to check for an update. + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + // Checks that the platform will allow the specified type of update. + //todo: add a log here + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + ) { + requestImmediateUpdate(appUpdateInfo) + } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + && (appUpdateInfo.clientVersionStalenessDays() + ?: -1) >= MAXIMUM_THRESHHOLD_FOR_FLEXIBLE_UPDATES + ) { + requestImmediateUpdate(appUpdateInfo) + } else if ( + appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + ) { + requestFlexibleUpdate() + } else { + //todo: check + } + } + } + + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo) { + //todo: show a dialog to the user to confirm the update + } + + private fun requestFlexibleUpdate() { + //todo: show a dialog to the user to confirm the update + } +} From 2ace10e727573cf684fd8df41b488370e6c419d2 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Fri, 24 Mar 2023 15:57:36 +0530 Subject: [PATCH 03/81] Adds: logic to start google play update --- .../org/wordpress/android/AppUpdateChecker.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt b/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt index 5579bed45c8f..be7ed6a92da8 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt @@ -1,5 +1,6 @@ package org.wordpress.android +import android.app.Activity import android.app.Application import com.google.android.play.core.appupdate.AppUpdateInfo import com.google.android.play.core.appupdate.AppUpdateManager @@ -10,6 +11,10 @@ import javax.inject.Singleton private const val MAXIMUM_THRESHHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 +const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + +const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + @Singleton class AppUpdateChecker { @@ -46,11 +51,27 @@ class AppUpdateChecker { } } - private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo) { - //todo: show a dialog to the user to confirm the update + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.startUpdateFlowForResult( + // Pass the intent that is returned by 'getAppUpdateInfo()'. + appUpdateInfo, + // Or 'AppUpdateType.FLEXIBLE' for flexible updates. + AppUpdateType.IMMEDIATE, + // The current activity making the update request. + activity, + // Include a request code to later monitor this update request. + APP_UPDATE_IMMEDIATE_REQUEST_CODE) } - private fun requestFlexibleUpdate() { - //todo: show a dialog to the user to confirm the update + private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.startUpdateFlowForResult( + // Pass the intent that is returned by 'getAppUpdateInfo()'. + appUpdateInfo, + // Or 'AppUpdateType.FLEXIBLE' for flexible updates. + AppUpdateType.FLEXIBLE, + // The current activity making the update request. + activity, + // Include a request code to later monitor this update request. + APP_UPDATE_FLEXIBLE_REQUEST_CODE) } } From e4f8a6d4870553d554651dbe860265209dded715 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 28 Mar 2023 15:37:27 +0530 Subject: [PATCH 04/81] Removes: in app update checker from app initializer --- .../src/main/java/org/wordpress/android/AppInitializer.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt index 71891351eccc..a4568c1f669c 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt @@ -226,9 +226,6 @@ class AppInitializer @Inject constructor( @Inject lateinit var jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper - @Inject - lateinit var appUpdateChecker: AppUpdateChecker - private lateinit var applicationLifecycleMonitor: ApplicationLifecycleMonitor lateinit var storyNotificationTrackerProvider: StoryNotificationTrackerProvider private set @@ -285,8 +282,6 @@ class AppInitializer @Inject constructor( fun init() { dispatcher.register(this) appConfig.init(appScope) - appUpdateChecker.init(application) - // Upload any encrypted logs that were queued but not yet uploaded encryptedLogging.start() From a3248622c255d482eda1aebd583a62b2591f86a1 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 28 Mar 2023 15:52:32 +0530 Subject: [PATCH 05/81] Moves: the dependency implementation and dependency version Moves: the dependency version and implementation code change for better dependency management visibility --- WordPress/build.gradle | 6 +++--- build.gradle | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WordPress/build.gradle b/WordPress/build.gradle index b589334c1bfa..5e85e7bd8d17 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -322,9 +322,6 @@ static def addBuildConfigFieldsFromPrefixedProperties(variant, properties, prefi } dependencies { - implementation 'com.google.android.play:app-update:2.0.1' - implementation 'com.google.android.play:app-update-ktx:2.0.1' - compileOnly project(path: ':libs:annotations') kapt project(':libs:processors') implementation (project(path:':libs:networking')) { @@ -376,6 +373,9 @@ dependencies { debugImplementation "com.facebook.stetho:stetho:$stethoVersion" debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoVersion" + implementation "com.google.android.play:$googlePlayInAppUpdateVersion" + implementation "com.google.android.play:app-update-ktx:$googlePlayInAppUpdateVersion" + implementation "androidx.arch.core:core-common:$androidxArchCoreVersion" implementation "androidx.arch.core:core-runtime:$androidxArchCoreVersion" implementation "com.google.code.gson:gson:$googleGsonVersion" diff --git a/build.gradle b/build.gradle index b233c935fd2e..20631dea72ca 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ ext { squareupRetrofitVersion = '2.9.0' uCropVersion = '2.2.8' zendeskVersion = '5.1.1' + googlePlayInAppUpdateVersion = '2.0.1' // test assertjVersion = '3.23.1' From c237e21e7a397a85a66ae522e0828cb418383658 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 28 Mar 2023 16:10:04 +0530 Subject: [PATCH 06/81] Fixes: Merge conflict in MySiteViewModelTest --- .../org/wordpress/android/AppUpdateChecker.kt | 77 ---------------- .../wordpress/android/InAppUpdateManager.kt | 91 +++++++++++++++++++ 2 files changed, 91 insertions(+), 77 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt diff --git a/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt b/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt deleted file mode 100644 index be7ed6a92da8..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/AppUpdateChecker.kt +++ /dev/null @@ -1,77 +0,0 @@ -package org.wordpress.android - -import android.app.Activity -import android.app.Application -import com.google.android.play.core.appupdate.AppUpdateInfo -import com.google.android.play.core.appupdate.AppUpdateManager -import com.google.android.play.core.appupdate.AppUpdateManagerFactory -import com.google.android.play.core.install.model.AppUpdateType -import com.google.android.play.core.install.model.UpdateAvailability -import javax.inject.Singleton - -private const val MAXIMUM_THRESHHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 - -const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 - -const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 - -@Singleton -class AppUpdateChecker { - - private lateinit var appUpdateManager: AppUpdateManager - - fun init(application: Application) { - AppUpdateManagerFactory.create(application) - } - - private fun checkPlayStoreUpdate() { - // Returns an intent object that you use to check for an update. - val appUpdateInfoTask = appUpdateManager.appUpdateInfo - // Checks that the platform will allow the specified type of update. - //todo: add a log here - appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - ) { - requestImmediateUpdate(appUpdateInfo) - } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && - appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - && (appUpdateInfo.clientVersionStalenessDays() - ?: -1) >= MAXIMUM_THRESHHOLD_FOR_FLEXIBLE_UPDATES - ) { - requestImmediateUpdate(appUpdateInfo) - } else if ( - appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && - appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - ) { - requestFlexibleUpdate() - } else { - //todo: check - } - } - } - - private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - appUpdateManager.startUpdateFlowForResult( - // Pass the intent that is returned by 'getAppUpdateInfo()'. - appUpdateInfo, - // Or 'AppUpdateType.FLEXIBLE' for flexible updates. - AppUpdateType.IMMEDIATE, - // The current activity making the update request. - activity, - // Include a request code to later monitor this update request. - APP_UPDATE_IMMEDIATE_REQUEST_CODE) - } - - private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - appUpdateManager.startUpdateFlowForResult( - // Pass the intent that is returned by 'getAppUpdateInfo()'. - appUpdateInfo, - // Or 'AppUpdateType.FLEXIBLE' for flexible updates. - AppUpdateType.FLEXIBLE, - // The current activity making the update request. - activity, - // Include a request code to later monitor this update request. - APP_UPDATE_FLEXIBLE_REQUEST_CODE) - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt new file mode 100644 index 000000000000..730d30c1b7e3 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -0,0 +1,91 @@ +package org.wordpress.android + +import android.app.Activity +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.install.InstallStateUpdatedListener +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability +import com.google.android.play.core.ktx.updatePriority + +import javax.inject.Singleton + +private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 + +private const val MIN_DURATION_TO_WAIT_FOR_UPDATE_PROMPT: Int = 7 + +@Singleton +class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateManager) { + + fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { + appUpdateManager.registerListener(installStateUpdatedListener) + } + + fun checkForAppUpdate(activity: Activity) { + if (!shouldRequestUpdate()) + return + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (isImmediateUpdateNecessary(appUpdateInfo)) { + requestImmediateUpdate(appUpdateInfo, activity) + } else if (isFlexibleUpdateNecessary(appUpdateInfo)) { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + } + + private fun isImmediateUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE + && (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + && isUpdatePriorityHigh(appUpdateInfo)) || isClientVersionOlderThanThreshold( + appUpdateInfo + ) + } + + private fun isUpdatePriorityHigh(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updatePriority > 4 + } + + private fun isClientVersionOlderThanThreshold(appUpdateInfo: AppUpdateInfo): Boolean { + return (appUpdateInfo.clientVersionStalenessDays() + ?: -1) >= MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES + } + + private fun isFlexibleUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + && !isUpdatePriorityHigh(appUpdateInfo) + } + + fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + AppUpdateType.IMMEDIATE, + activity, + APP_UPDATE_IMMEDIATE_REQUEST_CODE + ) + } + + fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + AppUpdateType.FLEXIBLE, + activity, + APP_UPDATE_FLEXIBLE_REQUEST_CODE + ) + } + + private fun setLastUpdateRequestedTime() { + //todo: add logic to save the time when the update request was made + } + + private fun shouldRequestUpdate(): Boolean { + //todo: add logic to check the time since last update request + } + + + companion object { + const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + } +} From 6c0235f0170408fb6199a06099204dae4495caee Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 28 Mar 2023 16:23:56 +0530 Subject: [PATCH 07/81] Adds: Helper functions to check whether update is in progress --- .../org/wordpress/android/InAppUpdateManager.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 730d30c1b7e3..ed88dbc8f6c5 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -75,6 +75,20 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana ) } + private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + return (appUpdateInfo.updateAvailability() + == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + && isUpdatePriorityHigh(appUpdateInfo) + } + + private fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + return (appUpdateInfo.updateAvailability() + == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + && !isUpdatePriorityHigh(appUpdateInfo) + } + private fun setLastUpdateRequestedTime() { //todo: add logic to save the time when the update request was made } From 5263ce6dbecbd38a3ef1d7cc209199bd5614bfe1 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 28 Mar 2023 18:50:24 +0530 Subject: [PATCH 08/81] Adds: the function to get the app update information --- .../main/java/org/wordpress/android/InAppUpdateManager.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index ed88dbc8f6c5..ba46ed9983a4 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -1,6 +1,7 @@ package org.wordpress.android import android.app.Activity +import com.google.android.gms.tasks.Task import com.google.android.play.core.appupdate.AppUpdateInfo import com.google.android.play.core.appupdate.AppUpdateManager import com.google.android.play.core.install.InstallStateUpdatedListener @@ -21,6 +22,10 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana appUpdateManager.registerListener(installStateUpdatedListener) } + fun getInAppUpdateManager(): Task { + return appUpdateManager.appUpdateInfo + } + fun checkForAppUpdate(activity: Activity) { if (!shouldRequestUpdate()) return From 7cf441ffc1ddbe971b94efad0f6d30cad9590996 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 3 Apr 2023 09:45:33 +0530 Subject: [PATCH 09/81] Fixes: google play update version --- WordPress/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/build.gradle b/WordPress/build.gradle index 5e85e7bd8d17..5796e9b53189 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -373,7 +373,7 @@ dependencies { debugImplementation "com.facebook.stetho:stetho:$stethoVersion" debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoVersion" - implementation "com.google.android.play:$googlePlayInAppUpdateVersion" + implementation "com.google.android.play:app-update:$googlePlayInAppUpdateVersion" implementation "com.google.android.play:app-update-ktx:$googlePlayInAppUpdateVersion" implementation "androidx.arch.core:core-common:$androidxArchCoreVersion" From 7ae9956e52ca62daa1a65bb1f031ac5e8aa585be Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 3 Apr 2023 09:46:08 +0530 Subject: [PATCH 10/81] Fixes: lint issue for now --- .../src/main/java/org/wordpress/android/InAppUpdateManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index ba46ed9983a4..1576655aa484 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -100,6 +100,7 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana private fun shouldRequestUpdate(): Boolean { //todo: add logic to check the time since last update request + return true } From dd460584034ecd1eebf916423717414a29016a1c Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 3 Apr 2023 12:58:22 +0530 Subject: [PATCH 11/81] Adds: logs for checking the update status --- .../wordpress/android/InAppUpdateManager.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 1576655aa484..67b23b7fd13a 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -1,6 +1,7 @@ package org.wordpress.android import android.app.Activity +import android.util.Log import com.google.android.gms.tasks.Task import com.google.android.play.core.appupdate.AppUpdateInfo import com.google.android.play.core.appupdate.AppUpdateManager @@ -27,16 +28,24 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } fun checkForAppUpdate(activity: Activity) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called") if (!shouldRequestUpdate()) return + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update") val appUpdateInfoTask = appUpdateManager.appUpdateInfo appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, success") if (isImmediateUpdateNecessary(appUpdateInfo)) { requestImmediateUpdate(appUpdateInfo, activity) } else if (isFlexibleUpdateNecessary(appUpdateInfo)) { requestFlexibleUpdate(appUpdateInfo, activity) } } + + appUpdateInfoTask.addOnFailureListener { exception -> + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, failure") + Log.e("AppUpdateChecker", exception.message.toString()) + } } private fun isImmediateUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { @@ -80,14 +89,14 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana ) } - private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { return (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && isUpdatePriorityHigh(appUpdateInfo) } - private fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { return (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) @@ -103,6 +112,13 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana return true } + fun completeUpdate() { + appUpdateManager.completeUpdate() + } + + fun unregisterListener(installStateUpdatedListener: InstallStateUpdatedListener) { + appUpdateManager.unregisterListener(installStateUpdatedListener) + } companion object { const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 From 79dc058c62d161d4fae8856387a0b1638916027e Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 3 Apr 2023 13:03:28 +0530 Subject: [PATCH 12/81] Adds: Dagger logic for InAppUpdate manager --- .../android/modules/ApplicationModule.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index c80723422816..70e5ec91d239 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -7,11 +7,14 @@ import androidx.lifecycle.LiveData; import androidx.preference.PreferenceManager; +import com.google.android.play.core.appupdate.AppUpdateManager; +import com.google.android.play.core.appupdate.AppUpdateManagerFactory; import com.tenor.android.core.network.ApiClient; import com.tenor.android.core.network.ApiService; import com.tenor.android.core.network.IApiClient; import org.wordpress.android.BuildConfig; +import org.wordpress.android.InAppUpdateManager; import org.wordpress.android.ui.CommentFullScreenDialogFragment; import org.wordpress.android.ui.accounts.signup.SettingsUsernameChangerFragment; import org.wordpress.android.ui.accounts.signup.UsernameChangerFullScreenDialogFragment; @@ -146,4 +149,14 @@ public static WizardManager provideRestoreWizardManager( RestoreStepsProvider stepsProvider) { return new WizardManager<>(stepsProvider.getSteps()); } + + @Provides + public static InAppUpdateManager provideInAppUpdateManager(AppUpdateManager appUpdateManager) { + return new InAppUpdateManager(appUpdateManager); + } + + @Provides + public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Context context) { + return AppUpdateManagerFactory.create(context); + } } From 61f287bbb6f9872e7cc0ad59af75c845d843c135 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 3 Apr 2023 13:03:58 +0530 Subject: [PATCH 13/81] Adds: the logic for checking the updates on OnResume --- .../android/ui/main/WPMainActivity.java | 65 +++++++++++++++++-- WordPress/src/main/res/values/strings.xml | 1 + version.properties | 4 +- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 0faac663a45a..f7eaee79dcea 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -10,6 +10,7 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; +import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; @@ -27,11 +28,15 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.play.core.install.InstallStateUpdatedListener; +import com.google.android.play.core.install.model.InstallStatus; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.BuildConfig; +import org.wordpress.android.InAppUpdateManager; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; @@ -274,6 +279,8 @@ public class WPMainActivity extends LocaleAwareActivity implements @Inject BuildConfigWrapper mBuildConfigWrapper; + @Inject InAppUpdateManager mInAppUpdateManager; + /* * fragments implement this if their contents can be scrolled, called when user * requests to scroll to the top @@ -309,7 +316,6 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); - mConnectionBar = findViewById(R.id.connection_bar); mConnectionBar.setOnClickListener(v -> { // slide out the bar on click, then re-check connection after a brief delay @@ -366,10 +372,10 @@ public void onCreate(Bundle savedInstanceState) { } } else if (openedFromShortcut) { initSelectedSite(); - mShortcutsNavigator.showTargetScreen(getIntent().getStringExtra( - ShortcutsNavigator.ACTION_OPEN_SHORTCUT), this, getSelectedSite()); - showJetpackOverlayIfNeeded(getIntent().getStringExtra( - ShortcutsNavigator.ACTION_OPEN_SHORTCUT)); + mShortcutsNavigator.showTargetScreen(getIntent().getStringExtra( + ShortcutsNavigator.ACTION_OPEN_SHORTCUT), this, getSelectedSite()); + showJetpackOverlayIfNeeded(getIntent().getStringExtra( + ShortcutsNavigator.ACTION_OPEN_SHORTCUT)); } else if (openRequestedPage) { handleOpenPageIntent(getIntent()); } else if (isQuickStartRequestedFromPush) { @@ -1085,9 +1091,39 @@ protected void onResume() { && mBottomNav.getCurrentSelectedPage() == PageType.MY_SITE ); + checkForAnyPendingInAppUpdates(); + mFirstResume = false; } + private void checkForAnyPendingInAppUpdates() { + mInAppUpdateManager.checkForAppUpdate(WPMainActivity.this); + mInAppUpdateManager.registerUpdateListener(installStateUpdatedListener); + + mInAppUpdateManager.getInAppUpdateManager().addOnSuccessListener(appUpdateInfo -> { + Log.e("WPMainActivity", "checkForAnyPendingInAppUpdates: " + appUpdateInfo); + if (mInAppUpdateManager.isImmediateUpdateInProgress(appUpdateInfo)) { + mInAppUpdateManager.requestImmediateUpdate(appUpdateInfo, WPMainActivity.this); + } else if (mInAppUpdateManager.isFlexibleUpdateInProgress(appUpdateInfo)) { + showSnackBarForUpdate(); + } else { + mInAppUpdateManager.checkForAppUpdate(WPMainActivity.this); + mInAppUpdateManager.registerUpdateListener(installStateUpdatedListener); + } + }); + } + + InstallStateUpdatedListener installStateUpdatedListener = state -> { + Log.e("WPMainActivity", "installStateUpdatedListener: " + state.installStatus()); + if (state.installStatus() == InstallStatus.DOWNLOADED) { + showSnackBarForUpdate(); + } else if (state.installStatus() == InstallStatus.INSTALLED) { + removeInstallStateUpdateListener(); + } else { + AppLog.d(AppLog.T.MAIN, "InstallStateUpdatedListener: state: " + state.installStatus()); + } + }; + private void checkQuickStartNotificationStatus() { SiteModel selectedSite = getSelectedSite(); long selectedSiteLocalId = mSelectedSiteRepository.getSelectedSiteLocalId(); @@ -1411,9 +1447,18 @@ public void onClick(View v) { case RequestCodes.DOMAIN_REGISTRATION: passOnActivityResultToMySiteFragment(requestCode, resultCode, data); break; + case InAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: + case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: + if (resultCode == RESULT_CANCELED) { + removeInstallStateUpdateListener(); + } } } + private void removeInstallStateUpdateListener() { + mInAppUpdateManager.unregisterListener(installStateUpdatedListener); + } + private void appLanguageChanged() { // Recreate this activity (much like a configuration change) // We need to post this call to UI thread, since it's called from onActivityResult and the call interferes with @@ -1831,6 +1876,16 @@ public void onSetPromptReminderClick(final int siteId) { onActivityResult(RequestCodes.SITE_PICKER, resultCode, data); } + private void showSnackBarForUpdate() { + Log.e("WPMainActivity", "showSnackBarForUpdate()"); + Snackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) + .setAction(R.string.update_now, v -> { + mInAppUpdateManager.completeUpdate(); + // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); + }) + .show(); + } + // We dismiss the QuickStart SnackBar every time activity is paused because // SnackBar sometimes do not appear when another SnackBar is still visible, even in other activities (weird) @Override diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 89cbc05c74f5..1a7247a53172 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4453,4 +4453,5 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> @string/label_done_button @string/blaze_activity_title + "Update is available for the app " diff --git a/version.properties b/version.properties index da52ad27a995..0693d2b9e813 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ #Mon, 30 Aug 2021 11:48:28 +0200 -versionName=22.0-rc-1 -versionCode=1322 +versionName=21.0 +versionCode=1321 From a74c72d62ab6abc03d87d48a4f28b8e548ae29f5 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Thu, 16 Nov 2023 13:50:54 +0530 Subject: [PATCH 14/81] =?UTF-8?q?=E2=86=91=20Updates:=20the=20app=20versio?= =?UTF-8?q?n=20for=20testing=20purposes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.properties b/version.properties index 0b85f7db4f66..ba9e15e99b08 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -versionName=23.7-rc-1 -versionCode=1383 +versionName=in-app-updates-1 +versionCode=80000 From dcb8805519fc14a5db05c226ff8504a3c214630c Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Thu, 16 Nov 2023 21:06:14 +0530 Subject: [PATCH 15/81] =?UTF-8?q?=E2=86=91=20Updates:=20the=20usage=20to?= =?UTF-8?q?=20Wp=20Snackbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wordpress/android/ui/main/WPMainActivity.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 3ed58c8f0bec..1ac2ce3a6593 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -100,11 +100,11 @@ import org.wordpress.android.ui.main.WPMainNavigationView.OnPageListener; import org.wordpress.android.ui.main.WPMainNavigationView.PageType; import org.wordpress.android.ui.mlp.ModalLayoutPickerFragment; +import org.wordpress.android.ui.mysite.BloggingPromptsOnboardingListener; import org.wordpress.android.ui.mysite.MySiteFragment; import org.wordpress.android.ui.mysite.MySiteViewModel; import org.wordpress.android.ui.mysite.SelectedSiteRepository; import org.wordpress.android.ui.mysite.cards.quickstart.QuickStartRepository; -import org.wordpress.android.ui.mysite.BloggingPromptsOnboardingListener; import org.wordpress.android.ui.notifications.NotificationEvents; import org.wordpress.android.ui.notifications.NotificationsListFragment; import org.wordpress.android.ui.notifications.SystemNotificationsTracker; @@ -164,6 +164,7 @@ import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel; import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel.CreatePageDashboardSource; import org.wordpress.android.widgets.AppRatingDialog; +import org.wordpress.android.widgets.WPSnackbar; import org.wordpress.android.workers.notification.createsite.CreateSiteNotificationScheduler; import org.wordpress.android.workers.weeklyroundup.WeeklyRoundupScheduler; @@ -1473,7 +1474,10 @@ public void onClick(View v) { case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: if (resultCode == RESULT_CANCELED) { removeInstallStateUpdateListener(); + break; } + showSnackBarForUpdate(); + break; } } @@ -1900,12 +1904,12 @@ public void onSetPromptReminderClick(final int siteId) { private void showSnackBarForUpdate() { Log.e("WPMainActivity", "showSnackBarForUpdate()"); - Snackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) - .setAction(R.string.update_now, v -> { + WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) + .setAction(R.string.update_now, v -> { mInAppUpdateManager.completeUpdate(); // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); }) - .show(); + .show(); } // We dismiss the QuickStart SnackBar every time activity is paused because From 5089d993d7f1ff2617659368c94b340a4ce6fd23 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Fri, 17 Nov 2023 14:00:24 +0530 Subject: [PATCH 16/81] + Adds: more logging to the update manager --- .../wordpress/android/InAppUpdateManager.kt | 39 +++++++++++++------ .../android/ui/main/WPMainActivity.java | 13 +++---- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 67b23b7fd13a..2e94364489b5 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -34,12 +34,15 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update") val appUpdateInfoTask = appUpdateManager.appUpdateInfo appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + Log.e("AppUpdateChecker", appUpdateInfo.toString()) + Log.e("AppUpdateChecker", appUpdateInfo.updateAvailability().toString()) Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, success") if (isImmediateUpdateNecessary(appUpdateInfo)) { requestImmediateUpdate(appUpdateInfo, activity) } else if (isFlexibleUpdateNecessary(appUpdateInfo)) { requestFlexibleUpdate(appUpdateInfo, activity) } + requestFlexibleUpdate(appUpdateInfo, activity) } appUpdateInfoTask.addOnFailureListener { exception -> @@ -72,21 +75,33 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, - AppUpdateType.IMMEDIATE, - activity, - APP_UPDATE_IMMEDIATE_REQUEST_CODE - ) + Log.e("AppUpdateChecker", "requestImmediateUpdate called") + try { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + AppUpdateType.IMMEDIATE, + activity, + APP_UPDATE_IMMEDIATE_REQUEST_CODE + ) + } catch (e: Exception) { + Log.e("AppUpdateChecker", "requestImmediateUpdate called, exception") + Log.e("AppUpdateChecker", e.message.toString()) + } } fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, - AppUpdateType.FLEXIBLE, - activity, - APP_UPDATE_FLEXIBLE_REQUEST_CODE - ) + Log.e("AppUpdateChecker", "requestFlexibleUpdate called") + try { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + AppUpdateType.FLEXIBLE, + activity, + APP_UPDATE_FLEXIBLE_REQUEST_CODE + ) + } catch (e: Exception) { + Log.e("AppUpdateChecker", "requestFlexibleUpdate called, exception") + Log.e("AppUpdateChecker", e.message.toString()) + } } fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 1ac2ce3a6593..1051b26b6860 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -1138,9 +1138,6 @@ protected void onResume() { } private void checkForAnyPendingInAppUpdates() { - mInAppUpdateManager.checkForAppUpdate(WPMainActivity.this); - mInAppUpdateManager.registerUpdateListener(installStateUpdatedListener); - mInAppUpdateManager.getInAppUpdateManager().addOnSuccessListener(appUpdateInfo -> { Log.e("WPMainActivity", "checkForAnyPendingInAppUpdates: " + appUpdateInfo); if (mInAppUpdateManager.isImmediateUpdateInProgress(appUpdateInfo)) { @@ -1149,19 +1146,19 @@ private void checkForAnyPendingInAppUpdates() { showSnackBarForUpdate(); } else { mInAppUpdateManager.checkForAppUpdate(WPMainActivity.this); - mInAppUpdateManager.registerUpdateListener(installStateUpdatedListener); + mInAppUpdateManager.registerUpdateListener(mInstallStateUpdatedListener); } }); } - InstallStateUpdatedListener installStateUpdatedListener = state -> { + @Nullable InstallStateUpdatedListener mInstallStateUpdatedListener = state -> { Log.e("WPMainActivity", "installStateUpdatedListener: " + state.installStatus()); if (state.installStatus() == InstallStatus.DOWNLOADED) { showSnackBarForUpdate(); } else if (state.installStatus() == InstallStatus.INSTALLED) { removeInstallStateUpdateListener(); } else { - AppLog.d(AppLog.T.MAIN, "InstallStateUpdatedListener: state: " + state.installStatus()); + Log.e("WPMainActivity", "InstallStateUpdatedListener: state: " + state.installStatus()); } }; @@ -1343,6 +1340,7 @@ private void setSite(Intent data) { @Override @SuppressWarnings("deprecation") public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.e("WPMainActivity", "onActivityResult: " + requestCode + " " + resultCode); super.onActivityResult(requestCode, resultCode, data); if (!mSelectedSiteRepository.hasSelectedSite()) { initSelectedSite(); @@ -1472,6 +1470,7 @@ public void onClick(View v) { break; case InAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: + Log.e("Update request code", "onActivityResult: " + requestCode + " " + resultCode); if (resultCode == RESULT_CANCELED) { removeInstallStateUpdateListener(); break; @@ -1482,7 +1481,7 @@ public void onClick(View v) { } private void removeInstallStateUpdateListener() { - mInAppUpdateManager.unregisterListener(installStateUpdatedListener); + mInAppUpdateManager.unregisterListener(mInstallStateUpdatedListener); } private void appLanguageChanged() { From ad2a75dffbec2fbec3154e66ed8857bcd66e4aea Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Fri, 17 Nov 2023 17:06:46 +0530 Subject: [PATCH 17/81] =?UTF-8?q?=E2=86=91=20Updates:=20the=20app=20update?= =?UTF-8?q?=20manager=20to=20check=20for=20update=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wordpress/android/InAppUpdateManager.kt | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 2e94364489b5..f312aeaf89ba 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -37,12 +37,38 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana Log.e("AppUpdateChecker", appUpdateInfo.toString()) Log.e("AppUpdateChecker", appUpdateInfo.updateAvailability().toString()) Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, success") - if (isImmediateUpdateNecessary(appUpdateInfo)) { - requestImmediateUpdate(appUpdateInfo, activity) - } else if (isFlexibleUpdateNecessary(appUpdateInfo)) { - requestFlexibleUpdate(appUpdateInfo, activity) + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_NOT_AVAILABLE) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, no update available") + } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + if (isImmediateUpdateNecessary(appUpdateInfo)) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, immediate update") + requestImmediateUpdate(appUpdateInfo, activity) + } else { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update") + requestFlexibleUpdate(appUpdateInfo, activity) + } + } else if ( + appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + ) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update in progress") + if (isImmediateUpdateInProgress(appUpdateInfo)) { + Log.e( + "AppUpdateChecker", + "checkPlayStoreUpdate called, checcking update, immediate update in progress" + ) + requestImmediateUpdate(appUpdateInfo, activity) + } else if (isFlexibleUpdateInProgress(appUpdateInfo)) { + Log.e( + "AppUpdateChecker", + "checkPlayStoreUpdate called, checcking update, flexible update in progress" + ) + requestFlexibleUpdate(appUpdateInfo, activity) + } + } else { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + return@addOnSuccessListener } - requestFlexibleUpdate(appUpdateInfo, activity) } appUpdateInfoTask.addOnFailureListener { exception -> @@ -52,8 +78,7 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } private fun isImmediateUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE - && (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + return (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && isUpdatePriorityHigh(appUpdateInfo)) || isClientVersionOlderThanThreshold( appUpdateInfo ) @@ -68,12 +93,6 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana ?: -1) >= MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES } - private fun isFlexibleUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - && !isUpdatePriorityHigh(appUpdateInfo) - } - fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { Log.e("AppUpdateChecker", "requestImmediateUpdate called") try { From f3c9bbed985dbe9ee1b4ee96e631d75a2d5c2af5 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Fri, 17 Nov 2023 17:07:01 +0530 Subject: [PATCH 18/81] =?UTF-8?q?=E2=86=91=20Updates:=20the=20install=20st?= =?UTF-8?q?ate=20listener=20to=20use=20various=20download=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/ui/main/WPMainActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 1051b26b6860..d50b63f34ebf 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -1154,11 +1154,15 @@ private void checkForAnyPendingInAppUpdates() { @Nullable InstallStateUpdatedListener mInstallStateUpdatedListener = state -> { Log.e("WPMainActivity", "installStateUpdatedListener: " + state.installStatus()); if (state.installStatus() == InstallStatus.DOWNLOADED) { + removeInstallStateUpdateListener(); showSnackBarForUpdate(); } else if (state.installStatus() == InstallStatus.INSTALLED) { removeInstallStateUpdateListener(); - } else { - Log.e("WPMainActivity", "InstallStateUpdatedListener: state: " + state.installStatus()); + showSnackBarForUpdate(); + } else if (state.installStatus() == InstallStatus.CANCELED || InstallStatus.FAILED == state.installStatus()) { + removeInstallStateUpdateListener(); + } else if (state.installStatus() == InstallStatus.PENDING) { + showSnackBarForUpdate(); } }; @@ -1284,7 +1288,7 @@ private void trackLastVisiblePage(PageType pageType, boolean trackAnalytics) { case MY_SITE: ActivityId.trackLastActivity(ActivityId.MY_SITE); if (trackAnalytics) { - mAnalyticsTrackerWrapper.track(AnalyticsTracker.Stat.MY_SITE_ACCESSED, getSelectedSite()); + mAnalyticsTrackerWrapper.track(AnalyticsTracker.Stat.MY_SITE_ACCESSED, getSelectedSite()); } break; case READER: @@ -1905,9 +1909,9 @@ private void showSnackBarForUpdate() { Log.e("WPMainActivity", "showSnackBarForUpdate()"); WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) .setAction(R.string.update_now, v -> { - mInAppUpdateManager.completeUpdate(); - // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); - }) + mInAppUpdateManager.completeUpdate(); + // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); + }) .show(); } From bcf890613658ddef5d6977d9931bb0a32b41c2bd Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 6 May 2024 16:08:03 +0530 Subject: [PATCH 19/81] * Fixes: formatting --- .../main/java/org/wordpress/android/InAppUpdateManager.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index f312aeaf89ba..1080a4ff63da 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -14,11 +14,8 @@ import javax.inject.Singleton private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 -private const val MIN_DURATION_TO_WAIT_FOR_UPDATE_PROMPT: Int = 7 - @Singleton class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateManager) { - fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { appUpdateManager.registerListener(installStateUpdatedListener) } @@ -138,11 +135,11 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } private fun setLastUpdateRequestedTime() { - //todo: add logic to save the time when the update request was made + // todo: add logic to save the time when the update request was made } private fun shouldRequestUpdate(): Boolean { - //todo: add logic to check the time since last update request + // todo: add logic to check the time since last update request return true } From 0dbd58812fb18e6ab4208fafc832ed58e2fed631 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 6 May 2024 16:10:19 +0530 Subject: [PATCH 20/81] * Fixes: redundant imports --- .../java/org/wordpress/android/ui/main/WPMainActivity.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index f715dbad6436..8307b6c35c74 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -31,13 +31,11 @@ import com.google.android.gms.tasks.Task; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.google.android.play.core.install.InstallStateUpdatedListener; +import com.google.android.play.core.install.model.InstallStatus; import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; -import com.google.android.play.core.review.model.ReviewErrorCode; -import com.google.android.material.snackbar.Snackbar; -import com.google.android.play.core.install.InstallStateUpdatedListener; -import com.google.android.play.core.install.model.InstallStatus; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; From 8d030fb102c5d7fb8b5dbb4243e24089dc8b88ec Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 6 May 2024 16:39:44 +0530 Subject: [PATCH 21/81] * Fixes: Checkstyle error --- .../java/org/wordpress/android/modules/ApplicationModule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 42737f6099be..de7d8d5e1248 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -13,6 +13,7 @@ import com.tenor.android.core.network.ApiClient; import com.tenor.android.core.network.ApiService; import com.tenor.android.core.network.IApiClient; + import org.wordpress.android.BuildConfig; import org.wordpress.android.InAppUpdateManager; import org.wordpress.android.ui.ActivityNavigator; From 9eaca957aca81fc9735198601eef05b95d71f856 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Mon, 6 May 2024 16:42:52 +0530 Subject: [PATCH 22/81] * Fixes: detekt issues --- .../java/org/wordpress/android/InAppUpdateManager.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 1080a4ff63da..212b280200b9 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -14,7 +14,10 @@ import javax.inject.Singleton private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 +private const val UPDATE_PRIORITY_DEFAULT: Int = 4 + @Singleton +@Suppress("TooManyFunctions") class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateManager) { fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { appUpdateManager.registerListener(installStateUpdatedListener) @@ -82,7 +85,7 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } private fun isUpdatePriorityHigh(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updatePriority > 4 + return appUpdateInfo.updatePriority > UPDATE_PRIORITY_DEFAULT } private fun isClientVersionOlderThanThreshold(appUpdateInfo: AppUpdateInfo): Boolean { @@ -90,6 +93,7 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana ?: -1) >= MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES } + @Suppress("TooGenericExceptionCaught") fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { Log.e("AppUpdateChecker", "requestImmediateUpdate called") try { @@ -105,7 +109,8 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana } } - fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + @Suppress("TooGenericExceptionCaught") + private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { Log.e("AppUpdateChecker", "requestFlexibleUpdate called") try { appUpdateManager.startUpdateFlowForResult( @@ -134,10 +139,12 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana && !isUpdatePriorityHigh(appUpdateInfo) } + @Suppress("unused") private fun setLastUpdateRequestedTime() { // todo: add logic to save the time when the update request was made } + @Suppress("unused","FunctionOnlyReturningConstant") private fun shouldRequestUpdate(): Boolean { // todo: add logic to check the time since last update request return true From 9180ab8922cb4378904fd66435e52db5d28f94ef Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 7 May 2024 11:54:54 +0300 Subject: [PATCH 23/81] Updates: in app updates lib version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fb7537f3d504..3a5f409ab54d 100644 --- a/build.gradle +++ b/build.gradle @@ -90,7 +90,7 @@ ext { squareupRetrofitVersion = '2.9.0' uCropVersion = '2.2.8' zendeskVersion = '5.1.2' - googlePlayInAppUpdateVersion = '2.0.1' + googlePlayInAppUpdateVersion = '2.1.0' // react native facebookReactVersion = '0.73.3' From 0f283808af9191870ebdb9ececdfd828505782d3 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 7 May 2024 14:43:32 +0300 Subject: [PATCH 24/81] Adds: InAppUpdateBlockingVersionConfig --- .../wordpress/android/InAppUpdateManager.kt | 14 ++++++++++---- .../android/modules/ApplicationModule.java | 7 +------ .../config/InAppUpdateBlockingVersionConfig.kt | 18 ++++++++++++++++++ .../android/util/config/RemoteConfigWrapper.kt | 4 +++- 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index 212b280200b9..e8a38428f31b 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -5,10 +5,13 @@ import android.util.Log import com.google.android.gms.tasks.Task import com.google.android.play.core.appupdate.AppUpdateInfo import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateOptions import com.google.android.play.core.install.InstallStateUpdatedListener import com.google.android.play.core.install.model.AppUpdateType import com.google.android.play.core.install.model.UpdateAvailability import com.google.android.play.core.ktx.updatePriority +import org.wordpress.android.util.config.RemoteConfigWrapper +import javax.inject.Inject import javax.inject.Singleton @@ -18,7 +21,10 @@ private const val UPDATE_PRIORITY_DEFAULT: Int = 4 @Singleton @Suppress("TooManyFunctions") -class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateManager) { +class InAppUpdateManager @Inject constructor( + private val appUpdateManager: AppUpdateManager, + private val remoteConfigWrapper: RemoteConfigWrapper +) { fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { appUpdateManager.registerListener(installStateUpdatedListener) } @@ -99,8 +105,8 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana try { appUpdateManager.startUpdateFlowForResult( appUpdateInfo, - AppUpdateType.IMMEDIATE, activity, + AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(), APP_UPDATE_IMMEDIATE_REQUEST_CODE ) } catch (e: Exception) { @@ -115,9 +121,9 @@ class InAppUpdateManager constructor(private val appUpdateManager: AppUpdateMana try { appUpdateManager.startUpdateFlowForResult( appUpdateInfo, - AppUpdateType.FLEXIBLE, activity, - APP_UPDATE_FLEXIBLE_REQUEST_CODE + AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build(), + APP_UPDATE_IMMEDIATE_REQUEST_CODE ) } catch (e: Exception) { Log.e("AppUpdateChecker", "requestFlexibleUpdate called, exception") diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index de7d8d5e1248..8b0cf4986da5 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -78,12 +78,7 @@ public static WizardManager provideRestoreWizardManager( RestoreStepsProvider stepsProvider) { return new WizardManager<>(stepsProvider.getSteps()); } - - @Provides - public static InAppUpdateManager provideInAppUpdateManager(AppUpdateManager appUpdateManager) { - return new InAppUpdateManager(appUpdateManager); - } - + @Provides public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Context context) { return AppUpdateManagerFactory.create(context); diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt new file mode 100644 index 000000000000..06cc3a122caa --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "in_app_update_blocking_version" +const val IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" + +@RemoteFieldDefaultGenerater( + remoteField = IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, + defaultValue = IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT +) + +class InAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt index 3113d9b84bfa..2334d6230a5f 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt @@ -7,9 +7,11 @@ import javax.inject.Singleton class RemoteConfigWrapper @Inject constructor( private val openWebLinksWithJetpackFlowFrequencyConfig: OpenWebLinksWithJetpackFlowFrequencyConfig, private val codeableGetFreeEstimateUrlConfig: CodeableGetFreeEstimateUrlConfig, - private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig + private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig, + private val inAppUpdateBlockingVersionConfig: InAppUpdateBlockingVersionConfig, ) { fun getOpenWebLinksWithJetpackFlowFrequency() = openWebLinksWithJetpackFlowFrequencyConfig.getValue() fun getPerformanceMonitoringSampleRate() = performanceMonitoringSampleRateConfig.getValue() fun getCodeableGetFreeEstimateUrl() = codeableGetFreeEstimateUrlConfig.getValue() + fun getInAppUpdateBlockingVersion() = inAppUpdateBlockingVersionConfig.getValue() } From fb0cc20604d926908b6690ec909cbb921ce65d6a Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 7 May 2024 17:51:50 +0300 Subject: [PATCH 25/81] Adds: functions in InAppUpdateManager for immediate updates --- .../wordpress/android/InAppUpdateManager.kt | 64 +++++++++++++------ .../android/modules/ApplicationModule.java | 2 +- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt index e8a38428f31b..a36cb13bdc3f 100644 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt @@ -9,21 +9,20 @@ import com.google.android.play.core.appupdate.AppUpdateOptions import com.google.android.play.core.install.InstallStateUpdatedListener import com.google.android.play.core.install.model.AppUpdateType import com.google.android.play.core.install.model.UpdateAvailability -import com.google.android.play.core.ktx.updatePriority +import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.config.RemoteConfigWrapper import javax.inject.Inject - import javax.inject.Singleton -private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 -private const val UPDATE_PRIORITY_DEFAULT: Int = 4 +private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 @Singleton @Suppress("TooManyFunctions") class InAppUpdateManager @Inject constructor( private val appUpdateManager: AppUpdateManager, - private val remoteConfigWrapper: RemoteConfigWrapper + private val remoteConfigWrapper: RemoteConfigWrapper, + private val buildConfigWrapper: BuildConfigWrapper ) { fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { appUpdateManager.registerListener(installStateUpdatedListener) @@ -47,7 +46,7 @@ class InAppUpdateManager @Inject constructor( Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, no update available") } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") - if (isImmediateUpdateNecessary(appUpdateInfo)) { + if (isImmediateUpdateNecessary()) { Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, immediate update") requestImmediateUpdate(appUpdateInfo, activity) } else { @@ -83,17 +82,6 @@ class InAppUpdateManager @Inject constructor( } } - private fun isImmediateUpdateNecessary(appUpdateInfo: AppUpdateInfo): Boolean { - return (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - && isUpdatePriorityHigh(appUpdateInfo)) || isClientVersionOlderThanThreshold( - appUpdateInfo - ) - } - - private fun isUpdatePriorityHigh(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updatePriority > UPDATE_PRIORITY_DEFAULT - } - private fun isClientVersionOlderThanThreshold(appUpdateInfo: AppUpdateInfo): Boolean { return (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES @@ -135,14 +123,14 @@ class InAppUpdateManager @Inject constructor( return (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - && isUpdatePriorityHigh(appUpdateInfo) + && isImmediateUpdateNecessary() } fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { return (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - && !isUpdatePriorityHigh(appUpdateInfo) + && !isImmediateUpdateNecessary() } @Suppress("unused") @@ -164,6 +152,44 @@ class InAppUpdateManager @Inject constructor( appUpdateManager.unregisterListener(installStateUpdatedListener) } + /** + * Retrieves the current version code of the application. + * + * This version code is obtained from the application's build configuration. + * + * @return The current application version code. + */ + private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() + + /** + * Retrieves the version code of the last known update that requires blocking. + * + * This value is sourced from a remote configuration that specifies the + * version of the application that requires immediate blocking updates. + * + * @return The version code of the last blocking app update. + */ + private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() + + /** + * Extracts the available version code for the app update from the given update information. + * + * The available version code indicates the most recent update version that's available + * and ready to be installed on the user's device. + * + * @param appUpdateInfo The update information object that contains version details. + * @return The available version code for the app update. + */ + private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() + + /** + * Checks if an immediate app update is required based on the current app version + * and the last known blocking version. + * + * @return `true` if the current app version is lower than the last blocking app version, otherwise `false`. + */ + private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() + companion object { const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 8b0cf4986da5..02e43a8bd62e 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -78,7 +78,7 @@ public static WizardManager provideRestoreWizardManager( RestoreStepsProvider stepsProvider) { return new WizardManager<>(stepsProvider.getSteps()); } - + @Provides public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Context context) { return AppUpdateManagerFactory.create(context); From 19bda63969bbdb3b93542e1909704d8216a4a73c Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 8 May 2024 12:19:45 +0300 Subject: [PATCH 26/81] Refactors: InAppUpdateManager --- .../wordpress/android/InAppUpdateManager.kt | 197 -------------- .../inappupdate/IInAppUpdateListener.kt | 10 + .../android/inappupdate/InAppUpdateManager.kt | 254 ++++++++++++++++++ .../android/modules/ApplicationModule.java | 18 +- .../android/ui/main/WPMainActivity.java | 96 ++++--- 5 files changed, 336 insertions(+), 239 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt diff --git a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt deleted file mode 100644 index a36cb13bdc3f..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/InAppUpdateManager.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.wordpress.android - -import android.app.Activity -import android.util.Log -import com.google.android.gms.tasks.Task -import com.google.android.play.core.appupdate.AppUpdateInfo -import com.google.android.play.core.appupdate.AppUpdateManager -import com.google.android.play.core.appupdate.AppUpdateOptions -import com.google.android.play.core.install.InstallStateUpdatedListener -import com.google.android.play.core.install.model.AppUpdateType -import com.google.android.play.core.install.model.UpdateAvailability -import org.wordpress.android.util.BuildConfigWrapper -import org.wordpress.android.util.config.RemoteConfigWrapper -import javax.inject.Inject -import javax.inject.Singleton - - -private const val MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES: Int = 60 - -@Singleton -@Suppress("TooManyFunctions") -class InAppUpdateManager @Inject constructor( - private val appUpdateManager: AppUpdateManager, - private val remoteConfigWrapper: RemoteConfigWrapper, - private val buildConfigWrapper: BuildConfigWrapper -) { - fun registerUpdateListener(installStateUpdatedListener: InstallStateUpdatedListener) { - appUpdateManager.registerListener(installStateUpdatedListener) - } - - fun getInAppUpdateManager(): Task { - return appUpdateManager.appUpdateInfo - } - - fun checkForAppUpdate(activity: Activity) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called") - if (!shouldRequestUpdate()) - return - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update") - val appUpdateInfoTask = appUpdateManager.appUpdateInfo - appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> - Log.e("AppUpdateChecker", appUpdateInfo.toString()) - Log.e("AppUpdateChecker", appUpdateInfo.updateAvailability().toString()) - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, success") - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_NOT_AVAILABLE) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, no update available") - } else if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") - if (isImmediateUpdateNecessary()) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, immediate update") - requestImmediateUpdate(appUpdateInfo, activity) - } else { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update") - requestFlexibleUpdate(appUpdateInfo, activity) - } - } else if ( - appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS - ) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update in progress") - if (isImmediateUpdateInProgress(appUpdateInfo)) { - Log.e( - "AppUpdateChecker", - "checkPlayStoreUpdate called, checcking update, immediate update in progress" - ) - requestImmediateUpdate(appUpdateInfo, activity) - } else if (isFlexibleUpdateInProgress(appUpdateInfo)) { - Log.e( - "AppUpdateChecker", - "checkPlayStoreUpdate called, checcking update, flexible update in progress" - ) - requestFlexibleUpdate(appUpdateInfo, activity) - } - } else { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") - return@addOnSuccessListener - } - } - - appUpdateInfoTask.addOnFailureListener { exception -> - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, failure") - Log.e("AppUpdateChecker", exception.message.toString()) - } - } - - private fun isClientVersionOlderThanThreshold(appUpdateInfo: AppUpdateInfo): Boolean { - return (appUpdateInfo.clientVersionStalenessDays() - ?: -1) >= MAXIMUM_THRESHOLD_FOR_FLEXIBLE_UPDATES - } - - @Suppress("TooGenericExceptionCaught") - fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e("AppUpdateChecker", "requestImmediateUpdate called") - try { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, - activity, - AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(), - APP_UPDATE_IMMEDIATE_REQUEST_CODE - ) - } catch (e: Exception) { - Log.e("AppUpdateChecker", "requestImmediateUpdate called, exception") - Log.e("AppUpdateChecker", e.message.toString()) - } - } - - @Suppress("TooGenericExceptionCaught") - private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e("AppUpdateChecker", "requestFlexibleUpdate called") - try { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, - activity, - AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build(), - APP_UPDATE_IMMEDIATE_REQUEST_CODE - ) - } catch (e: Exception) { - Log.e("AppUpdateChecker", "requestFlexibleUpdate called, exception") - Log.e("AppUpdateChecker", e.message.toString()) - } - } - - fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - return (appUpdateInfo.updateAvailability() - == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - && isImmediateUpdateNecessary() - } - - fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - return (appUpdateInfo.updateAvailability() - == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - && !isImmediateUpdateNecessary() - } - - @Suppress("unused") - private fun setLastUpdateRequestedTime() { - // todo: add logic to save the time when the update request was made - } - - @Suppress("unused","FunctionOnlyReturningConstant") - private fun shouldRequestUpdate(): Boolean { - // todo: add logic to check the time since last update request - return true - } - - fun completeUpdate() { - appUpdateManager.completeUpdate() - } - - fun unregisterListener(installStateUpdatedListener: InstallStateUpdatedListener) { - appUpdateManager.unregisterListener(installStateUpdatedListener) - } - - /** - * Retrieves the current version code of the application. - * - * This version code is obtained from the application's build configuration. - * - * @return The current application version code. - */ - private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() - - /** - * Retrieves the version code of the last known update that requires blocking. - * - * This value is sourced from a remote configuration that specifies the - * version of the application that requires immediate blocking updates. - * - * @return The version code of the last blocking app update. - */ - private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() - - /** - * Extracts the available version code for the app update from the given update information. - * - * The available version code indicates the most recent update version that's available - * and ready to be installed on the user's device. - * - * @param appUpdateInfo The update information object that contains version details. - * @return The available version code for the app update. - */ - private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() - - /** - * Checks if an immediate app update is required based on the current app version - * and the last known blocking version. - * - * @return `true` if the current app version is lower than the last blocking app version, otherwise `false`. - */ - private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() - - companion object { - const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 - const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt new file mode 100644 index 000000000000..114445ae8266 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt @@ -0,0 +1,10 @@ +package org.wordpress.android.inappupdate + +interface IInAppUpdateListener { + fun onAppUpdateStarted(type: Int) + fun onAppUpdateDownloaded() + fun onAppUpdateInstalled() + fun onAppUpdateFailed() + fun onAppUpdateCancelled() + fun onAppUpdatePending() +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt new file mode 100644 index 000000000000..2c2018420195 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -0,0 +1,254 @@ +package org.wordpress.android.inappupdate + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.util.Log +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.InstallState +import com.google.android.play.core.install.InstallStateUpdatedListener +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.InstallStatus.CANCELED +import com.google.android.play.core.install.model.InstallStatus.DOWNLOADED +import com.google.android.play.core.install.model.InstallStatus.DOWNLOADING +import com.google.android.play.core.install.model.InstallStatus.FAILED +import com.google.android.play.core.install.model.InstallStatus.INSTALLED +import com.google.android.play.core.install.model.InstallStatus.INSTALLING +import com.google.android.play.core.install.model.InstallStatus.PENDING +import com.google.android.play.core.install.model.InstallStatus.REQUIRES_UI_INTENT +import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS +import com.google.android.play.core.install.model.UpdateAvailability.UNKNOWN +import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE +import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_AVAILABLE + +import org.wordpress.android.util.BuildConfigWrapper +import org.wordpress.android.util.config.RemoteConfigWrapper +import javax.inject.Inject +import javax.inject.Singleton + + +@Singleton +@Suppress("TooManyFunctions") +class InAppUpdateManager( + private val appUpdateManager: AppUpdateManager, + private val remoteConfigWrapper: RemoteConfigWrapper, + private val buildConfigWrapper: BuildConfigWrapper, + private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} +) { + + private var updateListener: IInAppUpdateListener? = null + + fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called") + + updateListener = listener + + val appUpdateInfoTask = appUpdateManager.appUpdateInfo + + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + + Log.e("AppUpdateChecker", appUpdateInfo.toString()) + Log.e("AppUpdateChecker", appUpdateInfo.updateAvailability().toString()) + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checking update, success") + + val updateAvailability = appUpdateInfo.updateAvailability() + + when (updateAvailability) { + UPDATE_NOT_AVAILABLE -> { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checking update, no update available") + return@addOnSuccessListener + } + + UPDATE_AVAILABLE -> { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + if (isImmediateUpdateNecessary()) { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, immediate update") + requestImmediateUpdate(appUpdateInfo, activity) + } else { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update") + if (shouldRequestFlexibleUpdate(activity)) { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + } + + DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update in progress") + if (isImmediateUpdateInProgress(appUpdateInfo)) { + Log.e( + "AppUpdateChecker", + "checkPlayStoreUpdate called, checcking update, immediate update in progress" + ) + requestImmediateUpdate(appUpdateInfo, activity) + } else if (isFlexibleUpdateInProgress(appUpdateInfo)) { + Log.e( + "AppUpdateChecker", + "checkPlayStoreUpdate called, checcking update, flexible update in progress" + ) + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + + UNKNOWN -> { + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + return@addOnSuccessListener + } + } + } + + appUpdateInfoTask.addOnFailureListener { exception -> + Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, failure") + Log.e("AppUpdateChecker", exception.message.toString()) + } + } + + fun completeUpdate() { + appUpdateManager.completeUpdate() + } + + fun cancelAppUpdate() { + appUpdateManager.unregisterListener(installStateListener) + } + + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) + requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) + } + + private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.registerListener(installStateListener) + updateListener?.onAppUpdateStarted(AppUpdateType.FLEXIBLE) + requestUpdate(AppUpdateType.FLEXIBLE, appUpdateInfo, activity) + } + + @Suppress("TooGenericExceptionCaught") + private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { + Log.e("AppUpdateChecker", "requestUpdate called for type: $updateType") + val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { + APP_UPDATE_IMMEDIATE_REQUEST_CODE + } else { + setLastUpdateRequestedTime(activity) + APP_UPDATE_FLEXIBLE_REQUEST_CODE + } + try { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + activity, + AppUpdateOptions.newBuilder(updateType).build(), + requestCode + ) + } catch (e: Exception) { + Log.e("AppUpdateChecker", "requestUpdate for type: $updateType, exception occurred") + Log.e("AppUpdateChecker", e.message.toString()) + } + } + + private val installStateListener = object : InstallStateUpdatedListener { + @SuppressLint("SwitchIntDef") + override fun onStateUpdate(state: InstallState) { + when (state.installStatus()) { + DOWNLOADED -> { + updateListener?.onAppUpdateDownloaded() + } + INSTALLED -> { + updateListener?.onAppUpdateInstalled() + appUpdateManager.unregisterListener(this) // 'this' refers to the listener object + } + CANCELED -> { + updateListener?.onAppUpdateCancelled() + appUpdateManager.unregisterListener(this) + } + FAILED -> { + updateListener?.onAppUpdateFailed() + appUpdateManager.unregisterListener(this) + } + PENDING -> { + updateListener?.onAppUpdatePending() + } + DOWNLOADING, INSTALLING, InstallStatus.UNKNOWN -> { + // Do nothing + } + } + } + } + + private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + && isImmediateUpdateNecessary() + } + + private fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { + return appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) + && !isImmediateUpdateNecessary() + } + + private fun setLastUpdateRequestedTime(context: Context) { + val sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + sharedPref.edit().apply { + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTimeProvider.invoke()) + apply() + } + } + + private fun getLastUpdateRequestedTime(context: Context): Long { + val defaultValue = -1L + return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, defaultValue) + } + + private fun shouldRequestFlexibleUpdate(context: Context) = + currentTimeProvider.invoke() - getLastUpdateRequestedTime(context) >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS + + + /** + * Retrieves the current version code of the application. + * + * This version code is obtained from the application's build configuration. + * + * @return The current application version code. + */ + private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() + + /** + * Retrieves the version code of the last known update that requires blocking. + * + * This value is sourced from a remote configuration that specifies the + * version of the application that requires immediate blocking updates. + * + * @return The version code of the last blocking app update. + */ + private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() + + /** + * Extracts the available version code for the app update from the given update information. + * + * The available version code indicates the most recent update version that's available + * and ready to be installed on the user's device. + * + * @param appUpdateInfo The update information object that contains version details. + * @return The available version code for the app update. + */ + private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() + + /** + * Checks if an immediate app update is required based on the current app version + * and the last known blocking version. + * + * @return `true` if the current app version is lower than the last blocking app version, otherwise `false`. + */ + private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() + + companion object { + const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + + private const val PREF_NAME = "in_app_update_prefs" + private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" + private const val FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS: Long = 1000 * 60 * 60 * 24 * 5 // 5 days + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 02e43a8bd62e..13606cbdc3f5 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -15,7 +15,7 @@ import com.tenor.android.core.network.IApiClient; import org.wordpress.android.BuildConfig; -import org.wordpress.android.InAppUpdateManager; +import org.wordpress.android.inappupdate.InAppUpdateManager; import org.wordpress.android.ui.ActivityNavigator; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStep; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStepsProvider; @@ -24,6 +24,8 @@ import org.wordpress.android.ui.mediapicker.loader.TenorGifClient; import org.wordpress.android.ui.sitecreation.SiteCreationStep; import org.wordpress.android.ui.sitecreation.SiteCreationStepsProvider; +import org.wordpress.android.util.BuildConfigWrapper; +import org.wordpress.android.util.config.RemoteConfigWrapper; import org.wordpress.android.util.wizard.WizardManager; import org.wordpress.android.viewmodel.helpers.ConnectionStatus; import org.wordpress.android.viewmodel.helpers.ConnectionStatusLiveData; @@ -84,6 +86,20 @@ public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Conte return AppUpdateManagerFactory.create(context); } + @Provides + public static InAppUpdateManager provideInAppUpdateManager( + AppUpdateManager appUpdateManager, + RemoteConfigWrapper remoteConfigWrapper, + BuildConfigWrapper buildConfigWrapper + ) { + return new InAppUpdateManager( + appUpdateManager, + remoteConfigWrapper, + buildConfigWrapper, + System::currentTimeMillis + ); + } + @Provides public static ActivityNavigator provideActivityNavigator(@ApplicationContext Context context) { return new ActivityNavigator(); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 8307b6c35c74..d258df69be05 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -41,7 +41,8 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.BuildConfig; -import org.wordpress.android.InAppUpdateManager; +import org.wordpress.android.inappupdate.IInAppUpdateListener; +import org.wordpress.android.inappupdate.InAppUpdateManager; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; @@ -1204,40 +1205,54 @@ protected void onResume() { && mBottomNav.getCurrentSelectedPage() == PageType.MY_SITE ); - checkForAnyPendingInAppUpdates(); + checkForInAppUpdate(); mIsChangingConfiguration = false; } - private void checkForAnyPendingInAppUpdates() { - mInAppUpdateManager.getInAppUpdateManager().addOnSuccessListener(appUpdateInfo -> { - Log.e("WPMainActivity", "checkForAnyPendingInAppUpdates: " + appUpdateInfo); - if (mInAppUpdateManager.isImmediateUpdateInProgress(appUpdateInfo)) { - mInAppUpdateManager.requestImmediateUpdate(appUpdateInfo, WPMainActivity.this); - } else if (mInAppUpdateManager.isFlexibleUpdateInProgress(appUpdateInfo)) { - showSnackBarForUpdate(); - } else { - mInAppUpdateManager.checkForAppUpdate(WPMainActivity.this); - mInAppUpdateManager.registerUpdateListener(mInstallStateUpdatedListener); - } - }); + private void checkForInAppUpdate() { + Log.e("WPMainActivity", "checkForInAppUpdate() called"); + mInAppUpdateManager.checkForAppUpdate(this, mInAppUpdateListener); } - @Nullable InstallStateUpdatedListener mInstallStateUpdatedListener = state -> { - Log.e("WPMainActivity", "installStateUpdatedListener: " + state.installStatus()); - if (state.installStatus() == InstallStatus.DOWNLOADED) { - removeInstallStateUpdateListener(); - showSnackBarForUpdate(); - } else if (state.installStatus() == InstallStatus.INSTALLED) { - removeInstallStateUpdateListener(); - showSnackBarForUpdate(); - } else if (state.installStatus() == InstallStatus.CANCELED || InstallStatus.FAILED == state.installStatus()) { - removeInstallStateUpdateListener(); - } else if (state.installStatus() == InstallStatus.PENDING) { - showSnackBarForUpdate(); + @NonNull final IInAppUpdateListener mInAppUpdateListener = new IInAppUpdateListener() { + + @Override public void onAppUpdatePending() { + + } + + @Override public void onAppUpdateDownloaded() { + popupSnackbarForCompleteUpdate(); + } + @Override public void onAppUpdateStarted(int type) { + /* do nothing */ + } + + @Override public void onAppUpdateInstalled() { + /* do nothing */ + } + + @Override public void onAppUpdateFailed() { + /* do nothing */ + } + + @Override public void onAppUpdateCancelled() { + // Todo do I need to handle this? + /* do nothing */ } }; + private void popupSnackbarForCompleteUpdate() { + Log.e("WPMainActivity", "showSnackBarForUpdate()"); + WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) + .setAction(R.string.update_now, v -> { + mInAppUpdateManager.completeUpdate(); + + // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); + }) + .show(); + } + private void checkQuickStartNotificationStatus() { SiteModel selectedSite = getSelectedSite(); long selectedSiteLocalId = mSelectedSiteRepository.getSelectedSiteLocalId(); @@ -1507,16 +1522,24 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: Log.e("Update request code", "onActivityResult: " + requestCode + " " + resultCode); if (resultCode == RESULT_CANCELED) { - removeInstallStateUpdateListener(); + mInAppUpdateManager.cancelAppUpdate(); break; } - showSnackBarForUpdate(); + // Todo how to handle this? break; - } - } - private void removeInstallStateUpdateListener() { - mInAppUpdateManager.unregisterListener(mInstallStateUpdatedListener); + /* + There are several values you might receive from the onActivityResult() callback: + + RESULT_OK: The user has accepted the update. For immediate updates, you might not receive this callback + because the update should already be finished by the time control is given back to your app. + + RESULT_CANCELED: The user has denied or canceled the update. + + ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Some other error prevented either the user from providing + consent or the update from proceeding. + */ + } } private void appLanguageChanged() { @@ -1937,15 +1960,6 @@ public void onSetPromptReminderClick(final int siteId) { onActivityResult(RequestCodes.SITE_PICKER, resultCode, data); } - private void showSnackBarForUpdate() { - Log.e("WPMainActivity", "showSnackBarForUpdate()"); - WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) - .setAction(R.string.update_now, v -> { - mInAppUpdateManager.completeUpdate(); - // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); - }) - .show(); - } // We dismiss the QuickStart SnackBar every time activity is paused because // SnackBar sometimes do not appear when another SnackBar is still visible, even in other activities (weird) From a819803e04c83674f8bd018c5a48f2a28043d770 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 9 May 2024 13:10:13 +0300 Subject: [PATCH 27/81] Adds: separate inappupdate remote configs for wp and jp apps --- .../android/inappupdate/InAppUpdateManager.kt | 9 ++++++--- .../config/InAppUpdateBlockingVersionConfig.kt | 18 ------------------ .../JetpackInAppUpdateBlockingVersionConfig.kt | 18 ++++++++++++++++++ .../android/util/config/RemoteConfigWrapper.kt | 6 ++++-- ...ordPressInAppUpdateBlockingVersionConfig.kt | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 23 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index 2c2018420195..6200c8b23d9c 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -18,7 +18,6 @@ import com.google.android.play.core.install.model.InstallStatus.FAILED import com.google.android.play.core.install.model.InstallStatus.INSTALLED import com.google.android.play.core.install.model.InstallStatus.INSTALLING import com.google.android.play.core.install.model.InstallStatus.PENDING -import com.google.android.play.core.install.model.InstallStatus.REQUIRES_UI_INTENT import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS import com.google.android.play.core.install.model.UpdateAvailability.UNKNOWN import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE @@ -26,7 +25,6 @@ import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_ import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.config.RemoteConfigWrapper -import javax.inject.Inject import javax.inject.Singleton @@ -222,7 +220,12 @@ class InAppUpdateManager( * * @return The version code of the last blocking app update. */ - private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() + private fun getLastBlockingAppVersion(): Int = + if (buildConfigWrapper.isJetpackApp) { + remoteConfigWrapper.getJetpackInAppUpdateBlockingVersion() + } else { + remoteConfigWrapper.getWordPressInAppUpdateBlockingVersion() + } /** * Extracts the available version code for the app update from the given update information. diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt deleted file mode 100644 index 06cc3a122caa..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.annotation.RemoteFieldDefaultGenerater -import javax.inject.Inject - -const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "in_app_update_blocking_version" -const val IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" - -@RemoteFieldDefaultGenerater( - remoteField = IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, - defaultValue = IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT -) - -class InAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : - RemoteConfigField( - appConfig, - IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD - ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt new file mode 100644 index 000000000000..2b05ad01b21d --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "jp_in_app_update_blocking_version_android" +const val JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" + +@RemoteFieldDefaultGenerater( + remoteField = JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, + defaultValue = JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT +) + +class JetpackInAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt index 2334d6230a5f..a45b2fe7d172 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt @@ -8,10 +8,12 @@ class RemoteConfigWrapper @Inject constructor( private val openWebLinksWithJetpackFlowFrequencyConfig: OpenWebLinksWithJetpackFlowFrequencyConfig, private val codeableGetFreeEstimateUrlConfig: CodeableGetFreeEstimateUrlConfig, private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig, - private val inAppUpdateBlockingVersionConfig: InAppUpdateBlockingVersionConfig, + private val wordPressInAppUpdateBlockingVersionConfig: WordPressInAppUpdateBlockingVersionConfig, + private val jetpackInAppUpdateBlockingVersionConfig: JetpackInAppUpdateBlockingVersionConfig, ) { fun getOpenWebLinksWithJetpackFlowFrequency() = openWebLinksWithJetpackFlowFrequencyConfig.getValue() fun getPerformanceMonitoringSampleRate() = performanceMonitoringSampleRateConfig.getValue() fun getCodeableGetFreeEstimateUrl() = codeableGetFreeEstimateUrlConfig.getValue() - fun getInAppUpdateBlockingVersion() = inAppUpdateBlockingVersionConfig.getValue() + fun getWordPressInAppUpdateBlockingVersion() = wordPressInAppUpdateBlockingVersionConfig.getValue() + fun getJetpackInAppUpdateBlockingVersion() = jetpackInAppUpdateBlockingVersionConfig.getValue() } diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt new file mode 100644 index 000000000000..b5f1ad4efaf2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "wp_in_app_update_blocking_version_android" +const val WP_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" + +@RemoteFieldDefaultGenerater( + remoteField = WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, + defaultValue = WP_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT +) + +class WordPressInAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD + ) From a52d665a98e65808a0e2751a6d1ba124b2301166 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 9 May 2024 14:26:57 +0300 Subject: [PATCH 28/81] Fixes: InAppUpdateManager minor fixes --- .../android/inappupdate/InAppUpdateManager.kt | 48 ++++++++++++++----- .../android/modules/ApplicationModule.java | 2 + .../android/ui/main/WPMainActivity.java | 26 +++------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index 6200c8b23d9c..e0c348c55822 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -22,6 +22,7 @@ import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_T import com.google.android.play.core.install.model.UpdateAvailability.UNKNOWN import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_AVAILABLE +import dagger.hilt.android.qualifiers.ApplicationContext import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.config.RemoteConfigWrapper @@ -31,6 +32,7 @@ import javax.inject.Singleton @Singleton @Suppress("TooManyFunctions") class InAppUpdateManager( + @ApplicationContext private val applicationContext: Context, private val appUpdateManager: AppUpdateManager, private val remoteConfigWrapper: RemoteConfigWrapper, private val buildConfigWrapper: BuildConfigWrapper, @@ -67,7 +69,7 @@ class InAppUpdateManager( requestImmediateUpdate(appUpdateInfo, activity) } else { Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update") - if (shouldRequestFlexibleUpdate(activity)) { + if (shouldRequestFlexibleUpdate()) { requestFlexibleUpdate(appUpdateInfo, activity) } } @@ -103,7 +105,7 @@ class InAppUpdateManager( } } - fun completeUpdate() { + fun completeAppUpdate() { appUpdateManager.completeUpdate() } @@ -111,6 +113,10 @@ class InAppUpdateManager( appUpdateManager.unregisterListener(installStateListener) } + fun onUserAcceptedAppUpdate() { + resetLastUpdateRequestedTime() + } + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) @@ -128,7 +134,7 @@ class InAppUpdateManager( val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE } else { - setLastUpdateRequestedTime(activity) + setLastUpdateRequestedTime() APP_UPDATE_FLEXIBLE_REQUEST_CODE } try { @@ -141,6 +147,7 @@ class InAppUpdateManager( } catch (e: Exception) { Log.e("AppUpdateChecker", "requestUpdate for type: $updateType, exception occurred") Log.e("AppUpdateChecker", e.message.toString()) + appUpdateManager.unregisterListener(installStateListener) } } @@ -149,25 +156,36 @@ class InAppUpdateManager( override fun onStateUpdate(state: InstallState) { when (state.installStatus()) { DOWNLOADED -> { + Log.e("AppUpdateChecker", "installStateListener DOWNLOADED") updateListener?.onAppUpdateDownloaded() } INSTALLED -> { + Log.e("AppUpdateChecker", "installStateListener INSTALLED") updateListener?.onAppUpdateInstalled() appUpdateManager.unregisterListener(this) // 'this' refers to the listener object } CANCELED -> { + Log.e("AppUpdateChecker", "installStateListener CANCELED") updateListener?.onAppUpdateCancelled() appUpdateManager.unregisterListener(this) } FAILED -> { + Log.e("AppUpdateChecker", "installStateListener FAILED") updateListener?.onAppUpdateFailed() appUpdateManager.unregisterListener(this) } PENDING -> { + Log.e("AppUpdateChecker", "installStateListener PENDING") updateListener?.onAppUpdatePending() } - DOWNLOADING, INSTALLING, InstallStatus.UNKNOWN -> { - // Do nothing + DOWNLOADING -> { + Log.e("AppUpdateChecker", "installStateListener DOWNLOADING") + } + INSTALLING -> { + Log.e("AppUpdateChecker", "installStateListener INSTALLING") + } + InstallStatus.UNKNOWN -> { + Log.e("AppUpdateChecker", "installStateListener UNKNOWN") } } } @@ -185,22 +203,30 @@ class InAppUpdateManager( && !isImmediateUpdateNecessary() } - private fun setLastUpdateRequestedTime(context: Context) { - val sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + private fun setLastUpdateRequestedTime() { + val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTimeProvider.invoke()) apply() } } - private fun getLastUpdateRequestedTime(context: Context): Long { + private fun resetLastUpdateRequestedTime() { + val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + sharedPref.edit().apply { + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) + apply() + } + } + + private fun getLastUpdateRequestedTime(): Long { val defaultValue = -1L - return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + return applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, defaultValue) } - private fun shouldRequestFlexibleUpdate(context: Context) = - currentTimeProvider.invoke() - getLastUpdateRequestedTime(context) >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS + private fun shouldRequestFlexibleUpdate() = + currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS /** diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 13606cbdc3f5..d26b58208fc1 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -88,11 +88,13 @@ public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Conte @Provides public static InAppUpdateManager provideInAppUpdateManager( + @ApplicationContext Context context, AppUpdateManager appUpdateManager, RemoteConfigWrapper remoteConfigWrapper, BuildConfigWrapper buildConfigWrapper ) { return new InAppUpdateManager( + context, appUpdateManager, remoteConfigWrapper, buildConfigWrapper, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index d258df69be05..c9447342937f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -31,8 +31,6 @@ import com.google.android.gms.tasks.Task; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; -import com.google.android.play.core.install.InstallStateUpdatedListener; -import com.google.android.play.core.install.model.InstallStatus; import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; @@ -191,6 +189,7 @@ import javax.inject.Inject; import static androidx.lifecycle.Lifecycle.State.STARTED; +import static com.google.android.play.core.install.model.ActivityResult.RESULT_IN_APP_UPDATE_FAILED; import static org.wordpress.android.WordPress.SITE; import static org.wordpress.android.fluxc.store.SiteStore.CompleteQuickStartVariant.NEXT_STEPS; import static org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource.EMAIL; @@ -1246,7 +1245,7 @@ private void popupSnackbarForCompleteUpdate() { Log.e("WPMainActivity", "showSnackBarForUpdate()"); WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) .setAction(R.string.update_now, v -> { - mInAppUpdateManager.completeUpdate(); + mInAppUpdateManager.completeAppUpdate(); // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); }) @@ -1520,25 +1519,14 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { break; case InAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: - Log.e("Update request code", "onActivityResult: " + requestCode + " " + resultCode); - if (resultCode == RESULT_CANCELED) { + if (resultCode == RESULT_OK) { + // The user has accepted the update + mInAppUpdateManager.onUserAcceptedAppUpdate(); + } else if (resultCode == RESULT_CANCELED || resultCode == RESULT_IN_APP_UPDATE_FAILED) { + // The user denied the update or an error occurred mInAppUpdateManager.cancelAppUpdate(); - break; } - // Todo how to handle this? break; - - /* - There are several values you might receive from the onActivityResult() callback: - - RESULT_OK: The user has accepted the update. For immediate updates, you might not receive this callback - because the update should already be finished by the time control is given back to your app. - - RESULT_CANCELED: The user has denied or canceled the update. - - ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Some other error prevented either the user from providing - consent or the update from proceeding. - */ } } From 56144da0909ca628901058e7cb2440ebab204596 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 9 May 2024 17:35:42 +0300 Subject: [PATCH 29/81] wip --- .../org/wordpress/android/inappupdate/InAppUpdateManager.kt | 2 +- version.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index e0c348c55822..cae4e8055eb2 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -83,7 +83,7 @@ class InAppUpdateManager( "checkPlayStoreUpdate called, checcking update, immediate update in progress" ) requestImmediateUpdate(appUpdateInfo, activity) - } else if (isFlexibleUpdateInProgress(appUpdateInfo)) { + } else { Log.e( "AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update in progress" diff --git a/version.properties b/version.properties index ba9e15e99b08..d621f8335a07 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -versionName=in-app-updates-1 -versionCode=80000 +versionName=in-app-updates-11 +versionCode=90001 From 9577ac79d0740b2693f7a8dffcbb56f95015b657 Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Mon, 13 May 2024 21:05:39 +0200 Subject: [PATCH 30/81] Update Dangermattic --- Gemfile.lock | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8d2a8b367b52..631493a457ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -69,29 +69,16 @@ GEM no_proxy_fix octokit (>= 4.0) terminal-table (>= 1, < 4) - danger-dangermattic (1.0.0) + danger-dangermattic (1.0.2) danger (~> 9.4) - danger-junit (~> 1.0) danger-plugin-api (~> 1.0) danger-rubocop (~> 0.12) - danger-swiftlint (~> 0.35) - danger-xcode_summary (~> 1.0) - rubocop (~> 1.60) - danger-junit (1.0.2) - danger (> 2.0) - ox (~> 2.0) + rubocop (~> 1.61) danger-plugin-api (1.0.0) danger (> 2.0) danger-rubocop (0.12.0) danger rubocop (~> 1.0) - danger-swiftlint (0.35.0) - danger - rake (> 10) - thor (~> 1.0.0) - danger-xcode_summary (1.3.0) - danger-plugin-api (~> 1.0) - xcresult (~> 0.2) declarative (0.0.20) diffy (3.4.2) digest-crc (0.6.5) @@ -241,7 +228,7 @@ GEM concurrent-ruby (~> 1.0) java-properties (0.3.0) jmespath (1.6.2) - json (2.7.1) + json (2.7.2) jwt (2.8.0) base64 kramdown (2.4.0) @@ -254,7 +241,7 @@ GEM mini_portile2 (2.8.5) minitest (5.22.2) multi_json (1.15.0) - multipart-post (2.4.0) + multipart-post (2.4.1) mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) @@ -271,23 +258,22 @@ GEM options (2.3.2) optparse (0.4.0) os (1.1.4) - ox (2.14.17) parallel (1.24.0) - parser (3.3.0.5) + parser (3.3.1.0) ast (~> 2.4.1) racc plist (3.7.1) progress_bar (1.3.3) highline (>= 1.6, < 3) options (~> 2.3.0) - public_suffix (5.0.4) + public_suffix (5.0.5) racc (1.7.3) rainbow (3.1.1) - rake (13.1.0) + rake (13.2.1) rake-compiler (1.2.7) rake rchardet (1.8.0) - regexp_parser (2.9.0) + regexp_parser (2.9.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -296,7 +282,7 @@ GEM rexml (3.2.6) rmagick (4.3.0) rouge (2.0.7) - rubocop (1.60.2) + rubocop (1.63.5) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -304,11 +290,11 @@ GEM rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -327,7 +313,6 @@ GEM terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thor (1.0.1) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.2) @@ -349,7 +334,6 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - xcresult (0.2.1) PLATFORMS ruby From 568ab66584d74781f76a9d388a15dfdc2266d8fa Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Mon, 13 May 2024 21:12:42 +0200 Subject: [PATCH 31/81] Configure Danger on Linter Agent --- .buildkite/pipeline.yml | 10 ++++++++++ .github/workflows/run-danger.yml | 16 ++++++++++------ Dangerfile | 3 ++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 87d96c8032e3..c6c5356e0c4d 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -29,6 +29,16 @@ steps: ################# - group: "🕵️‍♂️ Linters" steps: + - label: "☢️ Danger - PR Check" + command: danger + key: danger + if: "build.pull_request.id != null" + retry: + manual: + permit_on_passed: true + agents: + queue: "linter" + - label: "🕵️ checkstyle" command: | cp gradle.properties-example gradle.properties diff --git a/.github/workflows/run-danger.yml b/.github/workflows/run-danger.yml index 856ab8cea46d..1031d64a7dd9 100644 --- a/.github/workflows/run-danger.yml +++ b/.github/workflows/run-danger.yml @@ -1,13 +1,17 @@ -name: ☢️ Danger +name: ☢️ Trigger Danger On Buildkite on: pull_request: - types: [opened, reopened, ready_for_review, synchronize, edited, labeled, unlabeled, milestoned, demilestoned] + types: [labeled, unlabeled, milestoned, demilestoned] jobs: dangermattic: - # runs on draft PRs only for opened / synchronize events - if: ${{ (github.event.pull_request.draft == false) || (github.event.pull_request.draft == true && contains(fromJSON('["opened", "synchronize"]'), github.event.action)) }} - uses: Automattic/dangermattic/.github/workflows/reusable-run-danger.yml@v1.0.0 + if: ${{ (github.event.pull_request.draft == false) }} + uses: Automattic/dangermattic/.github/workflows/reusable-retry-buildkite-step-on-events.yml@v1.1.0 + with: + org-slug: "automattic" + pipeline-slug: "wordpress-android" + retry-step-key: "danger" + build-commit-sha: "${{ github.event.pull_request.head.sha }}" secrets: - github-token: ${{ secrets.DANGERMATTIC_GITHUB_TOKEN }} + buildkite-api-token: ${{ secrets.TRIGGER_BK_BUILD_TOKEN }} diff --git a/Dangerfile b/Dangerfile index d439496653f9..ef55a1240632 100644 --- a/Dangerfile +++ b/Dangerfile @@ -3,7 +3,8 @@ github.dismiss_out_of_range_messages # `files: []` forces rubocop to scan all files, not just the ones modified in the PR -rubocop.lint(files: [], force_exclusion: true, inline_comment: true, fail_on_inline_comment: true, include_cop_names: true) +# Added a custom `rubocop_cmd` to prevent RuboCop from running using `bundle exec`, which we don't want on the linter agent +rubocop.lint(files: [], force_exclusion: true, inline_comment: true, fail_on_inline_comment: true, include_cop_names: true, rubocop_cmd: ': | rubocop') manifest_pr_checker.check_gemfile_lock_updated From 4a07459d13728c0cba052883a3dd913385cefb24 Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Mon, 13 May 2024 18:03:34 -0400 Subject: [PATCH 32/81] [Hack] Take 3. Using shared pref screen to store test flags. --- .../android/ui/debug/preferences/DebugPrefs.kt | 10 ++++++++++ .../DebugSharedPreferenceFlagsViewModel.kt | 7 ++++--- .../org/wordpress/android/ui/prefs/AppPrefs.java | 4 ++++ .../wordpress/android/ui/prefs/AppPrefsWrapper.kt | 6 +++++- .../viewmodel/main/WPMainActivityViewModel.kt | 12 +++++++++--- 5 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt new file mode 100644 index 000000000000..0ed4a2801880 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt @@ -0,0 +1,10 @@ +package org.wordpress.android.ui.debug.preferences + +import kotlin.reflect.KClass + +/** + * Class used to track + */ +enum class DebugPrefs(val key: String, val type: KClass<*>) { + ALWAYS_SHOW_ANNOUNCEMENT("prefs_always_show_announcement", Boolean::class) +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt index b7c99e4487e6..00d6050a3ed3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt @@ -15,9 +15,10 @@ class DebugSharedPreferenceFlagsViewModel @Inject constructor( val uiStateFlow = _uiStateFlow.asStateFlow() init { - val flags = prefsWrapper.getAllPrefs().mapNotNull { (key, value) -> - if (value is Boolean) key to value else null - }.toMap() + val flags = DebugPrefs.entries.mapNotNull { + // Only supporting boolean for now. + if (it.type == Boolean::class) it else null + }.associate { it.key to prefsWrapper.getDebugBooleanPref(it.key, false) } _uiStateFlow.value = flags } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java index 0396fb813f35..5bf4bdb7824b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java @@ -387,6 +387,10 @@ public static boolean getBoolean(PrefKey key, boolean def) { return Boolean.parseBoolean(value); } + public static boolean getRawBoolean(@NonNull final PrefKey key, boolean def) { + return prefs().getBoolean(key.name(), def); + } + public static void putBoolean(final PrefKey key, final boolean value) { prefs().edit().putBoolean(key.name(), value).apply(); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt index e844020b246f..4a835daca578 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt @@ -19,6 +19,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDa import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDataTypeSelectionViewModel.DataType.VIEWS import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDataTypeSelectionViewModel.DataType.VISITORS import org.wordpress.android.usecase.social.JetpackSocialFlow +import org.wordpress.android.util.BuildConfigWrapper import java.util.Date import javax.inject.Inject import javax.inject.Singleton @@ -31,7 +32,7 @@ import javax.inject.Singleton * */ @Singleton -class AppPrefsWrapper @Inject constructor() { +class AppPrefsWrapper @Inject constructor(val buildConfigWrapper: BuildConfigWrapper) { var featureAnnouncementShownVersion: Int get() = AppPrefs.getFeatureAnnouncementShownVersion() set(version) = AppPrefs.setFeatureAnnouncementShownVersion(version) @@ -451,6 +452,9 @@ class AppPrefsWrapper @Inject constructor() { fun getAllPrefs(): Map = AppPrefs.getAllPrefs() + fun getDebugBooleanPref(key: String, default: Boolean = false) = + buildConfigWrapper.isDebug() && AppPrefs.getRawBoolean({ key }, default) + fun setString(prefKey: PrefKey, value: String) { AppPrefs.setString(prefKey, value) } diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt index af5e5e430673..5b53ed60f895 100644 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt @@ -19,6 +19,7 @@ import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.bloggingprompts.BloggingPromptsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.bloggingprompts.BloggingPromptsSettingsHelper +import org.wordpress.android.ui.debug.preferences.DebugPrefs import org.wordpress.android.ui.main.MainActionListItem import org.wordpress.android.ui.main.MainActionListItem.ActionType import org.wordpress.android.ui.main.MainActionListItem.ActionType.ANSWER_BLOGGING_PROMPT @@ -297,9 +298,12 @@ class WPMainActivityViewModel @Inject constructor( launch { val currentVersionCode = buildConfigWrapper.getAppVersionCode() val previousVersionCode = appPrefsWrapper.lastFeatureAnnouncementAppVersionCode + val alwaysShowAnnouncement = appPrefsWrapper.getDebugBooleanPref( + DebugPrefs.ALWAYS_SHOW_ANNOUNCEMENT.key + ) // only proceed to feature announcement logic if we are upgrading the app - if (previousVersionCode != 0 && previousVersionCode < currentVersionCode) { + if (alwaysShowAnnouncement || previousVersionCode != 0 && previousVersionCode < currentVersionCode) { if (canShowFeatureAnnouncement()) { analyticsTracker.track(Stat.FEATURE_ANNOUNCEMENT_SHOWN_ON_APP_UPGRADE) _onFeatureAnnouncementRequested.call() @@ -337,9 +341,11 @@ class WPMainActivityViewModel @Inject constructor( private suspend fun canShowFeatureAnnouncement(): Boolean { val cachedAnnouncement = featureAnnouncementProvider.getLatestFeatureAnnouncement(true) + val alwaysShowAnnouncement = appPrefsWrapper.getDebugBooleanPref(DebugPrefs.ALWAYS_SHOW_ANNOUNCEMENT.key) return cachedAnnouncement != null && - cachedAnnouncement.canBeDisplayedOnAppUpgrade(buildConfigWrapper.getAppVersionName()) && - appPrefsWrapper.featureAnnouncementShownVersion < cachedAnnouncement.announcementVersion + (alwaysShowAnnouncement || + cachedAnnouncement.canBeDisplayedOnAppUpgrade(buildConfigWrapper.getAppVersionName()) && + appPrefsWrapper.featureAnnouncementShownVersion < cachedAnnouncement.announcementVersion) } private fun getExternalFocusPointInfo(task: QuickStartTask?): List { From 025b93011518e137d9a13ab909b0a30a609a912d Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Tue, 14 May 2024 01:39:11 -0700 Subject: [PATCH 33/81] Add null check --- .../android/ui/history/HistoryDetailContainerFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java index f51a98efabfa..cd848c1a34a6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java @@ -192,7 +192,9 @@ private ArrayList mapRevisionModelsToRevisions(@Nullable final List revisions = new ArrayList<>(); for (int i = 0; i < revisionModels.size(); i++) { final RevisionModel current = revisionModels.get(i); - revisions.add(new Revision(current)); + if (current != null) { + revisions.add(new Revision(current)); + } } return revisions; } From 6784eff763dddb65dedb9c392160efc252410016 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 14 May 2024 12:59:01 +0300 Subject: [PATCH 34/81] Adds: debug logs for InAppUpdateManager --- .../android/inappupdate/InAppUpdateManager.kt | 122 ++++++++++++------ 1 file changed, 84 insertions(+), 38 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index cae4e8055eb2..9d22da49ad16 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -42,7 +42,7 @@ class InAppUpdateManager( private var updateListener: IInAppUpdateListener? = null fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called") + Log.e(TAG, "checkForAppUpdate() entered") updateListener = listener @@ -50,25 +50,32 @@ class InAppUpdateManager( appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> - Log.e("AppUpdateChecker", appUpdateInfo.toString()) - Log.e("AppUpdateChecker", appUpdateInfo.updateAvailability().toString()) - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checking update, success") + Log.e(TAG, "checkForAppUpdate(): success") val updateAvailability = appUpdateInfo.updateAvailability() + // If the update is downloaded but not installed, + // notify the user to complete the update. + if (appUpdateInfo.installStatus() == DOWNLOADED) { + Log.e(TAG, "checkForAppUpdate(): appUpdateInfo.installStatus() == DOWNLOADED") + listener.onAppUpdateDownloaded() + Log.e(TAG, "checkForAppUpdate(): listener.onAppUpdateDownloaded() called") + return@addOnSuccessListener + } + when (updateAvailability) { UPDATE_NOT_AVAILABLE -> { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checking update, no update available") + Log.e(TAG, "checkForAppUpdate(): no update available") return@addOnSuccessListener } UPDATE_AVAILABLE -> { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + Log.e(TAG, "checkForAppUpdate(): update available") if (isImmediateUpdateNecessary()) { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, immediate update") + Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == true") requestImmediateUpdate(appUpdateInfo, activity) } else { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, flexible update") + Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == false") if (shouldRequestFlexibleUpdate()) { requestFlexibleUpdate(appUpdateInfo, activity) } @@ -76,53 +83,52 @@ class InAppUpdateManager( } DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update in progress") + Log.e(TAG, "checkForAppUpdate(): UPDATE_IN_PROGRESS") if (isImmediateUpdateInProgress(appUpdateInfo)) { - Log.e( - "AppUpdateChecker", - "checkPlayStoreUpdate called, checcking update, immediate update in progress" - ) + Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == true") requestImmediateUpdate(appUpdateInfo, activity) } else { - Log.e( - "AppUpdateChecker", - "checkPlayStoreUpdate called, checcking update, flexible update in progress" - ) + Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == false") requestFlexibleUpdate(appUpdateInfo, activity) } } UNKNOWN -> { - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, update available") + Log.e(TAG, "checkForAppUpdate(): UNKNOWN") return@addOnSuccessListener } } } appUpdateInfoTask.addOnFailureListener { exception -> - Log.e("AppUpdateChecker", "checkPlayStoreUpdate called, checcking update, failure") - Log.e("AppUpdateChecker", exception.message.toString()) + Log.e(TAG, "checkForAppUpdate():, checking update, failure") + Log.e(TAG, exception.message.toString()) } } fun completeAppUpdate() { + Log.e(TAG, "completeAppUpdate(): entered") appUpdateManager.completeUpdate() } fun cancelAppUpdate() { + Log.e(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) } fun onUserAcceptedAppUpdate() { + Log.e(TAG, "onUserAcceptedAppUpdate(): entered") resetLastUpdateRequestedTime() } private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + Log.e(TAG, "requestImmediateUpdate(): entered") updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) } private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + Log.e(TAG, "requestFlexibleUpdate(): entered") appUpdateManager.registerListener(installStateListener) updateListener?.onAppUpdateStarted(AppUpdateType.FLEXIBLE) requestUpdate(AppUpdateType.FLEXIBLE, appUpdateInfo, activity) @@ -130,7 +136,7 @@ class InAppUpdateManager( @Suppress("TooGenericExceptionCaught") private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e("AppUpdateChecker", "requestUpdate called for type: $updateType") + Log.e(TAG, "requestUpdate(): entered with updateType = $updateType") val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE } else { @@ -145,8 +151,8 @@ class InAppUpdateManager( requestCode ) } catch (e: Exception) { - Log.e("AppUpdateChecker", "requestUpdate for type: $updateType, exception occurred") - Log.e("AppUpdateChecker", e.message.toString()) + Log.e(TAG, "requestUpdate for type: $updateType, exception occurred") + Log.e(TAG, e.message.toString()) appUpdateManager.unregisterListener(installStateListener) } } @@ -156,62 +162,83 @@ class InAppUpdateManager( override fun onStateUpdate(state: InstallState) { when (state.installStatus()) { DOWNLOADED -> { - Log.e("AppUpdateChecker", "installStateListener DOWNLOADED") + Log.e(TAG, "installStateListener DOWNLOADED") updateListener?.onAppUpdateDownloaded() } INSTALLED -> { - Log.e("AppUpdateChecker", "installStateListener INSTALLED") + Log.e(TAG, "installStateListener INSTALLED") updateListener?.onAppUpdateInstalled() appUpdateManager.unregisterListener(this) // 'this' refers to the listener object } CANCELED -> { - Log.e("AppUpdateChecker", "installStateListener CANCELED") + Log.e(TAG, "installStateListener CANCELED") updateListener?.onAppUpdateCancelled() appUpdateManager.unregisterListener(this) } FAILED -> { - Log.e("AppUpdateChecker", "installStateListener FAILED") + Log.e(TAG, "installStateListener FAILED") updateListener?.onAppUpdateFailed() appUpdateManager.unregisterListener(this) } PENDING -> { - Log.e("AppUpdateChecker", "installStateListener PENDING") + Log.e(TAG, "installStateListener PENDING") updateListener?.onAppUpdatePending() } DOWNLOADING -> { - Log.e("AppUpdateChecker", "installStateListener DOWNLOADING") + Log.e(TAG, "installStateListener DOWNLOADING") } INSTALLING -> { - Log.e("AppUpdateChecker", "installStateListener INSTALLING") + Log.e(TAG, "installStateListener INSTALLING") } InstallStatus.UNKNOWN -> { - Log.e("AppUpdateChecker", "installStateListener UNKNOWN") + Log.e(TAG, "installStateListener UNKNOWN") } } } } private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + Log.e(TAG, "isImmediateUpdateInProgress(): entered, " + + "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + + "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) = ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + + "isImmediateUpdateNecessary = ${isImmediateUpdateNecessary()}") + val result = appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && isImmediateUpdateNecessary() + + Log.e(TAG, "isImmediateUpdateInProgress(): result = $result") + + return result + } private fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - return appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + Log.e(TAG, "isFlexibleUpdateInProgress(): entered, " + + "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + + "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) = ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + + "isImmediateUpdateNecessary = ${isImmediateUpdateNecessary()}") + val result = appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) && !isImmediateUpdateNecessary() + + Log.e(TAG, "isFlexibleUpdateInProgress(): result = $result") + + return result } private fun setLastUpdateRequestedTime() { + Log.e(TAG, "setLastUpdateRequestedTime(): entered") + val currentTime = currentTimeProvider.invoke() + Log.e(TAG, "setLastUpdateRequestedTime(): currentTime = $currentTime") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { - putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTimeProvider.invoke()) + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTime) apply() } } private fun resetLastUpdateRequestedTime() { + Log.e(TAG, "resetLastUpdateRequestedTime(): entered") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) @@ -221,12 +248,20 @@ class InAppUpdateManager( private fun getLastUpdateRequestedTime(): Long { val defaultValue = -1L - return applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, defaultValue) + + Log.e(TAG, "getLastUpdateRequestedTime(): result = $result") + + return result } - private fun shouldRequestFlexibleUpdate() = - currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS + private fun shouldRequestFlexibleUpdate(): Boolean { + Log.e(TAG, "shouldRequestFlexibleUpdate(): entered") + val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS + Log.e(TAG, "shouldRequestFlexibleUpdate(): result = $result") + return result + } /** @@ -270,12 +305,23 @@ class InAppUpdateManager( * * @return `true` if the current app version is lower than the last blocking app version, otherwise `false`. */ - private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() + private fun isImmediateUpdateNecessary(): Boolean { + Log.e(TAG, "isImmediateUpdateNecessary(): entered") + val currentVersion = getCurrentAppVersion() + val getLastBlockingAppVersion = getLastBlockingAppVersion() + Log.e(TAG, "isImmediateUpdateNecessary() called") + Log.e(TAG, "currentVersion = $currentVersion, lastBlockingVersion = $getLastBlockingAppVersion") + val result = getCurrentAppVersion() < getLastBlockingAppVersion() + Log.e(TAG, "isImmediateUpdateNecessary(): result = $result") + return result + } companion object { const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + private const val TAG = "AppUpdateChecker" + private const val PREF_NAME = "in_app_update_prefs" private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" private const val FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS: Long = 1000 * 60 * 60 * 24 * 5 // 5 days From 16f644d31a1eef3208434c07620ef6819a86c4a1 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 14 May 2024 14:55:48 +0300 Subject: [PATCH 35/81] Updates: InAppUpdateManager, reset saved values if new update is available --- .../android/inappupdate/InAppUpdateManager.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index 9d22da49ad16..2eb7920c9be9 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -71,6 +71,14 @@ class InAppUpdateManager( UPDATE_AVAILABLE -> { Log.e(TAG, "checkForAppUpdate(): update available") + + // reset saved values if new update is available + val updateVersion = getAvailableUpdateAppVersion(appUpdateInfo) + Log.e(TAG, "checkForAppUpdate(): updateVersion = $updateVersion") + if (updateVersion != getLastUpdateRequestedVersion()) { + resetLastUpdateRequestInfo() + } + if (isImmediateUpdateNecessary()) { Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == true") requestImmediateUpdate(appUpdateInfo, activity) @@ -118,7 +126,7 @@ class InAppUpdateManager( fun onUserAcceptedAppUpdate() { Log.e(TAG, "onUserAcceptedAppUpdate(): entered") - resetLastUpdateRequestedTime() + //resetLastUpdateRequestedTime() } private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { @@ -140,7 +148,7 @@ class InAppUpdateManager( val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE } else { - setLastUpdateRequestedTime() + saveLastUpdateRequestInfo(appUpdateInfo) APP_UPDATE_FLEXIBLE_REQUEST_CODE } try { @@ -226,26 +234,38 @@ class InAppUpdateManager( return result } - private fun setLastUpdateRequestedTime() { + private fun saveLastUpdateRequestInfo(appUpdateInfo: AppUpdateInfo) { Log.e(TAG, "setLastUpdateRequestedTime(): entered") val currentTime = currentTimeProvider.invoke() Log.e(TAG, "setLastUpdateRequestedTime(): currentTime = $currentTime") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { + putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, getAvailableUpdateAppVersion(appUpdateInfo)) putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTime) apply() } } - private fun resetLastUpdateRequestedTime() { + private fun resetLastUpdateRequestInfo() { Log.e(TAG, "resetLastUpdateRequestedTime(): entered") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { + putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) apply() } } + private fun getLastUpdateRequestedVersion(): Int { + val defaultValue = -1 + val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, defaultValue) + + Log.e(TAG, "getLastUpdateRequestedVersion(): result = $result") + + return result + } + private fun getLastUpdateRequestedTime(): Long { val defaultValue = -1L val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) @@ -323,6 +343,7 @@ class InAppUpdateManager( private const val TAG = "AppUpdateChecker" private const val PREF_NAME = "in_app_update_prefs" + private const val KEY_LAST_APP_UPDATE_CHECK_VERSION = "last_app_update_check_version" private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" private const val FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS: Long = 1000 * 60 * 60 * 24 * 5 // 5 days } From ce83a3bf52434dce1eaeb7a427aa0ad8da6e688a Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 14 May 2024 16:26:31 +0300 Subject: [PATCH 36/81] Adds: InAppUpdateBlockingVersionConfig for both jp and wp apps --- ...nAppUpdateBlockingVersionConfigConstants.kt | 5 +++++ .../android/inappupdate/InAppUpdateManager.kt | 8 ++------ .../config/InAppUpdateBlockingVersionConfig.kt | 17 +++++++++++++++++ .../JetpackInAppUpdateBlockingVersionConfig.kt | 18 ------------------ .../android/util/config/RemoteConfigWrapper.kt | 6 ++---- ...ordPressInAppUpdateBlockingVersionConfig.kt | 18 ------------------ ...nAppUpdateBlockingVersionConfigConstants.kt | 5 +++++ 7 files changed, 31 insertions(+), 46 deletions(-) create mode 100644 WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt create mode 100644 WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt diff --git a/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt b/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt new file mode 100644 index 000000000000..3d539360338c --- /dev/null +++ b/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt @@ -0,0 +1,5 @@ +package org.wordpress.android.util.config + +const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "jp_in_app_update_blocking_version_android" + + diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt index 2eb7920c9be9..48cb7476a330 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt @@ -301,12 +301,8 @@ class InAppUpdateManager( * * @return The version code of the last blocking app update. */ - private fun getLastBlockingAppVersion(): Int = - if (buildConfigWrapper.isJetpackApp) { - remoteConfigWrapper.getJetpackInAppUpdateBlockingVersion() - } else { - remoteConfigWrapper.getWordPressInAppUpdateBlockingVersion() - } + private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() + /** * Extracts the available version code for the app update from the given update information. diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt new file mode 100644 index 000000000000..c6df149a0f0a --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt @@ -0,0 +1,17 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" + +@RemoteFieldDefaultGenerater( + remoteField = IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, + defaultValue = IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT +) + +class InAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt deleted file mode 100644 index 2b05ad01b21d..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/JetpackInAppUpdateBlockingVersionConfig.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.annotation.RemoteFieldDefaultGenerater -import javax.inject.Inject - -const val JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "jp_in_app_update_blocking_version_android" -const val JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" - -@RemoteFieldDefaultGenerater( - remoteField = JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, - defaultValue = JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT -) - -class JetpackInAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : - RemoteConfigField( - appConfig, - JETPACK_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD - ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt index a45b2fe7d172..2334d6230a5f 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt @@ -8,12 +8,10 @@ class RemoteConfigWrapper @Inject constructor( private val openWebLinksWithJetpackFlowFrequencyConfig: OpenWebLinksWithJetpackFlowFrequencyConfig, private val codeableGetFreeEstimateUrlConfig: CodeableGetFreeEstimateUrlConfig, private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig, - private val wordPressInAppUpdateBlockingVersionConfig: WordPressInAppUpdateBlockingVersionConfig, - private val jetpackInAppUpdateBlockingVersionConfig: JetpackInAppUpdateBlockingVersionConfig, + private val inAppUpdateBlockingVersionConfig: InAppUpdateBlockingVersionConfig, ) { fun getOpenWebLinksWithJetpackFlowFrequency() = openWebLinksWithJetpackFlowFrequencyConfig.getValue() fun getPerformanceMonitoringSampleRate() = performanceMonitoringSampleRateConfig.getValue() fun getCodeableGetFreeEstimateUrl() = codeableGetFreeEstimateUrlConfig.getValue() - fun getWordPressInAppUpdateBlockingVersion() = wordPressInAppUpdateBlockingVersionConfig.getValue() - fun getJetpackInAppUpdateBlockingVersion() = jetpackInAppUpdateBlockingVersionConfig.getValue() + fun getInAppUpdateBlockingVersion() = inAppUpdateBlockingVersionConfig.getValue() } diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt deleted file mode 100644 index b5f1ad4efaf2..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/WordPressInAppUpdateBlockingVersionConfig.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.annotation.RemoteFieldDefaultGenerater -import javax.inject.Inject - -const val WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "wp_in_app_update_blocking_version_android" -const val WP_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" - -@RemoteFieldDefaultGenerater( - remoteField = WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, - defaultValue = WP_IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT -) - -class WordPressInAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : - RemoteConfigField( - appConfig, - WP_IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD - ) diff --git a/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt b/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt new file mode 100644 index 000000000000..168bda3e189a --- /dev/null +++ b/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt @@ -0,0 +1,5 @@ +package org.wordpress.android.util.config + +const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "wp_in_app_update_blocking_version_android" + + From b0d4ebcbbdb87136430c7000a9c2e661ec3350f8 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 14 May 2024 17:18:59 +0300 Subject: [PATCH 37/81] Modifies: in app update snackbar --- .../java/org/wordpress/android/ui/main/WPMainActivity.java | 5 ++--- WordPress/src/main/res/values/strings.xml | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index c9447342937f..c69cc94678da 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -1243,10 +1243,9 @@ private void checkForInAppUpdate() { private void popupSnackbarForCompleteUpdate() { Log.e("WPMainActivity", "showSnackBarForUpdate()"); - WPSnackbar.make(findViewById(R.id.coordinator), R.string.update_available, Snackbar.LENGTH_LONG) - .setAction(R.string.update_now, v -> { + WPSnackbar.make(findViewById(R.id.coordinator), R.string.in_app_update_available, Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.in_app_update_restart, v -> { mInAppUpdateManager.completeAppUpdate(); - // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); }) .show(); diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 26a49f5a74ee..c573d11a65a2 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4819,7 +4819,8 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> There was some trouble with the Security key login Please provide your security key to continue. - "Update is available for the app " + Update downloaded. Restart to apply. + Restart Alternatively, you can flatten the content by ungrouping the block. For this reason, we recommend editing the block using the web editor. From 40951e2157bc0a2258a28a53cd63e1f373353c77 Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Tue, 14 May 2024 11:16:44 -0400 Subject: [PATCH 38/81] Fixed failing test and finished class doc. --- .../wordpress/android/ui/debug/preferences/DebugPrefs.kt | 2 +- .../preferences/DebugSharedPreferenceFlagsViewModelTest.kt | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt index 0ed4a2801880..7dda8cc7bf70 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt @@ -3,7 +3,7 @@ package org.wordpress.android.ui.debug.preferences import kotlin.reflect.KClass /** - * Class used to track + * Class used to track debuggable shared preferences and will show up in [DebugSharedPreferenceFlagsActivity]. */ enum class DebugPrefs(val key: String, val type: KClass<*>) { ALWAYS_SHOW_ANNOUNCEMENT("prefs_always_show_announcement", Boolean::class) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt index 7e0d136c874b..1b69bc81d9b8 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt @@ -1,6 +1,7 @@ package org.wordpress.android.ui.debug.preferences import junit.framework.TestCase.assertTrue +import junit.framework.TestCase.assertFalse import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq @@ -19,11 +20,13 @@ class DebugSharedPreferenceFlagsViewModelTest { @Test fun `WHEN init THEN should load the flags from the prefs`() { - whenever(prefsWrapper.getAllPrefs()).thenReturn(mapOf("key" to true)) + DebugPrefs.entries.forEach { + whenever(prefsWrapper.getDebugBooleanPref(it.key, false)).thenReturn(false) + } initViewModel() - assertTrue(viewModel.uiStateFlow.value["key"]!!) + assertFalse(viewModel.uiStateFlow.value.isEmpty()) } @Test From ca598a965d8149f92ecea9240bab8e18dda91682 Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Tue, 14 May 2024 13:55:43 -0400 Subject: [PATCH 39/81] [Hack] Concat current method with debug prefs. --- .../preferences/DebugSharedPreferenceFlagsViewModel.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt index 00d6050a3ed3..a88c1cb1ed4f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt @@ -15,11 +15,16 @@ class DebugSharedPreferenceFlagsViewModel @Inject constructor( val uiStateFlow = _uiStateFlow.asStateFlow() init { - val flags = DebugPrefs.entries.mapNotNull { + val flags = prefsWrapper.getAllPrefs().mapNotNull { (key, value) -> + if (value is Boolean) key to value else null + }.toMap() + + val explicitFlags = DebugPrefs.entries.mapNotNull { // Only supporting boolean for now. if (it.type == Boolean::class) it else null }.associate { it.key to prefsWrapper.getDebugBooleanPref(it.key, false) } - _uiStateFlow.value = flags + + _uiStateFlow.value = flags + explicitFlags } fun setFlag(key: String, value: Boolean) { From 5518e1fd9fbae7fabd6a94a55a6dd610d1301189 Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Tue, 14 May 2024 14:03:56 -0400 Subject: [PATCH 40/81] [Hack] Updated test --- .../preferences/DebugSharedPreferenceFlagsViewModelTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt index 1b69bc81d9b8..15e7168d5e42 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.debug.preferences import junit.framework.TestCase.assertTrue -import junit.framework.TestCase.assertFalse import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq @@ -20,13 +19,15 @@ class DebugSharedPreferenceFlagsViewModelTest { @Test fun `WHEN init THEN should load the flags from the prefs`() { + whenever(prefsWrapper.getAllPrefs()).thenReturn(mapOf("key" to true)) DebugPrefs.entries.forEach { whenever(prefsWrapper.getDebugBooleanPref(it.key, false)).thenReturn(false) } initViewModel() - assertFalse(viewModel.uiStateFlow.value.isEmpty()) + assertTrue(viewModel.uiStateFlow.value["key"]!!) + assertTrue(viewModel.uiStateFlow.value.size >= 2) } @Test From 6ba025e1d89b0e852ce887d14f7467b3c7e58a40 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Tue, 14 May 2024 22:40:24 +0300 Subject: [PATCH 41/81] Change the order of `initSearchFragment` to resolve the crash --- .../java/org/wordpress/android/ui/posts/PostsListActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt index 4da1601f9492..c89268b5853a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt @@ -209,6 +209,7 @@ class PostsListActivity : LocaleAwareActivity(), setupActionBar() setupContent() initViewModel(initPreviewState, currentBottomSheetPostId) + initSearchFragment() initBloggingReminders() initInAppReviews() initTabLayout(tabIndex) @@ -492,7 +493,6 @@ class PostsListActivity : LocaleAwareActivity(), authorFilterMenuItem = menu.findItem(R.id.author_filter_menu_item) searchActionButton = menu.findItem(R.id.toggle_search) - initSearchFragment() binding.initSearchView() initAuthorFilter(authorFilterMenuItem) return true From 2500a0020e1fdc48b0c58bc778f3ec3c06b174c1 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Tue, 14 May 2024 22:55:52 +0300 Subject: [PATCH 42/81] Update RELEASE-NOTES --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 2f150f031f95..cb54d0376091 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -2,6 +2,7 @@ 25.0 ----- +* [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] 24.9 From 31615f53b4368d69ef331e561d82233d3edbd136 Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Tue, 14 May 2024 17:48:39 -0700 Subject: [PATCH 43/81] Add another null check --- .../android/ui/history/HistoryDetailContainerFragment.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java index cd848c1a34a6..61d2d2a59700 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java @@ -172,6 +172,9 @@ private ArrayList mapRevisions() { mRevision = getArguments().getParcelable(EXTRA_CURRENT_REVISION); final long[] previousRevisionsIds = getArguments().getLongArray(EXTRA_PREVIOUS_REVISIONS_IDS); + if (previousRevisionsIds == null) { + return null; + } final List revisionModels = new ArrayList<>(); final long postId = getArguments().getLong(EXTRA_POST_ID); final long siteId = getArguments().getLong(EXTRA_SITE_ID); From 1995dd3a687fc7947a55a42e038c82d7ec6c6715 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 11:34:18 +0300 Subject: [PATCH 44/81] Adds: InAppUpdatesFeatureConfig --- .../android/inappupdate/IInAppUpdateManager.kt | 14 ++++++++++++++ ...dateManager.kt => InAppUpdateManagerImpl.kt} | 17 +++++++++-------- .../inappupdate/InAppUpdateManagerNoop.kt | 17 +++++++++++++++++ .../android/modules/ApplicationModule.java | 17 ++++++++++++----- .../android/ui/main/WPMainActivity.java | 10 +++++----- .../util/config/InAppUpdatesFeatureConfig.kt | 16 ++++++++++++++++ 6 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt rename WordPress/src/main/java/org/wordpress/android/inappupdate/{InAppUpdateManager.kt => InAppUpdateManagerImpl.kt} (96%) create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt new file mode 100644 index 000000000000..47b328b49a55 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt @@ -0,0 +1,14 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity + +interface IInAppUpdateManager { + fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) + fun completeAppUpdate() + fun cancelAppUpdate() + + companion object { + const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt similarity index 96% rename from WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt rename to WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 48cb7476a330..2dc1f9d6c41c 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -23,6 +23,8 @@ import com.google.android.play.core.install.model.UpdateAvailability.UNKNOWN import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_AVAILABLE import dagger.hilt.android.qualifiers.ApplicationContext +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_FLEXIBLE_REQUEST_CODE +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_IMMEDIATE_REQUEST_CODE import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.config.RemoteConfigWrapper @@ -31,17 +33,17 @@ import javax.inject.Singleton @Singleton @Suppress("TooManyFunctions") -class InAppUpdateManager( +class InAppUpdateManagerImpl( @ApplicationContext private val applicationContext: Context, private val appUpdateManager: AppUpdateManager, private val remoteConfigWrapper: RemoteConfigWrapper, private val buildConfigWrapper: BuildConfigWrapper, private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} -) { +): IInAppUpdateManager { private var updateListener: IInAppUpdateListener? = null - fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { + override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { Log.e(TAG, "checkForAppUpdate() entered") updateListener = listener @@ -114,19 +116,18 @@ class InAppUpdateManager( } } - fun completeAppUpdate() { + override fun completeAppUpdate() { Log.e(TAG, "completeAppUpdate(): entered") appUpdateManager.completeUpdate() } - fun cancelAppUpdate() { + override fun cancelAppUpdate() { Log.e(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) } fun onUserAcceptedAppUpdate() { Log.e(TAG, "onUserAcceptedAppUpdate(): entered") - //resetLastUpdateRequestedTime() } private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { @@ -333,8 +334,8 @@ class InAppUpdateManager( } companion object { - const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 - const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + //const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + //const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 private const val TAG = "AppUpdateChecker" diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt new file mode 100644 index 000000000000..0fafc54bc7c6 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt @@ -0,0 +1,17 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity + +class InAppUpdateManagerNoop: IInAppUpdateManager { + override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { + /* Empty implementation */ + } + + override fun completeAppUpdate() { + /* Empty implementation */ + } + + override fun cancelAppUpdate() { + /* Empty implementation */ + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index d26b58208fc1..488ff3b16b70 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -15,7 +15,9 @@ import com.tenor.android.core.network.IApiClient; import org.wordpress.android.BuildConfig; -import org.wordpress.android.inappupdate.InAppUpdateManager; +import org.wordpress.android.inappupdate.IInAppUpdateManager; +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl; +import org.wordpress.android.inappupdate.InAppUpdateManagerNoop; import org.wordpress.android.ui.ActivityNavigator; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStep; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStepsProvider; @@ -25,6 +27,7 @@ import org.wordpress.android.ui.sitecreation.SiteCreationStep; import org.wordpress.android.ui.sitecreation.SiteCreationStepsProvider; import org.wordpress.android.util.BuildConfigWrapper; +import org.wordpress.android.util.config.InAppUpdatesFeatureConfig; import org.wordpress.android.util.config.RemoteConfigWrapper; import org.wordpress.android.util.wizard.WizardManager; import org.wordpress.android.viewmodel.helpers.ConnectionStatus; @@ -87,19 +90,23 @@ public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Conte } @Provides - public static InAppUpdateManager provideInAppUpdateManager( + public static IInAppUpdateManager provideInAppUpdateManager( @ApplicationContext Context context, AppUpdateManager appUpdateManager, RemoteConfigWrapper remoteConfigWrapper, - BuildConfigWrapper buildConfigWrapper + BuildConfigWrapper buildConfigWrapper, + InAppUpdatesFeatureConfig inAppUpdatesFeatureConfig ) { - return new InAppUpdateManager( + // Check if in-app updates feature is enabled + return inAppUpdatesFeatureConfig.isEnabled() + ? new InAppUpdateManagerImpl( context, appUpdateManager, remoteConfigWrapper, buildConfigWrapper, System::currentTimeMillis - ); + ) + : new InAppUpdateManagerNoop(); } @Provides diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index c69cc94678da..b8ef398c0920 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -40,7 +40,7 @@ import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.BuildConfig; import org.wordpress.android.inappupdate.IInAppUpdateListener; -import org.wordpress.android.inappupdate.InAppUpdateManager; +import org.wordpress.android.inappupdate.IInAppUpdateManager; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; @@ -301,7 +301,7 @@ public class WPMainActivity extends LocaleAwareActivity implements @Inject BuildConfigWrapper mBuildConfigWrapper; - @Inject InAppUpdateManager mInAppUpdateManager; + @Inject IInAppUpdateManager mInAppUpdateManager; @Inject GCMRegistrationScheduler mGCMRegistrationScheduler; @@ -1516,11 +1516,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { case RequestCodes.DOMAIN_REGISTRATION: passOnActivityResultToMySiteFragment(requestCode, resultCode, data); break; - case InAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: - case InAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: + case IInAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: + case IInAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: if (resultCode == RESULT_OK) { // The user has accepted the update - mInAppUpdateManager.onUserAcceptedAppUpdate(); + //mInAppUpdateManager.onUserAcceptedAppUpdate(); } else if (resultCode == RESULT_CANCELED || resultCode == RESULT_IN_APP_UPDATE_FAILED) { // The user denied the update or an error occurred mInAppUpdateManager.cancelAppUpdate(); diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt new file mode 100644 index 000000000000..3836cb91ff64 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val IN_APP_UPDATES_FEATURE_REMOTE_FIELD = "in_app_updates" + +@Feature(IN_APP_UPDATES_FEATURE_REMOTE_FIELD, false) +class InAppUpdatesFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.ENABLE_IN_APP_UPDATES, + IN_APP_UPDATES_FEATURE_REMOTE_FIELD +) From 223dadf04bbc5e515cdbee6d790dae8b6a708e5f Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 13:41:15 +0300 Subject: [PATCH 45/81] Adds: analytics for in-app updates --- .../inappupdate/IInAppUpdateManager.kt | 1 + .../InAppUpdateAnalyticsTracker.kt | 36 +++++++++++++++++++ .../inappupdate/InAppUpdateManagerImpl.kt | 6 +++- .../inappupdate/InAppUpdateManagerNoop.kt | 4 +++ .../android/modules/ApplicationModule.java | 5 ++- .../android/ui/main/WPMainActivity.java | 22 ++++++++---- .../android/analytics/AnalyticsTracker.java | 6 +++- 7 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt index 47b328b49a55..b0f6e2f00f51 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt @@ -6,6 +6,7 @@ interface IInAppUpdateManager { fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) fun completeAppUpdate() fun cancelAppUpdate() + fun onUserAcceptedAppUpdate(updateType: Int) companion object { const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt new file mode 100644 index 000000000000..5b98fc09d46d --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt @@ -0,0 +1,36 @@ +package org.wordpress.android.inappupdate + +import com.google.android.play.core.install.model.AppUpdateType +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper +import javax.inject.Inject + +class InAppUpdateAnalyticsTracker @Inject constructor( + private val tracker: AnalyticsTrackerWrapper +) { + fun trackUpdateShown(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, createPropertyMap(updateType)) + } + + fun trackUpdateAccepted(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, createPropertyMap(updateType)) + } + + fun trackUpdateDismissed() { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED) + } + + private fun createPropertyMap(updateType: Int): Map { + return when (updateType) { + AppUpdateType.FLEXIBLE -> mapOf(PROPERTY_UPDATE_TYPE to UPDATE_TYPE_FLEXIBLE) + AppUpdateType.IMMEDIATE -> mapOf(PROPERTY_UPDATE_TYPE to UPDATE_TYPE_BLOCKING) + else -> emptyMap() + } + } + + companion object { + private const val PROPERTY_UPDATE_TYPE = "type" + private const val UPDATE_TYPE_FLEXIBLE = "flexible" + private const val UPDATE_TYPE_BLOCKING = "blocking" + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 2dc1f9d6c41c..d36497e926ce 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -38,6 +38,7 @@ class InAppUpdateManagerImpl( private val appUpdateManager: AppUpdateManager, private val remoteConfigWrapper: RemoteConfigWrapper, private val buildConfigWrapper: BuildConfigWrapper, + private val inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker, private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} ): IInAppUpdateManager { @@ -124,10 +125,12 @@ class InAppUpdateManagerImpl( override fun cancelAppUpdate() { Log.e(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) + inAppUpdateAnalyticsTracker.trackUpdateDismissed() } - fun onUserAcceptedAppUpdate() { + override fun onUserAcceptedAppUpdate(updateType: Int) { Log.e(TAG, "onUserAcceptedAppUpdate(): entered") + inAppUpdateAnalyticsTracker.trackUpdateAccepted(updateType) } private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { @@ -159,6 +162,7 @@ class InAppUpdateManagerImpl( AppUpdateOptions.newBuilder(updateType).build(), requestCode ) + inAppUpdateAnalyticsTracker.trackUpdateShown(updateType) } catch (e: Exception) { Log.e(TAG, "requestUpdate for type: $updateType, exception occurred") Log.e(TAG, e.message.toString()) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt index 0fafc54bc7c6..cc944057c75f 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt @@ -14,4 +14,8 @@ class InAppUpdateManagerNoop: IInAppUpdateManager { override fun cancelAppUpdate() { /* Empty implementation */ } + + override fun onUserAcceptedAppUpdate(updateType: Int) { + /* Empty implementation */ + } } diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 488ff3b16b70..ed712b9d32e3 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -16,6 +16,7 @@ import org.wordpress.android.BuildConfig; import org.wordpress.android.inappupdate.IInAppUpdateManager; +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker; import org.wordpress.android.inappupdate.InAppUpdateManagerImpl; import org.wordpress.android.inappupdate.InAppUpdateManagerNoop; import org.wordpress.android.ui.ActivityNavigator; @@ -95,7 +96,8 @@ public static IInAppUpdateManager provideInAppUpdateManager( AppUpdateManager appUpdateManager, RemoteConfigWrapper remoteConfigWrapper, BuildConfigWrapper buildConfigWrapper, - InAppUpdatesFeatureConfig inAppUpdatesFeatureConfig + InAppUpdatesFeatureConfig inAppUpdatesFeatureConfig, + InAppUpdateAnalyticsTracker inAppUpdateAnalyticsTracker ) { // Check if in-app updates feature is enabled return inAppUpdatesFeatureConfig.isEnabled() @@ -104,6 +106,7 @@ public static IInAppUpdateManager provideInAppUpdateManager( appUpdateManager, remoteConfigWrapper, buildConfigWrapper, + inAppUpdateAnalyticsTracker, System::currentTimeMillis ) : new InAppUpdateManagerNoop(); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index b8ef398c0920..786d0d9574fc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -31,6 +31,7 @@ import com.google.android.gms.tasks.Task; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.google.android.play.core.install.model.AppUpdateType; import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; @@ -1517,18 +1518,25 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { passOnActivityResultToMySiteFragment(requestCode, resultCode, data); break; case IInAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: + handleUpdateResult(resultCode, AppUpdateType.FLEXIBLE); + break; case IInAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: - if (resultCode == RESULT_OK) { - // The user has accepted the update - //mInAppUpdateManager.onUserAcceptedAppUpdate(); - } else if (resultCode == RESULT_CANCELED || resultCode == RESULT_IN_APP_UPDATE_FAILED) { - // The user denied the update or an error occurred - mInAppUpdateManager.cancelAppUpdate(); - } + handleUpdateResult(resultCode, AppUpdateType.IMMEDIATE); break; } } + // Handles the result of the app update request + private void handleUpdateResult(int resultCode, int updateType) { + if (resultCode == RESULT_OK) { + // The user accepted the update + mInAppUpdateManager.onUserAcceptedAppUpdate(updateType); + } else if (resultCode == RESULT_CANCELED) { + // The user denied the update + mInAppUpdateManager.cancelAppUpdate(); + } + } + private void appLanguageChanged() { // Recreate this activity (much like a configuration change) // We need to post this call to UI thread, since it's called from onActivityResult and the call interferes with diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java index 1cd7bc461d0c..af3ad9ca14ce 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java @@ -1122,7 +1122,11 @@ public enum Stat { RESOLVE_AUTOSAVE_CONFLICT_CONFIRM_TAPPED, RESOLVE_AUTOSAVE_CONFLICT_CANCEL_TAPPED, RESOLVE_AUTOSAVE_CONFLICT_CLOSE_TAPPED, - RESOLVE_AUTOSAVE_CONFLICT_DISMISSED; + RESOLVE_AUTOSAVE_CONFLICT_DISMISSED, + IN_APP_UPDATE_SHOWN, + IN_APP_UPDATE_DISMISSED, + IN_APP_UPDATE_ACCEPTED; + /* * Please set the event name in the enum only if the new Stat's name in lower case does not match it. * In that case you also need to add the event in the `AnalyticsTrackerNosaraTest.specialNames` map. From 49b4229e9ae001a80d05dc5a33f855fabe8d33f2 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 14:17:41 +0300 Subject: [PATCH 46/81] Fixes: checkstyle and detekt issues --- .../inappupdate/InAppUpdateManagerImpl.kt | 23 ++----------------- .../android/ui/main/WPMainActivity.java | 4 +--- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index d36497e926ce..e10112494459 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -30,7 +30,6 @@ import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.config.RemoteConfigWrapper import javax.inject.Singleton - @Singleton @Suppress("TooManyFunctions") class InAppUpdateManagerImpl( @@ -41,7 +40,6 @@ class InAppUpdateManagerImpl( private val inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker, private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} ): IInAppUpdateManager { - private var updateListener: IInAppUpdateListener? = null override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { @@ -213,7 +211,8 @@ class InAppUpdateManagerImpl( private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { Log.e(TAG, "isImmediateUpdateInProgress(): entered, " + "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + - "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) = ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + + "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) " + + "= ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + "isImmediateUpdateNecessary = ${isImmediateUpdateNecessary()}") val result = appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) @@ -221,21 +220,6 @@ class InAppUpdateManagerImpl( Log.e(TAG, "isImmediateUpdateInProgress(): result = $result") - return result - - } - - private fun isFlexibleUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - Log.e(TAG, "isFlexibleUpdateInProgress(): entered, " + - "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + - "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) = ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + - "isImmediateUpdateNecessary = ${isImmediateUpdateNecessary()}") - val result = appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) - && !isImmediateUpdateNecessary() - - Log.e(TAG, "isFlexibleUpdateInProgress(): result = $result") - return result } @@ -338,9 +322,6 @@ class InAppUpdateManagerImpl( } companion object { - //const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 - //const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 - private const val TAG = "AppUpdateChecker" private const val PREF_NAME = "in_app_update_prefs" diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 786d0d9574fc..755ea3116f17 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -190,7 +190,6 @@ import javax.inject.Inject; import static androidx.lifecycle.Lifecycle.State.STARTED; -import static com.google.android.play.core.install.model.ActivityResult.RESULT_IN_APP_UPDATE_FAILED; import static org.wordpress.android.WordPress.SITE; import static org.wordpress.android.fluxc.store.SiteStore.CompleteQuickStartVariant.NEXT_STEPS; import static org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource.EMAIL; @@ -1216,9 +1215,8 @@ private void checkForInAppUpdate() { } @NonNull final IInAppUpdateListener mInAppUpdateListener = new IInAppUpdateListener() { - @Override public void onAppUpdatePending() { - + /* do nothing */ } @Override public void onAppUpdateDownloaded() { From e21f6d078ada83280c9bd45ede4ea790f4ae40e0 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 14:40:52 +0300 Subject: [PATCH 47/81] Refactors: InAppUpdateMaangerImpl --- .../inappupdate/InAppUpdateManagerImpl.kt | 207 +++++++----------- 1 file changed, 76 insertions(+), 131 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index e10112494459..6f37a2d94375 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -19,7 +19,6 @@ import com.google.android.play.core.install.model.InstallStatus.INSTALLED import com.google.android.play.core.install.model.InstallStatus.INSTALLING import com.google.android.play.core.install.model.InstallStatus.PENDING import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -import com.google.android.play.core.install.model.UpdateAvailability.UNKNOWN import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_AVAILABLE import dagger.hilt.android.qualifiers.ApplicationContext @@ -43,102 +42,84 @@ class InAppUpdateManagerImpl( private var updateListener: IInAppUpdateListener? = null override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { - Log.e(TAG, "checkForAppUpdate() entered") - + Log.d(TAG, "Checking for app update") updateListener = listener - val appUpdateInfoTask = appUpdateManager.appUpdateInfo - - appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> - - Log.e(TAG, "checkForAppUpdate(): success") - - val updateAvailability = appUpdateInfo.updateAvailability() - - // If the update is downloaded but not installed, - // notify the user to complete the update. - if (appUpdateInfo.installStatus() == DOWNLOADED) { - Log.e(TAG, "checkForAppUpdate(): appUpdateInfo.installStatus() == DOWNLOADED") - listener.onAppUpdateDownloaded() - Log.e(TAG, "checkForAppUpdate(): listener.onAppUpdateDownloaded() called") - return@addOnSuccessListener - } - - when (updateAvailability) { - UPDATE_NOT_AVAILABLE -> { - Log.e(TAG, "checkForAppUpdate(): no update available") - return@addOnSuccessListener - } - - UPDATE_AVAILABLE -> { - Log.e(TAG, "checkForAppUpdate(): update available") - - // reset saved values if new update is available - val updateVersion = getAvailableUpdateAppVersion(appUpdateInfo) - Log.e(TAG, "checkForAppUpdate(): updateVersion = $updateVersion") - if (updateVersion != getLastUpdateRequestedVersion()) { - resetLastUpdateRequestInfo() - } - - if (isImmediateUpdateNecessary()) { - Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == true") - requestImmediateUpdate(appUpdateInfo, activity) - } else { - Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == false") - if (shouldRequestFlexibleUpdate()) { - requestFlexibleUpdate(appUpdateInfo, activity) - } - } - } - - DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { - Log.e(TAG, "checkForAppUpdate(): UPDATE_IN_PROGRESS") - if (isImmediateUpdateInProgress(appUpdateInfo)) { - Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == true") - requestImmediateUpdate(appUpdateInfo, activity) - } else { - Log.e(TAG, "checkForAppUpdate(): isImmediateUpdateNecessary == false") - requestFlexibleUpdate(appUpdateInfo, activity) - } - } - - UNKNOWN -> { - Log.e(TAG, "checkForAppUpdate(): UNKNOWN") - return@addOnSuccessListener - } - } - } - - appUpdateInfoTask.addOnFailureListener { exception -> - Log.e(TAG, "checkForAppUpdate():, checking update, failure") - Log.e(TAG, exception.message.toString()) + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + handleUpdateInfoSuccess(appUpdateInfo, activity) + }.addOnFailureListener { exception -> + Log.e(TAG, "Failed to check for update: ${exception.message}") } } override fun completeAppUpdate() { - Log.e(TAG, "completeAppUpdate(): entered") + Log.d(TAG, "completeAppUpdate(): entered") appUpdateManager.completeUpdate() } override fun cancelAppUpdate() { - Log.e(TAG, "cancelAppUpdate(): entered") + Log.d(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) inAppUpdateAnalyticsTracker.trackUpdateDismissed() } override fun onUserAcceptedAppUpdate(updateType: Int) { - Log.e(TAG, "onUserAcceptedAppUpdate(): entered") + Log.d(TAG, "onUserAcceptedAppUpdate(): entered") inAppUpdateAnalyticsTracker.trackUpdateAccepted(updateType) } + private fun handleUpdateInfoSuccess(appUpdateInfo: AppUpdateInfo, activity: Activity) { + when (appUpdateInfo.updateAvailability()) { + UPDATE_NOT_AVAILABLE -> { + Log.d(TAG, "No update available") + } + UPDATE_AVAILABLE -> { + handleUpdateAvailable(appUpdateInfo, activity) + } + DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { + handleUpdateInProgress(appUpdateInfo, activity) + } + else -> { + Log.w(TAG, "Unknown update availability") + } + } + } + + private fun handleUpdateAvailable(appUpdateInfo: AppUpdateInfo, activity: Activity) { + if (appUpdateInfo.installStatus() == DOWNLOADED) { + Log.d(TAG, "Update downloaded, notifying listener") + updateListener?.onAppUpdateDownloaded() + return + } + + val updateVersion = getAvailableUpdateAppVersion(appUpdateInfo) + if (updateVersion != getLastUpdateRequestedVersion()) { + resetLastUpdateRequestInfo() + } + + if (isImmediateUpdateNecessary()) { + requestImmediateUpdate(appUpdateInfo, activity) + } else if (shouldRequestFlexibleUpdate()) { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + + private fun handleUpdateInProgress(appUpdateInfo: AppUpdateInfo, activity: Activity) { + if (isImmediateUpdateInProgress(appUpdateInfo)) { + requestImmediateUpdate(appUpdateInfo, activity) + } else { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e(TAG, "requestImmediateUpdate(): entered") + Log.d(TAG, "requestImmediateUpdate(): entered") updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) } private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e(TAG, "requestFlexibleUpdate(): entered") + Log.d(TAG, "requestFlexibleUpdate(): entered") appUpdateManager.registerListener(installStateListener) updateListener?.onAppUpdateStarted(AppUpdateType.FLEXIBLE) requestUpdate(AppUpdateType.FLEXIBLE, appUpdateInfo, activity) @@ -146,7 +127,7 @@ class InAppUpdateManagerImpl( @Suppress("TooGenericExceptionCaught") private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.e(TAG, "requestUpdate(): entered with updateType = $updateType") + Log.d(TAG, "requestUpdate(): entered with updateType = $updateType") val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE } else { @@ -173,43 +154,43 @@ class InAppUpdateManagerImpl( override fun onStateUpdate(state: InstallState) { when (state.installStatus()) { DOWNLOADED -> { - Log.e(TAG, "installStateListener DOWNLOADED") + Log.d(TAG, "installStateListener DOWNLOADED") updateListener?.onAppUpdateDownloaded() } INSTALLED -> { - Log.e(TAG, "installStateListener INSTALLED") + Log.d(TAG, "installStateListener INSTALLED") updateListener?.onAppUpdateInstalled() appUpdateManager.unregisterListener(this) // 'this' refers to the listener object } CANCELED -> { - Log.e(TAG, "installStateListener CANCELED") + Log.d(TAG, "installStateListener CANCELED") updateListener?.onAppUpdateCancelled() appUpdateManager.unregisterListener(this) } FAILED -> { - Log.e(TAG, "installStateListener FAILED") + Log.d(TAG, "installStateListener FAILED") updateListener?.onAppUpdateFailed() appUpdateManager.unregisterListener(this) } PENDING -> { - Log.e(TAG, "installStateListener PENDING") + Log.d(TAG, "installStateListener PENDING") updateListener?.onAppUpdatePending() } DOWNLOADING -> { - Log.e(TAG, "installStateListener DOWNLOADING") + Log.d(TAG, "installStateListener DOWNLOADING") } INSTALLING -> { - Log.e(TAG, "installStateListener INSTALLING") + Log.d(TAG, "installStateListener INSTALLING") } InstallStatus.UNKNOWN -> { - Log.e(TAG, "installStateListener UNKNOWN") + Log.d(TAG, "installStateListener UNKNOWN") } } } } private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - Log.e(TAG, "isImmediateUpdateInProgress(): entered, " + + Log.d(TAG, "isImmediateUpdateInProgress(): entered, " + "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) " + "= ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + @@ -218,15 +199,15 @@ class InAppUpdateManagerImpl( && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && isImmediateUpdateNecessary() - Log.e(TAG, "isImmediateUpdateInProgress(): result = $result") + Log.d(TAG, "isImmediateUpdateInProgress(): result = $result") return result } private fun saveLastUpdateRequestInfo(appUpdateInfo: AppUpdateInfo) { - Log.e(TAG, "setLastUpdateRequestedTime(): entered") + Log.d(TAG, "setLastUpdateRequestedTime(): entered") val currentTime = currentTimeProvider.invoke() - Log.e(TAG, "setLastUpdateRequestedTime(): currentTime = $currentTime") + Log.d(TAG, "setLastUpdateRequestedTime(): currentTime = $currentTime") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, getAvailableUpdateAppVersion(appUpdateInfo)) @@ -236,7 +217,7 @@ class InAppUpdateManagerImpl( } private fun resetLastUpdateRequestInfo() { - Log.e(TAG, "resetLastUpdateRequestedTime(): entered") + Log.d(TAG, "resetLastUpdateRequestedTime(): entered") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) @@ -249,9 +230,7 @@ class InAppUpdateManagerImpl( val defaultValue = -1 val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .getInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, defaultValue) - - Log.e(TAG, "getLastUpdateRequestedVersion(): result = $result") - + Log.d(TAG, "getLastUpdateRequestedVersion(): result = $result") return result } @@ -259,65 +238,31 @@ class InAppUpdateManagerImpl( val defaultValue = -1L val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, defaultValue) - - Log.e(TAG, "getLastUpdateRequestedTime(): result = $result") - + Log.d(TAG, "getLastUpdateRequestedTime(): result = $result") return result } private fun shouldRequestFlexibleUpdate(): Boolean { - Log.e(TAG, "shouldRequestFlexibleUpdate(): entered") + Log.d(TAG, "shouldRequestFlexibleUpdate(): entered") val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS - Log.e(TAG, "shouldRequestFlexibleUpdate(): result = $result") + Log.d(TAG, "shouldRequestFlexibleUpdate(): result = $result") return result } - - /** - * Retrieves the current version code of the application. - * - * This version code is obtained from the application's build configuration. - * - * @return The current application version code. - */ private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() - /** - * Retrieves the version code of the last known update that requires blocking. - * - * This value is sourced from a remote configuration that specifies the - * version of the application that requires immediate blocking updates. - * - * @return The version code of the last blocking app update. - */ private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() - - /** - * Extracts the available version code for the app update from the given update information. - * - * The available version code indicates the most recent update version that's available - * and ready to be installed on the user's device. - * - * @param appUpdateInfo The update information object that contains version details. - * @return The available version code for the app update. - */ private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() - /** - * Checks if an immediate app update is required based on the current app version - * and the last known blocking version. - * - * @return `true` if the current app version is lower than the last blocking app version, otherwise `false`. - */ private fun isImmediateUpdateNecessary(): Boolean { - Log.e(TAG, "isImmediateUpdateNecessary(): entered") + Log.d(TAG, "isImmediateUpdateNecessary(): entered") val currentVersion = getCurrentAppVersion() val getLastBlockingAppVersion = getLastBlockingAppVersion() - Log.e(TAG, "isImmediateUpdateNecessary() called") - Log.e(TAG, "currentVersion = $currentVersion, lastBlockingVersion = $getLastBlockingAppVersion") + Log.d(TAG, "isImmediateUpdateNecessary() called") + Log.d(TAG, "currentVersion = $currentVersion, lastBlockingVersion = $getLastBlockingAppVersion") val result = getCurrentAppVersion() < getLastBlockingAppVersion() - Log.e(TAG, "isImmediateUpdateNecessary(): result = $result") + Log.d(TAG, "isImmediateUpdateNecessary(): result = $result") return result } From 36e756961a6ece02beb9f6fc6ed03235ee808b48 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 16:21:36 +0300 Subject: [PATCH 48/81] Adds: remote config for flexible updates interval --- .../inappupdate/InAppUpdateManagerImpl.kt | 10 ++++++++-- .../InAppUpdateFlexibleIntervalConfig.kt | 18 ++++++++++++++++++ .../android/util/config/RemoteConfigWrapper.kt | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 6f37a2d94375..27dccefa346c 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -244,11 +244,18 @@ class InAppUpdateManagerImpl( private fun shouldRequestFlexibleUpdate(): Boolean { Log.d(TAG, "shouldRequestFlexibleUpdate(): entered") - val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS + val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= getFlexibleUpdateIntervalInMillis() Log.d(TAG, "shouldRequestFlexibleUpdate(): result = $result") return result } + private fun getFlexibleUpdateIntervalInMillis(): Long { + Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): entered") + val result = 1000 * 60 * 60 * 24 * remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays() + Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): result = $result") + return result.toLong() + } + private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() @@ -272,6 +279,5 @@ class InAppUpdateManagerImpl( private const val PREF_NAME = "in_app_update_prefs" private const val KEY_LAST_APP_UPDATE_CHECK_VERSION = "last_app_update_check_version" private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" - private const val FLEXIBLE_UPDATES_INTERVAL_IN_MILLIS: Long = 1000 * 60 * 60 * 24 * 5 // 5 days } } diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt new file mode 100644 index 000000000000..17541cc9dfea --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD = "in_app_update_flexible_interval_in_days_android" +const val IN_APP_UPDATE_FLEXIBLE_INTERVAL_DEFAULT = "5" + +@RemoteFieldDefaultGenerater( + remoteField = IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD, + defaultValue = IN_APP_UPDATE_FLEXIBLE_INTERVAL_DEFAULT +) + +class InAppUpdateFlexibleIntervalConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt index 2334d6230a5f..eae8223994d6 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt @@ -9,9 +9,11 @@ class RemoteConfigWrapper @Inject constructor( private val codeableGetFreeEstimateUrlConfig: CodeableGetFreeEstimateUrlConfig, private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig, private val inAppUpdateBlockingVersionConfig: InAppUpdateBlockingVersionConfig, + private val inAppUpdateFlexibleIntervalConfig: InAppUpdateFlexibleIntervalConfig, ) { fun getOpenWebLinksWithJetpackFlowFrequency() = openWebLinksWithJetpackFlowFrequencyConfig.getValue() fun getPerformanceMonitoringSampleRate() = performanceMonitoringSampleRateConfig.getValue() fun getCodeableGetFreeEstimateUrl() = codeableGetFreeEstimateUrlConfig.getValue() fun getInAppUpdateBlockingVersion() = inAppUpdateBlockingVersionConfig.getValue() + fun getInAppUpdateFlexibleIntervalInDays() = inAppUpdateFlexibleIntervalConfig.getValue() } From a5a84c8eead11200a919ca90d46e83256ce8fa33 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 16:27:58 +0300 Subject: [PATCH 49/81] Fixes: detekt issue --- .../org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 27dccefa346c..98d473ead49b 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -249,6 +249,7 @@ class InAppUpdateManagerImpl( return result } + @Suppress("MagicNumber") private fun getFlexibleUpdateIntervalInMillis(): Long { Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): entered") val result = 1000 * 60 * 60 * 24 * remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays() From 82f908cc3f213af0f77dedb1a958dca7237606a9 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Wed, 15 May 2024 16:54:59 +0300 Subject: [PATCH 50/81] Fixes: in-app updated FF --- WordPress/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/build.gradle b/WordPress/build.gradle index ad020f8b7d7a..2729cd045f39 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -166,6 +166,7 @@ android { buildConfigField "boolean", "DASHBOARD_PERSONALIZATION", "false" buildConfigField "boolean", "ENABLE_SITE_MONITORING", "false" buildConfigField "boolean", "SYNC_PUBLISHING", "false" + buildConfigField "boolean", "ENABLE_IN_APP_UPDATES", "false" manifestPlaceholders = [magicLinkScheme:"wordpress"] } From 913a200d6e8c89a561567aa22403c8832405e571 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Wed, 15 May 2024 16:52:33 +0300 Subject: [PATCH 51/81] Replace callback with lambda in SmartLockHelper.java --- .../android/ui/accounts/SmartLockHelper.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java index 0bac7f2b6a63..94ff89bf02c1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java @@ -13,7 +13,6 @@ import com.google.android.gms.auth.api.Auth; import com.google.android.gms.auth.api.credentials.Credential; import com.google.android.gms.auth.api.credentials.CredentialRequest; -import com.google.android.gms.auth.api.credentials.CredentialRequestResult; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.api.CommonStatusCodes; @@ -82,33 +81,30 @@ public void smartLockAutoFill(@NonNull final Callback callback) { .setPasswordLoginSupported(true) .build(); Auth.CredentialsApi.request(mCredentialsClient, credentialRequest).setResultCallback( - new ResultCallback() { - @Override - public void onResult(@NonNull CredentialRequestResult result) { - Status status = result.getStatus(); - if (status.isSuccess()) { - Credential credential = result.getCredential(); - callback.onCredentialRetrieved(credential); - } else { - if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { - try { - Activity activity = getActivityAndCheckAvailability(); - if (activity == null) { - return; - } - // Prompt the user to choose a saved credential - status.startResolutionForResult(activity, RequestCodes.SMART_LOCK_READ); - } catch (IntentSender.SendIntentException e) { - AppLog.d(T.NUX, "SmartLock: Failed to send resolution for credential request"); - - callback.onCredentialsUnavailable(); + result -> { + Status status = result.getStatus(); + if (status.isSuccess()) { + Credential credential = result.getCredential(); + callback.onCredentialRetrieved(credential); + } else { + if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { + try { + Activity currentActivity = getActivityAndCheckAvailability(); + if (currentActivity == null) { + return; } - } else { - // The user must create an account or log in manually. - AppLog.d(T.NUX, "SmartLock: Unsuccessful credential request."); + // Prompt the user to choose a saved credential + status.startResolutionForResult(currentActivity, RequestCodes.SMART_LOCK_READ); + } catch (IntentSender.SendIntentException e) { + AppLog.d(T.NUX, "SmartLock: Failed to send resolution for credential request"); callback.onCredentialsUnavailable(); } + } else { + // The user must create an account or log in manually. + AppLog.d(T.NUX, "SmartLock: Unsuccessful credential request."); + + callback.onCredentialsUnavailable(); } } }); From 907f9ce20560521c518fb19367dee35c075655c7 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Wed, 15 May 2024 16:57:56 +0300 Subject: [PATCH 52/81] Fix a rare crash on login screen --- .../wordpress/android/ui/accounts/SmartLockHelper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java index 94ff89bf02c1..e0d0b0fe1278 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java @@ -82,6 +82,10 @@ public void smartLockAutoFill(@NonNull final Callback callback) { .build(); Auth.CredentialsApi.request(mCredentialsClient, credentialRequest).setResultCallback( result -> { + Activity currentActivity = getActivityAndCheckAvailability(); + if (currentActivity == null) { + return; + } Status status = result.getStatus(); if (status.isSuccess()) { Credential credential = result.getCredential(); @@ -89,10 +93,6 @@ public void smartLockAutoFill(@NonNull final Callback callback) { } else { if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { try { - Activity currentActivity = getActivityAndCheckAvailability(); - if (currentActivity == null) { - return; - } // Prompt the user to choose a saved credential status.startResolutionForResult(currentActivity, RequestCodes.SMART_LOCK_READ); } catch (IntentSender.SendIntentException e) { From 33bca44df0a2ccae66a2b84b7ca0fb5436eaa3d5 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Wed, 15 May 2024 17:10:39 +0300 Subject: [PATCH 53/81] Update RELEASE-NOTES.txt --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index cb54d0376091..7454a862db0e 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,7 +3,7 @@ 25.0 ----- * [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] - +* [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] 24.9 ----- From e9e35d0fbe4650828bed08c8e7aa045e05dbc47a Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Wed, 15 May 2024 17:15:20 -0400 Subject: [PATCH 54/81] [Bug] Fixed lateinit UninitializedPropertyAccessException --- .../ui/sitecreation/SiteCreationMainVM.kt | 7 +++--- .../previews/SitePreviewViewModel.kt | 22 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt index a7f69cc2fde0..978fe2561f65 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt @@ -347,9 +347,10 @@ class SiteCreationMainVM @Inject constructor( _onCompleted.value = NotCreated to isSiteTitleTaskCompleted() } - fun onWizardFinished(result: Created) { - siteCreationState = siteCreationState.copy(result = result) - _onCompleted.value = result to isSiteTitleTaskCompleted() + fun onWizardFinished(result: Created?) { + val nullCheckedResult = result ?: NotCreated + siteCreationState = siteCreationState.copy(result = nullCheckedResult) + _onCompleted.value = nullCheckedResult to isSiteTitleTaskCompleted() } private fun isSiteTitleTaskCompleted() = !siteCreationState.siteName.isNullOrBlank() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt index 84f96ab53e6c..d3e9554b21e6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt @@ -69,8 +69,7 @@ class SitePreviewViewModel @Inject constructor( private var siteDesign: String? = null private var isFree: Boolean = true - private lateinit var result: Created - private lateinit var domainName: String + private var result: Created? = null private val _uiState: MutableLiveData = MutableLiveData() val uiState: LiveData = _uiState @@ -78,8 +77,8 @@ class SitePreviewViewModel @Inject constructor( private val _preloadPreview: MutableLiveData = MutableLiveData() val preloadPreview: LiveData = _preloadPreview - private val _onOkButtonClicked = SingleLiveEvent() - val onOkButtonClicked: LiveData = _onOkButtonClicked + private val _onOkButtonClicked = SingleLiveEvent() + val onOkButtonClicked: LiveData = _onOkButtonClicked fun start(siteCreationState: SiteCreationState) { if (isStarted) return else isStarted = true @@ -90,12 +89,13 @@ class SitePreviewViewModel @Inject constructor( siteDesign = siteCreationState.siteDesign result = siteCreationState.result isFree = requireNotNull(siteCreationState.domain).isFree - domainName = getCleanUrl(result.site.url) ?: "" startPreLoadingWebView() - if (result is CreatedButNotFetched) { - launch { - fetchNewlyCreatedSiteModel(result.site.siteId)?.let { - result = Completed(it) + result?.let { + if (it is CreatedButNotFetched) { + launch { + fetchNewlyCreatedSiteModel(it.site.siteId)?.let { + result = Completed(it) + } } } } @@ -121,7 +121,7 @@ class SitePreviewViewModel @Inject constructor( } } // Load the newly created site in the webview - result.site.url?.let { url -> + result?.site?.url?.let { url -> val urlToLoad = urlUtils.addUrlSchemeIfNeeded( url = url, addHttps = isWordPressComSubDomain(url) @@ -172,7 +172,7 @@ class SitePreviewViewModel @Inject constructor( private fun getCleanUrl(url: String) = StringUtils.removeTrailingSlash(urlUtils.removeScheme(url)) private fun createSitePreviewData(): UrlData { - val url = domainName + val url = result?.let { getCleanUrl(it.site.url) ?: "" } ?: "" val subDomain = urlUtils.extractSubDomain(url) val fullUrl = urlUtils.addUrlSchemeIfNeeded(url, true) val subDomainIndices = 0 to subDomain.length From 03fc44bbf39324ce5f94a42eeb4d6ca670667261 Mon Sep 17 00:00:00 2001 From: Thomas Horta Date: Wed, 15 May 2024 19:31:53 -0300 Subject: [PATCH 55/81] Add .aiexclude file for safer Gemini usage Based on: https://developer.android.com/studio/preview/gemini/aiexclude --- .aiexclude | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .aiexclude diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 000000000000..05433f9baf75 --- /dev/null +++ b/.aiexclude @@ -0,0 +1,39 @@ +# OS X generated file +.DS_Store + +# Build-related files +fastlane/ + +# Key-related files +.jks +.keystore + +# Backup files +.bak + +# Generated files +bin/ +gen/ +build/ +build.log + +# Built application files +.apk +.ap_ +.aab + +# Dex VM files +.dex + +# Configuration files +.configure +.configure-files/ +google-services.json +google-upload-credentials.json +firebase.secrets.json +sentry.properties + +# Gradle files +gradle.properties +local.properties +local-builds.gradle From 49956888d79f3c362325ebb3c8c96cc2aae4ecf7 Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Wed, 15 May 2024 19:17:53 -0400 Subject: [PATCH 56/81] Fix failing test. --- .../ui/sitecreation/previews/SitePreviewViewModelTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt index b1ed67de828e..7fba132c2b51 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt @@ -62,7 +62,7 @@ class SitePreviewViewModelTest : BaseUnitTest() { private lateinit var uiStateObserver: Observer @Mock - private lateinit var onOkClickedObserver: Observer + private lateinit var onOkClickedObserver: Observer @Mock private lateinit var preloadPreviewObserver: Observer From a20e6a2fb25f203d668ffbd781fb2e317736f97d Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Wed, 15 May 2024 17:33:31 -0700 Subject: [PATCH 57/81] Add null check to calendar.time = date --- .../wordpress/android/ui/posts/PublishSettingsViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt index d8672ec395f5..8d2bd9dc1f4d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt @@ -23,6 +23,7 @@ import org.wordpress.android.util.LocaleManagerWrapper import org.wordpress.android.viewmodel.Event import org.wordpress.android.viewmodel.ResourceProvider import java.util.Calendar +import java.util.Date abstract class PublishSettingsViewModel constructor( @@ -212,7 +213,10 @@ constructor( val dateCreated = postRepository.dateCreated // Set the currently selected time if available if (!TextUtils.isEmpty(dateCreated)) { - calendar.time = DateTimeUtils.dateFromIso8601(dateCreated) + // Calendar.setTime(Date date) expects a non-null Date object + val maybeDate: Date? = DateTimeUtils.dateFromIso8601(dateCreated) + maybeDate?.let { date -> calendar.time = date } + calendar.timeZone = localeManagerWrapper.getTimeZone() } return calendar From 79399f419b6f7c2d965e24f94499323f0f6876b0 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 16 May 2024 11:17:42 +0300 Subject: [PATCH 58/81] Move the code snippet which caused ANR to background thread --- .../ui/reader/views/ReaderSiteHeaderView.java | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java index 3fd01fae0109..6943e15a488a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java @@ -3,6 +3,8 @@ import android.content.Context; import android.icu.text.CompactDecimalFormat; import android.icu.text.NumberFormat; +import android.os.Handler; +import android.os.Looper; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -31,6 +33,9 @@ import org.wordpress.android.util.image.BlavatarShape; import org.wordpress.android.util.image.ImageManager; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import javax.inject.Inject; /** @@ -55,6 +60,10 @@ public interface OnBlogInfoLoadedListener { private OnBlogInfoLoadedListener mBlogInfoListener; private OnFollowListener mFollowListener; + private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + + @Inject AccountStore mAccountStore; @Inject ImageManager mImageManager; @Inject ReaderTracker mReaderTracker; @@ -103,7 +112,6 @@ public void loadBlogInfo( mBlogId = blogId; mFeedId = feedId; - final ReaderBlog localBlogInfo; if (blogId == 0 && feedId == 0) { ToastUtils.showToast(getContext(), R.string.reader_toast_err_show_blog); return; @@ -111,33 +119,35 @@ public void loadBlogInfo( mIsFeed = ReaderUtils.isExternalFeed(mBlogId, mFeedId); - if (mIsFeed) { - localBlogInfo = ReaderBlogTable.getFeedInfo(mFeedId); - } else { - localBlogInfo = ReaderBlogTable.getBlogInfo(mBlogId); - } - - if (localBlogInfo != null) { - showBlogInfo(localBlogInfo, source); - } - - // then get from server if doesn't exist locally or is time to update it - if (localBlogInfo == null || ReaderBlogTable.isTimeToUpdateBlogInfo(localBlogInfo)) { - ReaderActions.UpdateBlogInfoListener listener = new ReaderActions.UpdateBlogInfoListener() { - @Override - public void onResult(ReaderBlog serverBlogInfo) { - if (isAttachedToWindow()) { - showBlogInfo(serverBlogInfo, source); - } - } - }; - + // run in background to avoid ANR + mExecutorService.execute(() -> { + final ReaderBlog localBlogInfo; if (mIsFeed) { - ReaderBlogActions.updateFeedInfo(mFeedId, null, listener); + localBlogInfo = ReaderBlogTable.getFeedInfo(mFeedId); } else { - ReaderBlogActions.updateBlogInfo(mBlogId, null, listener); + localBlogInfo = ReaderBlogTable.getBlogInfo(mBlogId); } - } + + mMainHandler.post(() -> { + if (localBlogInfo != null) { + showBlogInfo(localBlogInfo, source); + } + // then get from server if doesn't exist locally or is time to update it + if (localBlogInfo == null || ReaderBlogTable.isTimeToUpdateBlogInfo(localBlogInfo)) { + ReaderActions.UpdateBlogInfoListener listener = serverBlogInfo -> { + if (isAttachedToWindow()) { + showBlogInfo(serverBlogInfo, source); + } + }; + + if (mIsFeed) { + ReaderBlogActions.updateFeedInfo(mFeedId, null, listener); + } else { + ReaderBlogActions.updateBlogInfo(mBlogId, null, listener); + } + } + }); + }); } private void showBlogInfo(ReaderBlog blogInfo, String source) { From 267ea8d000e496dcb9006b37d7583abba5e5fb0d Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 16 May 2024 11:38:04 +0300 Subject: [PATCH 59/81] Adds: property for trackUpdateDismissed --- .../org/wordpress/android/inappupdate/IInAppUpdateManager.kt | 2 +- .../android/inappupdate/InAppUpdateAnalyticsTracker.kt | 4 ++-- .../wordpress/android/inappupdate/InAppUpdateManagerImpl.kt | 4 ++-- .../wordpress/android/inappupdate/InAppUpdateManagerNoop.kt | 2 +- .../java/org/wordpress/android/ui/main/WPMainActivity.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt index b0f6e2f00f51..e87c284de84e 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt @@ -5,7 +5,7 @@ import android.app.Activity interface IInAppUpdateManager { fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) fun completeAppUpdate() - fun cancelAppUpdate() + fun cancelAppUpdate(updateType: Int) fun onUserAcceptedAppUpdate(updateType: Int) companion object { diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt index 5b98fc09d46d..4bc6b4a912eb 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt @@ -16,8 +16,8 @@ class InAppUpdateAnalyticsTracker @Inject constructor( tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, createPropertyMap(updateType)) } - fun trackUpdateDismissed() { - tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED) + fun trackUpdateDismissed(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, createPropertyMap(updateType)) } private fun createPropertyMap(updateType: Int): Map { diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 98d473ead49b..90a59e418571 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -57,10 +57,10 @@ class InAppUpdateManagerImpl( appUpdateManager.completeUpdate() } - override fun cancelAppUpdate() { + override fun cancelAppUpdate(updateType: Int) { Log.d(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) - inAppUpdateAnalyticsTracker.trackUpdateDismissed() + inAppUpdateAnalyticsTracker.trackUpdateDismissed(updateType) } override fun onUserAcceptedAppUpdate(updateType: Int) { diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt index cc944057c75f..81aeccd05708 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt @@ -11,7 +11,7 @@ class InAppUpdateManagerNoop: IInAppUpdateManager { /* Empty implementation */ } - override fun cancelAppUpdate() { + override fun cancelAppUpdate(updateType: Int) { /* Empty implementation */ } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index ceb29f9ef49c..7f0a35db9e8e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -1531,7 +1531,7 @@ private void handleUpdateResult(int resultCode, int updateType) { mInAppUpdateManager.onUserAcceptedAppUpdate(updateType); } else if (resultCode == RESULT_CANCELED) { // The user denied the update - mInAppUpdateManager.cancelAppUpdate(); + mInAppUpdateManager.cancelAppUpdate(updateType); } } From 03a7cbdbc7eff9127ddf7e0081440ac5df84b085 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 16 May 2024 11:56:40 +0300 Subject: [PATCH 60/81] Update the release note --- RELEASE-NOTES.txt | 1 + .../android/ui/reader/views/ReaderSiteHeaderView.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 7454a862db0e..4b04f4bb87e6 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ ----- * [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] * [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] +* [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] 24.9 ----- diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java index 6943e15a488a..7516e164beae 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java @@ -62,8 +62,7 @@ public interface OnBlogInfoLoadedListener { private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - - + @Inject AccountStore mAccountStore; @Inject ImageManager mImageManager; @Inject ReaderTracker mReaderTracker; From a488fe6c355beab5dc156af05f892444d4978f28 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 16 May 2024 13:14:53 +0300 Subject: [PATCH 61/81] Adds: check interval for immediate updates --- .../android/inappupdate/InAppUpdateManagerImpl.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 90a59e418571..c26650ab3b23 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -98,7 +98,9 @@ class InAppUpdateManagerImpl( } if (isImmediateUpdateNecessary()) { - requestImmediateUpdate(appUpdateInfo, activity) + if (shouldRequestImmediateUpdate()) { + requestImmediateUpdate(appUpdateInfo, activity) + } } else if (shouldRequestFlexibleUpdate()) { requestFlexibleUpdate(appUpdateInfo, activity) } @@ -128,10 +130,10 @@ class InAppUpdateManagerImpl( @Suppress("TooGenericExceptionCaught") private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { Log.d(TAG, "requestUpdate(): entered with updateType = $updateType") + saveLastUpdateRequestInfo(appUpdateInfo) val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE } else { - saveLastUpdateRequestInfo(appUpdateInfo) APP_UPDATE_FLEXIBLE_REQUEST_CODE } try { @@ -249,6 +251,13 @@ class InAppUpdateManagerImpl( return result } + private fun shouldRequestImmediateUpdate(): Boolean { + Log.d(TAG, "shouldRequestImmediateUpdate(): entered") + val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS + Log.d(TAG, "shouldRequestFlexibleUpdate(): result = $result") + return result + } + @Suppress("MagicNumber") private fun getFlexibleUpdateIntervalInMillis(): Long { Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): entered") @@ -276,7 +285,7 @@ class InAppUpdateManagerImpl( companion object { private const val TAG = "AppUpdateChecker" - + private const val IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS = 1000 * 60 * 5 // 5 minutes private const val PREF_NAME = "in_app_update_prefs" private const val KEY_LAST_APP_UPDATE_CHECK_VERSION = "last_app_update_check_version" private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" From cb17fcaba4ea43747bc523de664ab82e77b2a1ab Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Thu, 16 May 2024 16:18:13 +0300 Subject: [PATCH 62/81] Fix a crash that occurs when navigating back from the people screen --- .../wordpress/android/ui/people/PeopleManagementActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java index 0add5ecb5c0b..ca3a29e92275 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java @@ -604,7 +604,7 @@ private void refreshDetailFragment() { private boolean navigateBackToPeopleListFragment() { FragmentManager fragmentManager = getSupportFragmentManager(); - if (fragmentManager.getBackStackEntryCount() > 0) { + if (!fragmentManager.isStateSaved() && fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStack(); ActionBar actionBar = getSupportActionBar(); From fa300ad65195c333ab0f3fc3ca72ef6bebca7baf Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Thu, 16 May 2024 16:58:17 +0300 Subject: [PATCH 63/81] Update RELEASE-NOTES.txt --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 7454a862db0e..f0595fc3e338 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ ----- * [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] * [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] +* [*] Fix a crash that occurs when remove a user [https://github.com/wordpress-mobile/WordPress-Android/pull/20837] 24.9 ----- From 0747bbde23b7cdd791c80ef688f7b218cf16e431 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Thu, 16 May 2024 15:45:48 +0300 Subject: [PATCH 64/81] Fix the crash on featured image confirmation dialog --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index d4c8c63b0770..279ed0d5db49 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -900,6 +900,10 @@ public void onClick(DialogInterface dialog, int id) { @UiThread public void showFeaturedImageConfirmationDialog(final int mediaId) { + if (isStateSaved()) { + return; + } + GutenbergDialogFragment dialog = new GutenbergDialogFragment(); dialog.initialize( TAG_REPLACE_FEATURED_DIALOG, From 62250f0c884571cd8dbc36147611dc33d43a0d51 Mon Sep 17 00:00:00 2001 From: Irfan Omur Date: Thu, 16 May 2024 15:58:59 +0300 Subject: [PATCH 65/81] Update RELEASE-NOTES.txt --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 7454a862db0e..6472b694a351 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ ----- * [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] * [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] +* [*] Fixed a rare crash on the featured image confirmation dialog [https://github.com/wordpress-mobile/WordPress-Android/pull/20836] 24.9 ----- From d7963e63c69412f9033f54d1defc114eecef3e23 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Thu, 16 May 2024 17:12:44 +0300 Subject: [PATCH 66/81] Adds: unit tests for in-app updates --- .../InAppUpdateAnalyticsTracker.kt | 6 +- .../inappupdate/InAppUpdateManagerImpl.kt | 11 +- .../InAppUpdateAnalyticsTrackerTest.kt | 133 ++++++++++ .../inappupdate/InAppUpdateManagerImplTest.kt | 235 ++++++++++++++++++ 4 files changed, 378 insertions(+), 7 deletions(-) create mode 100644 WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt create mode 100644 WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt index 4bc6b4a912eb..dab51e87f88f 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt @@ -29,8 +29,8 @@ class InAppUpdateAnalyticsTracker @Inject constructor( } companion object { - private const val PROPERTY_UPDATE_TYPE = "type" - private const val UPDATE_TYPE_FLEXIBLE = "flexible" - private const val UPDATE_TYPE_BLOCKING = "blocking" + const val PROPERTY_UPDATE_TYPE = "type" + const val UPDATE_TYPE_FLEXIBLE = "flexible" + const val UPDATE_TYPE_BLOCKING = "blocking" } } diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index c26650ab3b23..5d59b2e760b5 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -253,8 +253,10 @@ class InAppUpdateManagerImpl( private fun shouldRequestImmediateUpdate(): Boolean { Log.d(TAG, "shouldRequestImmediateUpdate(): entered") - val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS - Log.d(TAG, "shouldRequestFlexibleUpdate(): result = $result") + val currentTime = currentTimeProvider.invoke() + val diff = currentTime - getLastUpdateRequestedTime() + val result = diff >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS + Log.d(TAG, "shouldRequestImmediateUpdate(): result = $result") return result } @@ -284,10 +286,11 @@ class InAppUpdateManagerImpl( } companion object { + const val IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS = 1000 * 60 * 5 // 5 minutes + const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" + private const val TAG = "AppUpdateChecker" - private const val IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS = 1000 * 60 * 5 // 5 minutes private const val PREF_NAME = "in_app_update_prefs" private const val KEY_LAST_APP_UPDATE_CHECK_VERSION = "last_app_update_check_version" - private const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" } } diff --git a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt new file mode 100644 index 000000000000..12932813e511 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt @@ -0,0 +1,133 @@ +package org.wordpress.android.inappupdate + +import com.google.android.play.core.install.model.AppUpdateType +import org.assertj.core.api.Assertions +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.mock +import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.PROPERTY_UPDATE_TYPE +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.UPDATE_TYPE_BLOCKING +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.UPDATE_TYPE_FLEXIBLE + +@RunWith(MockitoJUnitRunner::class) +class InAppUpdateAnalyticsTrackerTest { + private val analyticsTracker: AnalyticsTrackerWrapper = mock() + lateinit var tracker: InAppUpdateAnalyticsTracker + + private val flexibleProps = mapOf( + PROPERTY_UPDATE_TYPE to UPDATE_TYPE_FLEXIBLE + ) + private val blockingProps = mapOf( + PROPERTY_UPDATE_TYPE to UPDATE_TYPE_BLOCKING + ) + private val emptyProps = emptyMap() + + @Before + fun setUp() { + tracker = InAppUpdateAnalyticsTracker(analyticsTracker) + } + + @Test + fun `trackUpdateShown tracks flexible update shown`() { + tracker.trackUpdateShown(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateShown tracks immediate update shown`() { + tracker.trackUpdateShown(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateShown tracks invalid update shown`() { + tracker.trackUpdateShown(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = emptyProps + ) + } + + @Test + fun `trackUpdateAccepted tracks flexible update accepted`() { + tracker.trackUpdateAccepted(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateAccepted tracks immediate update accepted`() { + tracker.trackUpdateAccepted(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateAccepted tracks invalid update accepted`() { + tracker.trackUpdateAccepted(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = emptyProps + ) + } + + @Test + fun `trackUpdateDismissed tracks flexible update dismissed`() { + tracker.trackUpdateDismissed(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateDismissed tracks immediate update dismissed`() { + tracker.trackUpdateDismissed(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateDismissed tracks invalid update dismissed`() { + tracker.trackUpdateDismissed(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = emptyProps + ) + } + + private fun mapCaptor() = argumentCaptor>() + private fun verifyCorrectEventTracking( + expectedEvent: AnalyticsTracker.Stat, + expectedProps: Map, + expectedTimes: Int = 1 + ) { + mapCaptor().apply { + verify(analyticsTracker, times(expectedTimes)).track( + eq(expectedEvent), + capture() + ) + Assertions.assertThat(firstValue).isEqualTo(expectedProps) + } + } +} diff --git a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt new file mode 100644 index 000000000000..8000f13c9970 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt @@ -0,0 +1,235 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity +import android.content.Context +import android.content.SharedPreferences +import com.google.android.gms.tasks.OnFailureListener +import com.google.android.gms.tasks.OnSuccessListener +import com.google.android.gms.tasks.Task +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.UpdateAvailability +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.ArgumentMatchers.anyString +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_FLEXIBLE_REQUEST_CODE +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_IMMEDIATE_REQUEST_CODE +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl.Companion.IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl.Companion.KEY_LAST_APP_UPDATE_CHECK_TIME +import org.wordpress.android.util.BuildConfigWrapper +import org.wordpress.android.util.config.RemoteConfigWrapper + +@RunWith(MockitoJUnitRunner::class) +class InAppUpdateManagerImplTest { + @Mock + lateinit var applicationContext: Context + + @Mock + lateinit var appUpdateManager: AppUpdateManager + + @Mock + lateinit var remoteConfigWrapper: RemoteConfigWrapper + + @Mock + lateinit var buildConfigWrapper: BuildConfigWrapper + + @Mock + lateinit var inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker + + @Mock + lateinit var updateListener: IInAppUpdateListener + + @Mock + lateinit var activity: Activity + + @Mock + lateinit var appUpdateInfo: AppUpdateInfo + + @Mock + lateinit var sharedPreferences: SharedPreferences + + @Mock + lateinit var sharedPreferencesEditor: SharedPreferences.Editor + + lateinit var currentTimeProvider: () -> Long + + lateinit var inAppUpdateManager: InAppUpdateManagerImpl + + @Before + fun setUp() { + currentTimeProvider = {1715866314746L} // Thu May 16 2024 13:31:54 UTC + + // Mock SharedPreferences behavior + `when`(applicationContext.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences) + `when`(sharedPreferences.getInt(anyString(), anyInt())).thenReturn(-1) + `when`(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor) + `when`(sharedPreferencesEditor.putInt(anyString(), anyInt())).thenReturn(sharedPreferencesEditor) + `when`(sharedPreferencesEditor.putLong(anyString(), anyLong())).thenReturn(sharedPreferencesEditor) + + inAppUpdateManager = InAppUpdateManagerImpl( + applicationContext, + appUpdateManager, + remoteConfigWrapper, + buildConfigWrapper, + inAppUpdateAnalyticsTracker, + currentTimeProvider + ) + } + + @Test + fun `checkForAppUpdate when update is not available does not trigger update`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_NOT_AVAILABLE) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager.appUpdateInfo).addOnSuccessListener(any()) + verify(appUpdateManager, times(0)).startUpdateFlowForResult( + any(), + any(), + any(), + anyInt() + ) + } + + @Test + fun `checkForAppUpdate when update is downloaded calls update listener`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.DOWNLOADED) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(updateListener).onAppUpdateDownloaded() + } + + @Test + fun `checkForAppUpdate requests immediate update when necessary`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.UNKNOWN) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) // current version + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(200) // blocking version + val lastCheckTimestamp = currentTimeProvider.invoke() - IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS + `when`(sharedPreferences.getLong( eq(KEY_LAST_APP_UPDATE_CHECK_TIME), anyLong())).thenReturn(lastCheckTimestamp) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + any(), + any(), + any(), + eq(APP_UPDATE_IMMEDIATE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate requests flexible update when necessary`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.UNKNOWN) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) // current version + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(50) // blocking version + `when`(remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays()).thenReturn(1) + val lastCheckTimestamp = currentTimeProvider.invoke() - 1000*60*60*24 + `when`(sharedPreferences.getLong( eq(KEY_LAST_APP_UPDATE_CHECK_TIME), anyLong())).thenReturn(lastCheckTimestamp) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + any(), + any(), + any(), + eq(APP_UPDATE_FLEXIBLE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate handles developer triggered update in progress`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) + `when`(appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)).thenReturn(true) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(200) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + eq(appUpdateInfo), + eq(activity), + any(), + eq(APP_UPDATE_IMMEDIATE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate handles failure correctly`() { + // Arrange + val task = mockAppUpdateInfoTaskWithFailure() + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager.appUpdateInfo).addOnFailureListener(any()) + } + + // Helper method to mock Task with success + @Suppress("UNCHECKED_CAST") + private fun mockAppUpdateInfoTask(appUpdateInfo: AppUpdateInfo): Task { + val task = mock(Task::class.java) as Task + `when`(task.addOnSuccessListener(any())).thenAnswer { invocation -> + (invocation.arguments[0] as OnSuccessListener).onSuccess(appUpdateInfo) + task + } + `when`(task.addOnFailureListener(any())).thenReturn(task) + return task + } + + // Helper method to mock Task with failure + @Suppress("UNCHECKED_CAST") + private fun mockAppUpdateInfoTaskWithFailure(): Task { + val task = mock(Task::class.java) as Task + `when`(task.addOnFailureListener(any())).thenAnswer { invocation -> + (invocation.arguments[0] as OnFailureListener).onFailure(Exception("Update check failed")) + task + } + `when`(task.addOnSuccessListener(any())).thenReturn(task) + return task + } +} From b5489040c3bc02a125b531a88fffdfdee312bd0d Mon Sep 17 00:00:00 2001 From: Gerardo Date: Thu, 16 May 2024 18:47:17 +0200 Subject: [PATCH 67/81] Update Gutenberg ref --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9b774d3f6deb..1047ec8f8322 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = 'v1.118.0' + gutenbergMobileVersion = 'v1.119.0-alpha1' wordPressAztecVersion = 'v2.1.3' wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' From 5313b834711a682d4ca1323ef2479b3f1fc4aa1d Mon Sep 17 00:00:00 2001 From: Andy Valdez Date: Thu, 16 May 2024 16:27:13 -0400 Subject: [PATCH 68/81] [Bug] Fix crash caused by requireContext --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index d4c8c63b0770..766e1982c773 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -694,7 +694,8 @@ private ArrayList initOtherMediaImageOptions() { Bundle arguments = getArguments(); FragmentActivity activity = getActivity(); - if (activity == null || arguments == null) { + final Context context = getContext(); + if (activity == null || context == null || arguments == null) { AppLog.e(T.EDITOR, "Failed to initialize other media options because the activity or getArguments() is null"); return otherMediaOptions; @@ -710,13 +711,13 @@ private ArrayList initOtherMediaImageOptions() { String packageName = activity.getApplication().getPackageName(); if (supportStockPhotos) { int stockMediaResourceId = - getResources().getIdentifier("photo_picker_stock_media", "string", packageName); + context.getResources().getIdentifier("photo_picker_stock_media", "string", packageName); otherMediaOptions.add(new MediaOption(MEDIA_SOURCE_STOCK_MEDIA, getString(stockMediaResourceId))); } if (supportsTenor) { int gifMediaResourceId = - getResources().getIdentifier("photo_picker_gif", "string", packageName); + context.getResources().getIdentifier("photo_picker_gif", "string", packageName); otherMediaOptions.add(new MediaOption(GIF_MEDIA, getString(gifMediaResourceId))); } From ef66d6fc736587bc84b7ac31fe47c5c708bb73a0 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Fri, 17 May 2024 11:06:20 +0300 Subject: [PATCH 69/81] Removes: logs from in-app update feature --- .../inappupdate/InAppUpdateManagerImpl.kt | 107 ++++-------------- .../android/ui/main/WPMainActivity.java | 3 - 2 files changed, 20 insertions(+), 90 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 5d59b2e760b5..002a8c3c5611 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -42,9 +42,7 @@ class InAppUpdateManagerImpl( private var updateListener: IInAppUpdateListener? = null override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { - Log.d(TAG, "Checking for app update") updateListener = listener - appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> handleUpdateInfoSuccess(appUpdateInfo, activity) }.addOnFailureListener { exception -> @@ -53,25 +51,22 @@ class InAppUpdateManagerImpl( } override fun completeAppUpdate() { - Log.d(TAG, "completeAppUpdate(): entered") appUpdateManager.completeUpdate() } override fun cancelAppUpdate(updateType: Int) { - Log.d(TAG, "cancelAppUpdate(): entered") appUpdateManager.unregisterListener(installStateListener) inAppUpdateAnalyticsTracker.trackUpdateDismissed(updateType) } override fun onUserAcceptedAppUpdate(updateType: Int) { - Log.d(TAG, "onUserAcceptedAppUpdate(): entered") inAppUpdateAnalyticsTracker.trackUpdateAccepted(updateType) } private fun handleUpdateInfoSuccess(appUpdateInfo: AppUpdateInfo, activity: Activity) { when (appUpdateInfo.updateAvailability()) { UPDATE_NOT_AVAILABLE -> { - Log.d(TAG, "No update available") + /* do nothing */ } UPDATE_AVAILABLE -> { handleUpdateAvailable(appUpdateInfo, activity) @@ -79,15 +74,12 @@ class InAppUpdateManagerImpl( DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { handleUpdateInProgress(appUpdateInfo, activity) } - else -> { - Log.w(TAG, "Unknown update availability") - } + else -> { /* do nothing */ } } } private fun handleUpdateAvailable(appUpdateInfo: AppUpdateInfo, activity: Activity) { if (appUpdateInfo.installStatus() == DOWNLOADED) { - Log.d(TAG, "Update downloaded, notifying listener") updateListener?.onAppUpdateDownloaded() return } @@ -115,13 +107,11 @@ class InAppUpdateManagerImpl( } private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.d(TAG, "requestImmediateUpdate(): entered") updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) } private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.d(TAG, "requestFlexibleUpdate(): entered") appUpdateManager.registerListener(installStateListener) updateListener?.onAppUpdateStarted(AppUpdateType.FLEXIBLE) requestUpdate(AppUpdateType.FLEXIBLE, appUpdateInfo, activity) @@ -129,7 +119,6 @@ class InAppUpdateManagerImpl( @Suppress("TooGenericExceptionCaught") private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { - Log.d(TAG, "requestUpdate(): entered with updateType = $updateType") saveLastUpdateRequestInfo(appUpdateInfo) val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { APP_UPDATE_IMMEDIATE_REQUEST_CODE @@ -156,70 +145,45 @@ class InAppUpdateManagerImpl( override fun onStateUpdate(state: InstallState) { when (state.installStatus()) { DOWNLOADED -> { - Log.d(TAG, "installStateListener DOWNLOADED") updateListener?.onAppUpdateDownloaded() } INSTALLED -> { - Log.d(TAG, "installStateListener INSTALLED") updateListener?.onAppUpdateInstalled() appUpdateManager.unregisterListener(this) // 'this' refers to the listener object } CANCELED -> { - Log.d(TAG, "installStateListener CANCELED") updateListener?.onAppUpdateCancelled() appUpdateManager.unregisterListener(this) } FAILED -> { - Log.d(TAG, "installStateListener FAILED") updateListener?.onAppUpdateFailed() appUpdateManager.unregisterListener(this) } PENDING -> { - Log.d(TAG, "installStateListener PENDING") updateListener?.onAppUpdatePending() } - DOWNLOADING -> { - Log.d(TAG, "installStateListener DOWNLOADING") - } - INSTALLING -> { - Log.d(TAG, "installStateListener INSTALLING") - } - InstallStatus.UNKNOWN -> { - Log.d(TAG, "installStateListener UNKNOWN") + DOWNLOADING, INSTALLING, InstallStatus.UNKNOWN -> { + /* do nothing */ } } } } - private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo): Boolean { - Log.d(TAG, "isImmediateUpdateInProgress(): entered, " + - "appUpdateInfo.updateAvailability() = ${appUpdateInfo.updateAvailability()}," + - "appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) " + - "= ${appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)}" + - "isImmediateUpdateNecessary = ${isImmediateUpdateNecessary()}") - val result = appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo) = + appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && isImmediateUpdateNecessary() - Log.d(TAG, "isImmediateUpdateInProgress(): result = $result") - - return result - } - private fun saveLastUpdateRequestInfo(appUpdateInfo: AppUpdateInfo) { - Log.d(TAG, "setLastUpdateRequestedTime(): entered") - val currentTime = currentTimeProvider.invoke() - Log.d(TAG, "setLastUpdateRequestedTime(): currentTime = $currentTime") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, getAvailableUpdateAppVersion(appUpdateInfo)) - putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTime) + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTimeProvider.invoke()) apply() } } private fun resetLastUpdateRequestInfo() { - Log.d(TAG, "resetLastUpdateRequestedTime(): entered") val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) sharedPref.edit().apply { putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) @@ -228,45 +192,23 @@ class InAppUpdateManagerImpl( } } - private fun getLastUpdateRequestedVersion(): Int { - val defaultValue = -1 - val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - .getInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, defaultValue) - Log.d(TAG, "getLastUpdateRequestedVersion(): result = $result") - return result - } + private fun getLastUpdateRequestedVersion() = + applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) - private fun getLastUpdateRequestedTime(): Long { - val defaultValue = -1L - val result = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, defaultValue) - Log.d(TAG, "getLastUpdateRequestedTime(): result = $result") - return result - } + private fun getLastUpdateRequestedTime() = + applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) - private fun shouldRequestFlexibleUpdate(): Boolean { - Log.d(TAG, "shouldRequestFlexibleUpdate(): entered") - val result = currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= getFlexibleUpdateIntervalInMillis() - Log.d(TAG, "shouldRequestFlexibleUpdate(): result = $result") - return result - } + private fun shouldRequestFlexibleUpdate() = + currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= getFlexibleUpdateIntervalInMillis() - private fun shouldRequestImmediateUpdate(): Boolean { - Log.d(TAG, "shouldRequestImmediateUpdate(): entered") - val currentTime = currentTimeProvider.invoke() - val diff = currentTime - getLastUpdateRequestedTime() - val result = diff >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS - Log.d(TAG, "shouldRequestImmediateUpdate(): result = $result") - return result - } + private fun shouldRequestImmediateUpdate() = + currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS @Suppress("MagicNumber") - private fun getFlexibleUpdateIntervalInMillis(): Long { - Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): entered") - val result = 1000 * 60 * 60 * 24 * remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays() - Log.d(TAG, "getFlexibleUpdateIntervalInMillis(): result = $result") - return result.toLong() - } + private fun getFlexibleUpdateIntervalInMillis(): Long = + 1000 * 60 * 60 * 24 * remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays().toLong() private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() @@ -274,16 +216,7 @@ class InAppUpdateManagerImpl( private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() - private fun isImmediateUpdateNecessary(): Boolean { - Log.d(TAG, "isImmediateUpdateNecessary(): entered") - val currentVersion = getCurrentAppVersion() - val getLastBlockingAppVersion = getLastBlockingAppVersion() - Log.d(TAG, "isImmediateUpdateNecessary() called") - Log.d(TAG, "currentVersion = $currentVersion, lastBlockingVersion = $getLastBlockingAppVersion") - val result = getCurrentAppVersion() < getLastBlockingAppVersion() - Log.d(TAG, "isImmediateUpdateNecessary(): result = $result") - return result - } + private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() companion object { const val IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS = 1000 * 60 * 5 // 5 minutes diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 7f0a35db9e8e..01b0f803b526 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -1210,7 +1210,6 @@ protected void onResume() { } private void checkForInAppUpdate() { - Log.e("WPMainActivity", "checkForInAppUpdate() called"); mInAppUpdateManager.checkForAppUpdate(this, mInAppUpdateListener); } @@ -1241,11 +1240,9 @@ private void checkForInAppUpdate() { }; private void popupSnackbarForCompleteUpdate() { - Log.e("WPMainActivity", "showSnackBarForUpdate()"); WPSnackbar.make(findViewById(R.id.coordinator), R.string.in_app_update_available, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.in_app_update_restart, v -> { mInAppUpdateManager.completeAppUpdate(); - // todo: AnalyticsTracker.track(Stat.IN_APP_UPDATE_COMPLETED); }) .show(); } From a939a3dc9cfb9ffc7d2897cd315e0d1fee3e721c Mon Sep 17 00:00:00 2001 From: Gerardo Date: Fri, 17 May 2024 11:43:44 +0200 Subject: [PATCH 70/81] Update Gutenberg ref --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1047ec8f8322..ae6ac33a1ac1 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = 'v1.119.0-alpha1' + gutenbergMobileVersion = '6870-da95fc52ce7b2b09286d34b6d78a2c8652e69799' wordPressAztecVersion = 'v2.1.3' wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' From f8e12e825afc074bdc8898e182b826821d719302 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Fri, 17 May 2024 13:20:37 +0200 Subject: [PATCH 71/81] Update Gutenberg ref --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ae6ac33a1ac1..02f9a15490fd 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = '6870-da95fc52ce7b2b09286d34b6d78a2c8652e69799' + gutenbergMobileVersion = '6870-cb4c2c1f2b0290eefde78817f576764cbe26e463' wordPressAztecVersion = 'v2.1.3' wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' From fe0f5f84929b8490d7bfe03424d59487abceeb21 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Fri, 17 May 2024 13:22:52 +0200 Subject: [PATCH 72/81] Update Release Notes --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 4b04f4bb87e6..5f54eb3a1901 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -5,6 +5,7 @@ * [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] * [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] * [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] +* [*] [internal] Block Editor: Upgrade target sdk version to Android API 34 [https://github.com/wordpress-mobile/WordPress-Android/pull/20841] 24.9 ----- From 97fda0beffcb26385bcfb7193137bc5cf85efa90 Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Fri, 17 May 2024 16:59:33 -0700 Subject: [PATCH 73/81] Add null check --- .../org/wordpress/android/ui/prefs/SiteSettingsFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 4469e5763261..1433ed0265dc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -1301,7 +1301,7 @@ private void initBloggingReminders() { } private void setupBloggingRemindersBottomSheet() { - if (mBloggingRemindersPref == null || !isAdded()) { + if (mBloggingRemindersPref == null || !isAdded() || mSite == null) { return; } mBloggingRemindersViewModel.onBlogSettingsItemClicked(mSite.getId()); From ec5dbb6e9fe470f66e4961401135824bfce10b1d Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Mon, 20 May 2024 11:56:30 +0300 Subject: [PATCH 74/81] Adds: tracking for app restart to compete app update --- .../android/inappupdate/InAppUpdateAnalyticsTracker.kt | 4 ++++ .../wordpress/android/inappupdate/InAppUpdateManagerImpl.kt | 1 + .../org/wordpress/android/analytics/AnalyticsTracker.java | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt index dab51e87f88f..abf15b094bfd 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt @@ -20,6 +20,10 @@ class InAppUpdateAnalyticsTracker @Inject constructor( tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, createPropertyMap(updateType)) } + fun trackAppRestartToCompleteUpdate() { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_COMPLETED_WITH_APP_RESTART) + } + private fun createPropertyMap(updateType: Int): Map { return when (updateType) { AppUpdateType.FLEXIBLE -> mapOf(PROPERTY_UPDATE_TYPE to UPDATE_TYPE_FLEXIBLE) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 002a8c3c5611..2ed7a1e322ff 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -51,6 +51,7 @@ class InAppUpdateManagerImpl( } override fun completeAppUpdate() { + inAppUpdateAnalyticsTracker.trackAppRestartToCompleteUpdate() appUpdateManager.completeUpdate() } diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java index af3ad9ca14ce..c154d9cdd62d 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java @@ -1125,7 +1125,8 @@ public enum Stat { RESOLVE_AUTOSAVE_CONFLICT_DISMISSED, IN_APP_UPDATE_SHOWN, IN_APP_UPDATE_DISMISSED, - IN_APP_UPDATE_ACCEPTED; + IN_APP_UPDATE_ACCEPTED, + IN_APP_UPDATE_COMPLETED_WITH_APP_RESTART; /* * Please set the event name in the enum only if the new Stat's name in lower case does not match it. From 74b0577d01af8b62498986773720a78302b823c5 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Mon, 20 May 2024 11:05:26 +0200 Subject: [PATCH 75/81] Update Gutenberg ref --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 02f9a15490fd..df7da43a7353 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = '6870-cb4c2c1f2b0290eefde78817f576764cbe26e463' + gutenbergMobileVersion = 'v1.119.0-alpha2' wordPressAztecVersion = 'v2.1.3' wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' From c138065e79285b65ca01f11784b7477456d2046c Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Mon, 20 May 2024 13:11:39 +0300 Subject: [PATCH 76/81] Refactors: InAppUpdateListener --- .../inappupdate/IInAppUpdateListener.kt | 10 ------ .../inappupdate/IInAppUpdateManager.kt | 2 +- .../inappupdate/InAppUpdateListener.kt | 34 +++++++++++++++++++ .../inappupdate/InAppUpdateManagerImpl.kt | 4 +-- .../inappupdate/InAppUpdateManagerNoop.kt | 2 +- .../android/ui/main/WPMainActivity.java | 24 ++----------- .../inappupdate/InAppUpdateManagerImplTest.kt | 2 +- 7 files changed, 41 insertions(+), 37 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt deleted file mode 100644 index 114445ae8266..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateListener.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.wordpress.android.inappupdate - -interface IInAppUpdateListener { - fun onAppUpdateStarted(type: Int) - fun onAppUpdateDownloaded() - fun onAppUpdateInstalled() - fun onAppUpdateFailed() - fun onAppUpdateCancelled() - fun onAppUpdatePending() -} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt index e87c284de84e..fc1135f274c8 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt @@ -3,7 +3,7 @@ package org.wordpress.android.inappupdate import android.app.Activity interface IInAppUpdateManager { - fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) + fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) fun completeAppUpdate() fun cancelAppUpdate(updateType: Int) fun onUserAcceptedAppUpdate(updateType: Int) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt new file mode 100644 index 000000000000..e002395f3cd9 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt @@ -0,0 +1,34 @@ +package org.wordpress.android.inappupdate + +/** + * Abstract class for handling callbacks related to in-app update events. + * + * Each method provides a default implementation that does nothing, allowing + * implementers to only override the necessary methods without implementing + * all callback methods. + */ +abstract class InAppUpdateListener { + open fun onAppUpdateStarted(type: Int) { + // Default empty implementation + } + + open fun onAppUpdateDownloaded() { + // Default empty implementation + } + + open fun onAppUpdateInstalled() { + // Default empty implementation + } + + open fun onAppUpdateFailed() { + // Default empty implementation + } + + open fun onAppUpdateCancelled() { + // Default empty implementation + } + + open fun onAppUpdatePending() { + // Default empty implementation + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt index 2ed7a1e322ff..11ac7fc132e1 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -39,9 +39,9 @@ class InAppUpdateManagerImpl( private val inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker, private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} ): IInAppUpdateManager { - private var updateListener: IInAppUpdateListener? = null + private var updateListener: InAppUpdateListener? = null - override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { + override fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) { updateListener = listener appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> handleUpdateInfoSuccess(appUpdateInfo, activity) diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt index 81aeccd05708..d732dc62d7e9 100644 --- a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt @@ -3,7 +3,7 @@ package org.wordpress.android.inappupdate import android.app.Activity class InAppUpdateManagerNoop: IInAppUpdateManager { - override fun checkForAppUpdate(activity: Activity, listener: IInAppUpdateListener) { + override fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) { /* Empty implementation */ } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 01b0f803b526..d15191ccd33d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -40,7 +40,7 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.BuildConfig; -import org.wordpress.android.inappupdate.IInAppUpdateListener; +import org.wordpress.android.inappupdate.InAppUpdateListener; import org.wordpress.android.inappupdate.IInAppUpdateManager; import org.wordpress.android.R; import org.wordpress.android.WordPress; @@ -1213,30 +1213,10 @@ private void checkForInAppUpdate() { mInAppUpdateManager.checkForAppUpdate(this, mInAppUpdateListener); } - @NonNull final IInAppUpdateListener mInAppUpdateListener = new IInAppUpdateListener() { - @Override public void onAppUpdatePending() { - /* do nothing */ - } - + @NonNull final InAppUpdateListener mInAppUpdateListener = new InAppUpdateListener() { @Override public void onAppUpdateDownloaded() { popupSnackbarForCompleteUpdate(); } - @Override public void onAppUpdateStarted(int type) { - /* do nothing */ - } - - @Override public void onAppUpdateInstalled() { - /* do nothing */ - } - - @Override public void onAppUpdateFailed() { - /* do nothing */ - } - - @Override public void onAppUpdateCancelled() { - // Todo do I need to handle this? - /* do nothing */ - } }; private void popupSnackbarForCompleteUpdate() { diff --git a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt index 8000f13c9970..c54ba84bfd1e 100644 --- a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt @@ -51,7 +51,7 @@ class InAppUpdateManagerImplTest { lateinit var inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker @Mock - lateinit var updateListener: IInAppUpdateListener + lateinit var updateListener: InAppUpdateListener @Mock lateinit var activity: Activity From 6bcecb8fd6806bb7b3c52a427629160c72f9f853 Mon Sep 17 00:00:00 2001 From: Annmarie Ziegler Date: Mon, 20 May 2024 11:13:48 -0400 Subject: [PATCH 77/81] Implement voice to content feature flag --- WordPress/build.gradle | 1 + .../util/config/VoiceToContentFeatureConfig.kt | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt diff --git a/WordPress/build.gradle b/WordPress/build.gradle index 9a94a59d41d6..5d2535b7266d 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -151,6 +151,7 @@ android { buildConfigField "boolean", "READER_DISCOVER_NEW_ENDPOINT", "false" buildConfigField "boolean", "READER_READING_PREFERENCES", "false" buildConfigField "boolean", "READER_READING_PREFERENCES_FEEDBACK", "false" + buildConfigField "boolean", "VOICE_TO_CONTENT", "false" // Override these constants in jetpack product flavor to enable/ disable features buildConfigField "boolean", "ENABLE_SITE_CREATION", "true" diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt new file mode 100644 index 000000000000..75f5f9099108 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val VOICE_TO_CONTENT_REMOTE_FIELD = "voice_to_content" + +@Feature(remoteField = VOICE_TO_CONTENT_REMOTE_FIELD, defaultValue = false) +class VoiceToContentFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.VOICE_TO_CONTENT, + VOICE_TO_CONTENT_REMOTE_FIELD, +) From d9d362951ea6a84b70b919b25f7a536408d497bc Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Mon, 20 May 2024 14:41:03 -0700 Subject: [PATCH 78/81] Add null check to mBloggingRemindersViewModel --- .../org/wordpress/android/ui/prefs/SiteSettingsFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 1433ed0265dc..7c072d98a450 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -1301,7 +1301,7 @@ private void initBloggingReminders() { } private void setupBloggingRemindersBottomSheet() { - if (mBloggingRemindersPref == null || !isAdded() || mSite == null) { + if (mBloggingRemindersPref == null || !isAdded() || mSite == null || mBloggingRemindersViewModel == null) { return; } mBloggingRemindersViewModel.onBlogSettingsItemClicked(mSite.getId()); From 3e3072ecbcd2972c96c6a68a9996a43762d9f523 Mon Sep 17 00:00:00 2001 From: Pantelis Stampoulis Date: Tue, 21 May 2024 14:54:01 +0300 Subject: [PATCH 79/81] Updates: RELEASE-NOTES for in-app updates --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index ea21c397300c..276733001c4a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -8,6 +8,7 @@ * [*] Fixed a rare crash on the featured image confirmation dialog [https://github.com/wordpress-mobile/WordPress-Android/pull/20836] * [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] * [*] [internal] Block Editor: Upgrade target sdk version to Android API 34 [https://github.com/wordpress-mobile/WordPress-Android/pull/20841] +* [*] In-app updates feature [https://github.com/wordpress-mobile/WordPress-Android/pull/20822] 24.9 ----- From 77ed1116b02500d8d3cc4a7773570f5ce3bc007e Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Tue, 21 May 2024 20:06:54 +0530 Subject: [PATCH 80/81] =?UTF-8?q?=E2=86=91=20Updates:=20the=20release=20no?= =?UTF-8?q?tes=20to=20have=20internal=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 276733001c4a..888b25ae704e 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -8,7 +8,7 @@ * [*] Fixed a rare crash on the featured image confirmation dialog [https://github.com/wordpress-mobile/WordPress-Android/pull/20836] * [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] * [*] [internal] Block Editor: Upgrade target sdk version to Android API 34 [https://github.com/wordpress-mobile/WordPress-Android/pull/20841] -* [*] In-app updates feature [https://github.com/wordpress-mobile/WordPress-Android/pull/20822] +* [*] [internal] In-app updates feature [https://github.com/wordpress-mobile/WordPress-Android/pull/20822] 24.9 ----- From 421e2dfce5ab87003a6f35918610ae3706bedaf2 Mon Sep 17 00:00:00 2001 From: Aditi Bhatia Date: Tue, 21 May 2024 16:04:19 -0700 Subject: [PATCH 81/81] Update RELEASE-NOTES.txt --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 888b25ae704e..a3128872399b 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -7,6 +7,7 @@ * [*] Fix a crash that occurs when remove a user [https://github.com/wordpress-mobile/WordPress-Android/pull/20837] * [*] Fixed a rare crash on the featured image confirmation dialog [https://github.com/wordpress-mobile/WordPress-Android/pull/20836] * [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] +* [*] Fixed a crash that occurs with Blogging Reminders [https://github.com/wordpress-mobile/WordPress-Android/pull/20845] * [*] [internal] Block Editor: Upgrade target sdk version to Android API 34 [https://github.com/wordpress-mobile/WordPress-Android/pull/20841] * [*] [internal] In-app updates feature [https://github.com/wordpress-mobile/WordPress-Android/pull/20822]