diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt index f67614fd3be..b9d77464349 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionViewModel.kt @@ -5,9 +5,9 @@ import androidx.lifecycle.ViewModel import org.oppia.android.R import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.translation.AppLanguageResourceHandler -import org.oppia.android.app.utility.getLastUpdateTime -import org.oppia.android.app.utility.getVersionName import org.oppia.android.app.viewmodel.ObservableViewModel +import org.oppia.android.util.extensions.getLastUpdateTime +import org.oppia.android.util.extensions.getVersionName import javax.inject.Inject /** [ViewModel] for [AppVersionFragment]. */ diff --git a/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeActionListener.kt b/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeActionListener.kt index b9edef60fcc..73900fb119b 100644 --- a/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeActionListener.kt +++ b/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeActionListener.kt @@ -1,9 +1,24 @@ package org.oppia.android.app.notice -import org.oppia.android.app.splash.DeprecationNoticeActionType +import org.oppia.android.app.model.DeprecationNoticeType /** Listener for when an option on any deprecation dialog is clicked. */ interface DeprecationNoticeActionListener { /** Called when a dialog button is clicked. */ - fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) + fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) +} + +/** Sealed data class for the response to a deprecation notice action. */ +sealed class DeprecationNoticeActionResponse { + /** Action for when the user presses the 'Close' button on a deprecation dialog. */ + object Close : DeprecationNoticeActionResponse() + + /** Action for when the user presses the 'Dismiss' button on a deprecation dialog. */ + data class Dismiss( + val deprecationNoticeType: DeprecationNoticeType, + val deprecatedVersion: Int, + ) : DeprecationNoticeActionResponse() + + /** Action for when the user presses the 'Update' button on a deprecation dialog. */ + object Update : DeprecationNoticeActionResponse() } diff --git a/app/src/main/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentPresenter.kt index 265670fb3be..ca7bf0c46a7 100644 --- a/app/src/main/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentPresenter.kt @@ -4,14 +4,13 @@ import android.app.Dialog import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import org.oppia.android.R -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.translation.AppLanguageResourceHandler import javax.inject.Inject /** Presenter class responsible for showing an app deprecation dialog to the user. */ class ForcedAppDeprecationNoticeDialogFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, ) { private val deprecationNoticeActionListener by lazy { activity as DeprecationNoticeActionListener @@ -31,12 +30,12 @@ class ForcedAppDeprecationNoticeDialogFragmentPresenter @Inject constructor( ) .setPositiveButton(R.string.forced_app_update_dialog_update_button_text) { _, _ -> deprecationNoticeActionListener.onActionButtonClicked( - DeprecationNoticeActionType.UPDATE + DeprecationNoticeActionResponse.Update ) } .setNegativeButton(R.string.forced_app_update_dialog_close_button_text) { _, _ -> deprecationNoticeActionListener.onActionButtonClicked( - DeprecationNoticeActionType.CLOSE + DeprecationNoticeActionResponse.Close ) } .setCancelable(false) diff --git a/app/src/main/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentPresenter.kt index be4b938c522..47e5de99de3 100644 --- a/app/src/main/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentPresenter.kt @@ -4,14 +4,18 @@ import android.app.Dialog import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import org.oppia.android.R -import org.oppia.android.app.splash.DeprecationNoticeActionType +import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.platformparameter.OptionalAppUpdateVersionCode +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** Presenter class responsible for showing an optional update dialog to the user. */ class OptionalAppDeprecationNoticeDialogFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @OptionalAppUpdateVersionCode + private val optionalAppUpdateVersionCode: PlatformParameterValue, ) { private val deprecationNoticeActionListener by lazy { activity as DeprecationNoticeActionListener @@ -31,12 +35,15 @@ class OptionalAppDeprecationNoticeDialogFragmentPresenter @Inject constructor( ) .setPositiveButton(R.string.optional_app_update_dialog_update_button_text) { _, _ -> deprecationNoticeActionListener.onActionButtonClicked( - DeprecationNoticeActionType.UPDATE + DeprecationNoticeActionResponse.Update ) } .setNegativeButton(R.string.optional_app_update_dialog_dismiss_button_text) { _, _ -> deprecationNoticeActionListener.onActionButtonClicked( - DeprecationNoticeActionType.DISMISS + DeprecationNoticeActionResponse.Dismiss( + deprecationNoticeType = DeprecationNoticeType.APP_DEPRECATION, + deprecatedVersion = optionalAppUpdateVersionCode.value + ) ) } .setCancelable(false) diff --git a/app/src/main/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentPresenter.kt index efcfc84ed20..d7f875c7400 100644 --- a/app/src/main/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentPresenter.kt @@ -4,14 +4,18 @@ import android.app.Dialog import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import org.oppia.android.R -import org.oppia.android.app.splash.DeprecationNoticeActionType +import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.util.platformparameter.LowestSupportedApiLevel +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** Presenter class responsible for showing an OS deprecation dialog to the user. */ class OsDeprecationNoticeDialogFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @LowestSupportedApiLevel + private val lowestSupportedApiLevel: PlatformParameterValue ) { private val deprecationNoticeActionListener by lazy { activity as DeprecationNoticeActionListener @@ -31,7 +35,10 @@ class OsDeprecationNoticeDialogFragmentPresenter @Inject constructor( ) .setNegativeButton(R.string.os_deprecation_dialog_dismiss_button_text) { _, _ -> deprecationNoticeActionListener.onActionButtonClicked( - DeprecationNoticeActionType.DISMISS + DeprecationNoticeActionResponse.Dismiss( + deprecationNoticeType = DeprecationNoticeType.OS_DEPRECATION, + deprecatedVersion = lowestSupportedApiLevel.value + ) ) } .setCancelable(false) diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/ForcedAppDeprecationNoticeDialogFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/notice/testing/ForcedAppDeprecationNoticeDialogFragmentTestActivity.kt index e6b223af14d..f147a68ea66 100644 --- a/app/src/main/java/org/oppia/android/app/notice/testing/ForcedAppDeprecationNoticeDialogFragmentTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/notice/testing/ForcedAppDeprecationNoticeDialogFragmentTestActivity.kt @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing import android.os.Bundle import org.oppia.android.app.notice.DeprecationNoticeActionListener +import org.oppia.android.app.notice.DeprecationNoticeActionResponse import org.oppia.android.app.notice.ForcedAppDeprecationNoticeDialogFragment -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.testing.activity.TestActivity /** [TestActivity] for setting up a test environment for testing the beta notice dialog. */ @@ -24,7 +24,7 @@ class ForcedAppDeprecationNoticeDialogFragmentTestActivity : .showNow(supportFragmentManager, "forced_app_deprecation_dialog") } - override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) { - mockCallbackListener.onActionButtonClicked(noticeType) + override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) { + mockCallbackListener.onActionButtonClicked(noticeActionResponse) } } diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/OptionalAppDeprecationNoticeDialogFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/notice/testing/OptionalAppDeprecationNoticeDialogFragmentTestActivity.kt index d3ffd8b519e..fec17b953ff 100644 --- a/app/src/main/java/org/oppia/android/app/notice/testing/OptionalAppDeprecationNoticeDialogFragmentTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/notice/testing/OptionalAppDeprecationNoticeDialogFragmentTestActivity.kt @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing import android.os.Bundle import org.oppia.android.app.notice.DeprecationNoticeActionListener +import org.oppia.android.app.notice.DeprecationNoticeActionResponse import org.oppia.android.app.notice.OptionalAppDeprecationNoticeDialogFragment -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.testing.activity.TestActivity /** [TestActivity] for setting up a test environment for testing the beta notice dialog. */ @@ -24,7 +24,7 @@ class OptionalAppDeprecationNoticeDialogFragmentTestActivity : .showNow(supportFragmentManager, "optional_app_deprecation_dialog") } - override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) { - mockCallbackListener.onActionButtonClicked(noticeType) + override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) { + mockCallbackListener.onActionButtonClicked(noticeActionResponse) } } diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/OsDeprecationNoticeDialogFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/notice/testing/OsDeprecationNoticeDialogFragmentTestActivity.kt index 13923f43fd8..53801dd3d4e 100644 --- a/app/src/main/java/org/oppia/android/app/notice/testing/OsDeprecationNoticeDialogFragmentTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/notice/testing/OsDeprecationNoticeDialogFragmentTestActivity.kt @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing import android.os.Bundle import org.oppia.android.app.notice.DeprecationNoticeActionListener +import org.oppia.android.app.notice.DeprecationNoticeActionResponse import org.oppia.android.app.notice.OsDeprecationNoticeDialogFragment -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.testing.activity.TestActivity /** [TestActivity] for setting up a test environment for testing the beta notice dialog. */ @@ -24,7 +24,7 @@ class OsDeprecationNoticeDialogFragmentTestActivity : .showNow(supportFragmentManager, "os_deprecation_dialog") } - override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) { - mockCallbackListener.onActionButtonClicked(noticeType) + override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) { + mockCallbackListener.onActionButtonClicked(noticeActionResponse) } } diff --git a/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt index f9310821ddb..062139ce975 100644 --- a/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt +++ b/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt @@ -11,21 +11,13 @@ import org.oppia.android.app.fragment.FragmentComponentBuilderInjector import org.oppia.android.app.fragment.FragmentComponentFactory import org.oppia.android.app.model.ScreenName.SPLASH_ACTIVITY import org.oppia.android.app.notice.BetaNoticeClosedListener +import org.oppia.android.app.notice.DeprecationNoticeActionListener +import org.oppia.android.app.notice.DeprecationNoticeActionResponse import org.oppia.android.app.notice.DeprecationNoticeExitAppListener import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeClosedListener import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName import javax.inject.Inject -/** Enum class for the various deprecation notice actions available to the user. */ -enum class DeprecationNoticeActionType { - /** Action for when the user presses the 'Close' option on a deprecation dialog. */ - CLOSE, - /** Action for when the user presses the 'Dismiss' option on a deprecation dialog. */ - DISMISS, - /** Action for when the user presses the 'Update' option on a deprecation dialog. */ - UPDATE -} - /** * An activity that shows a temporary loading page until the app is fully loaded then navigates to * the profile selection screen. @@ -38,6 +30,7 @@ class SplashActivity : AppCompatActivity(), FragmentComponentFactory, DeprecationNoticeExitAppListener, + DeprecationNoticeActionListener, BetaNoticeClosedListener, GeneralAvailabilityUpgradeNoticeClosedListener { @@ -69,4 +62,7 @@ class SplashActivity : override fun onGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss: Boolean) = splashActivityPresenter.handleOnGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss) + + override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) = + splashActivityPresenter.handleOnDeprecationNoticeActionClicked(noticeActionResponse) } diff --git a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt index dd926f56612..805a1268c13 100644 --- a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt @@ -13,9 +13,15 @@ import org.oppia.android.app.model.AppStartupState import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode import org.oppia.android.app.model.AppStartupState.StartupMode import org.oppia.android.app.model.BuildFlavor +import org.oppia.android.app.model.DeprecationNoticeType +import org.oppia.android.app.model.DeprecationResponse import org.oppia.android.app.notice.AutomaticAppDeprecationNoticeDialogFragment import org.oppia.android.app.notice.BetaNoticeDialogFragment +import org.oppia.android.app.notice.DeprecationNoticeActionResponse +import org.oppia.android.app.notice.ForcedAppDeprecationNoticeDialogFragment import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragment +import org.oppia.android.app.notice.OptionalAppDeprecationNoticeDialogFragment +import org.oppia.android.app.notice.OsDeprecationNoticeDialogFragment import org.oppia.android.app.onboarding.OnboardingActivity import org.oppia.android.app.profile.ProfileChooserActivity import org.oppia.android.app.translation.AppLanguageLocaleHandler @@ -23,6 +29,7 @@ import org.oppia.android.app.utility.lifecycle.LifecycleSafeTimerFactory import org.oppia.android.databinding.SplashActivityBinding import org.oppia.android.domain.locale.LocaleController import org.oppia.android.domain.onboarding.AppStartupStateController +import org.oppia.android.domain.onboarding.DeprecationController import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.topic.PrimeTopicAssetsController import org.oppia.android.domain.translation.TranslationController @@ -31,11 +38,16 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.combineWith import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.locale.OppiaLocale +import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject private const val AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG = "auto_deprecation_notice_dialog" +private const val FORCED_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG = "forced_deprecation_notice_dialog" private const val BETA_NOTICE_DIALOG_FRAGMENT_TAG = "beta_notice_dialog" private const val GA_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG = "general_availability_update_notice_dialog" +private const val OPTIONAL_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG = "optional_update_notice_dialog" +private const val OS_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG = "os_update_notice_dialog" private const val SPLASH_INIT_STATE_DATA_PROVIDER_ID = "splash_init_state_data_provider" /** The presenter for [SplashActivity]. */ @@ -47,9 +59,12 @@ class SplashActivityPresenter @Inject constructor( private val primeTopicAssetsController: PrimeTopicAssetsController, private val translationController: TranslationController, private val localeController: LocaleController, + private val deprecationController: DeprecationController, private val appLanguageLocaleHandler: AppLanguageLocaleHandler, private val lifecycleSafeTimerFactory: LifecycleSafeTimerFactory, - private val currentBuildFlavor: BuildFlavor + private val currentBuildFlavor: BuildFlavor, + @EnableAppAndOsDeprecation + private val enableAppAndOsDeprecation: PlatformParameterValue, ) { lateinit var startupMode: StartupMode @@ -67,6 +82,19 @@ class SplashActivityPresenter @Inject constructor( subscribeToOnboardingFlow() } + fun handleOnDeprecationNoticeActionClicked( + noticeActionResponse: DeprecationNoticeActionResponse + ) { + when (noticeActionResponse) { + is DeprecationNoticeActionResponse.Close -> handleOnDeprecationNoticeCloseAppButtonClicked() + is DeprecationNoticeActionResponse.Dismiss -> handleOnDeprecationNoticeDialogDismissed( + deprecationNoticeType = noticeActionResponse.deprecationNoticeType, + deprecatedVersion = noticeActionResponse.deprecatedVersion + ) + is DeprecationNoticeActionResponse.Update -> handleOnDeprecationNoticeUpdateButtonClicked() + } + } + /** Handles cases where the user clicks the close app option on a deprecation notice dialog. */ fun handleOnDeprecationNoticeCloseAppButtonClicked() { // If the app close button is clicked for the deprecation notice, finish the activity to close @@ -74,8 +102,8 @@ class SplashActivityPresenter @Inject constructor( activity.finish() } - /** Handles cases where the user clicks the update option on a deprecation notice dialog. */ - fun handleOnDeprecationNoticeUpdateButtonClicked() { + /** Handles cases where the user clicks the update button on a deprecation notice dialog. */ + private fun handleOnDeprecationNoticeUpdateButtonClicked() { // If the Update button is clicked for the deprecation notice, launch the Play Store and open // the Oppia app's page. val packageName = activity.packageName @@ -100,7 +128,17 @@ class SplashActivityPresenter @Inject constructor( } /** Handles cases where the user dismisses the deprecation notice dialog. */ - fun handleOnDeprecationNoticeDialogDismissed() { + private fun handleOnDeprecationNoticeDialogDismissed( + deprecationNoticeType: DeprecationNoticeType, + deprecatedVersion: Int + ) { + val deprecationResponse = DeprecationResponse.newBuilder() + .setDeprecationNoticeType(deprecationNoticeType) + .setDeprecatedVersion(deprecatedVersion) + .build() + + deprecationController.saveDeprecationResponse(deprecationResponse) + // If the Dismiss button is clicked for the deprecation notice, the dialog is automatically // dismissed. Navigate to profile chooser activity. activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity)) @@ -200,6 +238,47 @@ class SplashActivityPresenter @Inject constructor( } private fun processStartupMode() { + if (enableAppAndOsDeprecation.value) { + processAppAndOsDeprecationEnabledStartUpMode() + } else { + processLegacyStartupMode() + } + } + + private fun processAppAndOsDeprecationEnabledStartUpMode() { + when (startupMode) { + StartupMode.USER_IS_ONBOARDED -> { + activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity)) + activity.finish() + } + StartupMode.APP_IS_DEPRECATED -> { + showDialog( + FORCED_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG, + ForcedAppDeprecationNoticeDialogFragment::newInstance + ) + } + StartupMode.OPTIONAL_UPDATE_AVAILABLE -> { + showDialog( + OPTIONAL_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG, + OptionalAppDeprecationNoticeDialogFragment::newInstance + ) + } + StartupMode.OS_IS_DEPRECATED -> { + showDialog( + OS_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG, + OsDeprecationNoticeDialogFragment::newInstance + ) + } + else -> { + // In all other cases (including errors when the startup state fails to load or is + // defaulted), assume the user needs to be onboarded. + activity.startActivity(OnboardingActivity.createOnboardingActivity(activity)) + activity.finish() + } + } + } + + private fun processLegacyStartupMode() { when (startupMode) { StartupMode.USER_IS_ONBOARDED -> { activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity)) diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt index 82bc4bd09d3..1ceb6cd85ee 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt @@ -46,8 +46,6 @@ import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionMo import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape -import org.oppia.android.app.utility.getLastUpdateTime -import org.oppia.android.app.utility.getVersionName import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule import org.oppia.android.domain.classify.InteractionsModule @@ -91,6 +89,8 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getLastUpdateTime +import org.oppia.android.util.extensions.getVersionName import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.extractCurrentAppScreenName diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel index d0733552b3b..8a43be43344 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel @@ -103,6 +103,7 @@ app_test( "//third_party:robolectric_android-all", "//utility/src/main/java/org/oppia/android/util/accessibility:test_module", "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", "//utility/src/main/java/org/oppia/android/util/logging:standard_event_logging_configuration_module", "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], @@ -131,6 +132,7 @@ app_test( "//third_party:robolectric_android-all", "//utility/src/main/java/org/oppia/android/util/accessibility:test_module", "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", "//utility/src/main/java/org/oppia/android/util/logging:standard_event_logging_configuration_module", "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], @@ -159,6 +161,7 @@ app_test( "//third_party:robolectric_android-all", "//utility/src/main/java/org/oppia/android/util/accessibility:test_module", "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", "//utility/src/main/java/org/oppia/android/util/logging:standard_event_logging_configuration_module", "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentTest.kt index 1af22c3a018..af3c7d48a5f 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragmentTest.kt @@ -39,7 +39,6 @@ import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.notice.testing.ForcedAppDeprecationNoticeDialogFragmentTestActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -165,7 +164,7 @@ class ForcedAppDeprecationNoticeDialogFragmentTest { clickOnDialogView(withText(R.string.forced_app_update_dialog_update_button_text)) verify(mockDeprecationNoticeActionListener) - .onActionButtonClicked(DeprecationNoticeActionType.UPDATE) + .onActionButtonClicked(DeprecationNoticeActionResponse.Update) } } @@ -183,7 +182,7 @@ class ForcedAppDeprecationNoticeDialogFragmentTest { clickOnDialogView(withText(R.string.forced_app_update_dialog_close_button_text)) verify(mockDeprecationNoticeActionListener) - .onActionButtonClicked(DeprecationNoticeActionType.CLOSE) + .onActionButtonClicked(DeprecationNoticeActionResponse.Close) } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentTest.kt index db82579e4e9..723ba24ae55 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragmentTest.kt @@ -36,10 +36,10 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.notice.testing.OptionalAppDeprecationNoticeDialogFragmentTestActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -85,6 +85,7 @@ import org.oppia.android.testing.time.FakeOppiaClockModule import org.oppia.android.util.accessibility.AccessibilityTestModule import org.oppia.android.util.caching.AssetModule import org.oppia.android.util.caching.testing.CachingTestModule +import org.oppia.android.util.extensions.getVersionCode import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.locale.LocaleProdModule import org.oppia.android.util.logging.EventLoggingConfigurationModule @@ -166,7 +167,7 @@ class OptionalAppDeprecationNoticeDialogFragmentTest { clickOnDialogView(withText(R.string.optional_app_update_dialog_update_button_text)) verify(mockDeprecationNoticeActionListener) - .onActionButtonClicked(DeprecationNoticeActionType.UPDATE) + .onActionButtonClicked(DeprecationNoticeActionResponse.Update) } } @@ -184,7 +185,12 @@ class OptionalAppDeprecationNoticeDialogFragmentTest { clickOnDialogView(withText(R.string.optional_app_update_dialog_dismiss_button_text)) verify(mockDeprecationNoticeActionListener) - .onActionButtonClicked(DeprecationNoticeActionType.DISMISS) + .onActionButtonClicked( + DeprecationNoticeActionResponse.Dismiss( + deprecationNoticeType = DeprecationNoticeType.APP_DEPRECATION, + deprecatedVersion = context.getVersionCode() + ) as DeprecationNoticeActionResponse + ) } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentTest.kt index 51afc4b45e2..16eda2a2b4b 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragmentTest.kt @@ -36,10 +36,10 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.notice.testing.OsDeprecationNoticeDialogFragmentTestActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule -import org.oppia.android.app.splash.DeprecationNoticeActionType import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule import org.oppia.android.data.backends.gae.NetworkConfigProdModule import org.oppia.android.data.backends.gae.NetworkModule @@ -167,7 +167,12 @@ class OsDeprecationNoticeDialogFragmentTest { clickOnDialogView(withText(R.string.os_deprecation_dialog_dismiss_button_text)) verify(mockDeprecationNoticeActionListener) - .onActionButtonClicked(DeprecationNoticeActionType.DISMISS) + .onActionButtonClicked( + DeprecationNoticeActionResponse.Dismiss( + deprecationNoticeType = DeprecationNoticeType.OS_DEPRECATION, + deprecatedVersion = 19, + ) + ) } } diff --git a/data/src/main/java/org/oppia/android/data/backends/gae/RemoteAuthNetworkInterceptor.kt b/data/src/main/java/org/oppia/android/data/backends/gae/RemoteAuthNetworkInterceptor.kt index c0b7269d2f1..a3839304d7d 100644 --- a/data/src/main/java/org/oppia/android/data/backends/gae/RemoteAuthNetworkInterceptor.kt +++ b/data/src/main/java/org/oppia/android/data/backends/gae/RemoteAuthNetworkInterceptor.kt @@ -4,8 +4,8 @@ import android.content.Context import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import org.oppia.android.app.utility.getVersionCode -import org.oppia.android.app.utility.getVersionName +import org.oppia.android.util.extensions.getVersionCode +import org.oppia.android.util.extensions.getVersionName import java.io.IOException import javax.inject.Inject diff --git a/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt b/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt index 31b6b203ba7..ee30f69b061 100644 --- a/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt +++ b/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt @@ -4,14 +4,18 @@ import org.oppia.android.app.model.AppStartupState import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode import org.oppia.android.app.model.AppStartupState.StartupMode import org.oppia.android.app.model.BuildFlavor +import org.oppia.android.app.model.DeprecationResponseDatabase import org.oppia.android.app.model.OnboardingState import org.oppia.android.data.persistence.PersistentCacheStore import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.util.data.DataProvider -import org.oppia.android.util.data.DataProviders.Companion.transform +import org.oppia.android.util.data.DataProviders.Companion.combineWith import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.locale.OppiaLocale +import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject +import javax.inject.Provider import javax.inject.Singleton private const val APP_STARTUP_STATE_PROVIDER_ID = "app_startup_state_data_provider_id" @@ -23,7 +27,10 @@ class AppStartupStateController @Inject constructor( private val oppiaLogger: OppiaLogger, private val expirationMetaDataRetriever: ExpirationMetaDataRetriever, private val machineLocale: OppiaLocale.MachineLocale, - private val currentBuildFlavor: BuildFlavor + private val currentBuildFlavor: BuildFlavor, + private val deprecationController: DeprecationController, + @EnableAppAndOsDeprecation + private val enableAppAndOsDeprecation: Provider>, ) { private val onboardingFlowStore by lazy { cacheStoreFactory.create("on_boarding_flow", OnboardingState.getDefaultInstance()) @@ -90,9 +97,14 @@ class AppStartupStateController @Inject constructor( fun getAppStartupState(): DataProvider = appStartupStateDataProvider private fun computeAppStartupStateProvider(): DataProvider { - return onboardingFlowStore.transform(APP_STARTUP_STATE_PROVIDER_ID) { onboardingState -> + val databaseProvider = deprecationController.getDeprecationDatabase() + + return onboardingFlowStore.combineWith( + databaseProvider, + APP_STARTUP_STATE_PROVIDER_ID + ) { onboardingState, deprecationResponseDatabase -> AppStartupState.newBuilder().apply { - startupMode = computeAppStartupMode(onboardingState) + startupMode = computeAppStartupMode(onboardingState, deprecationResponseDatabase) buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState, startupMode) }.build() } @@ -115,12 +127,21 @@ class AppStartupStateController @Inject constructor( } } - private fun computeAppStartupMode(onboardingState: OnboardingState): StartupMode { - return when { - hasAppExpired() -> StartupMode.APP_IS_DEPRECATED - onboardingState.alreadyOnboardedApp -> StartupMode.USER_IS_ONBOARDED - else -> StartupMode.USER_NOT_YET_ONBOARDED + private fun computeAppStartupMode( + onboardingState: OnboardingState, + deprecationResponseDatabase: DeprecationResponseDatabase + ): StartupMode { + // Process and return either a StartupMode.APP_IS_DEPRECATED, StartupMode.USER_IS_ONBOARDED or + // StartupMode.USER_NOT_YET_ONBOARDED if the app and OS deprecation feature flag is not enabled. + if (!enableAppAndOsDeprecation.get().value) { + return when { + hasAppExpired() -> StartupMode.APP_IS_DEPRECATED + onboardingState.alreadyOnboardedApp -> StartupMode.USER_IS_ONBOARDED + else -> StartupMode.USER_NOT_YET_ONBOARDED + } } + + return deprecationController.processStartUpMode(onboardingState, deprecationResponseDatabase) } private fun computeBuildNoticeMode( diff --git a/domain/src/main/java/org/oppia/android/domain/onboarding/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/onboarding/BUILD.bazel index ae8e6e2d0ef..bf578eb1067 100644 --- a/domain/src/main/java/org/oppia/android/domain/onboarding/BUILD.bazel +++ b/domain/src/main/java/org/oppia/android/domain/onboarding/BUILD.bazel @@ -23,6 +23,7 @@ kt_android_library( "//utility/src/main/java/org/oppia/android/util/data:data_provider", "//utility/src/main/java/org/oppia/android/util/data:data_providers", "//utility/src/main/java/org/oppia/android/util/extensions:bundle_extensions", + "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", ], ) @@ -75,6 +76,7 @@ kt_android_library( "//utility/src/main/java/org/oppia/android/util/data:data_provider", "//utility/src/main/java/org/oppia/android/util/data:data_providers", "//utility/src/main/java/org/oppia/android/util/extensions:bundle_extensions", + "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", ], ) diff --git a/domain/src/main/java/org/oppia/android/domain/onboarding/DeprecationController.kt b/domain/src/main/java/org/oppia/android/domain/onboarding/DeprecationController.kt index d798fe83b62..37afcfd0b51 100644 --- a/domain/src/main/java/org/oppia/android/domain/onboarding/DeprecationController.kt +++ b/domain/src/main/java/org/oppia/android/domain/onboarding/DeprecationController.kt @@ -1,16 +1,26 @@ package org.oppia.android.domain.onboarding +import android.content.Context +import android.os.Build import kotlinx.coroutines.Deferred +import org.oppia.android.app.model.AppStartupState.StartupMode import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.model.DeprecationResponse import org.oppia.android.app.model.DeprecationResponseDatabase +import org.oppia.android.app.model.OnboardingState import org.oppia.android.data.persistence.PersistentCacheStore import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders import org.oppia.android.util.data.DataProviders.Companion.transform +import org.oppia.android.util.extensions.getVersionCode +import org.oppia.android.util.platformparameter.ForcedAppUpdateVersionCode +import org.oppia.android.util.platformparameter.LowestSupportedApiLevel +import org.oppia.android.util.platformparameter.OptionalAppUpdateVersionCode +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject +import javax.inject.Provider import javax.inject.Singleton private const val GET_DEPRECATION_RESPONSE_PROVIDER_ID = "get_deprecation_response_provider_id" @@ -23,8 +33,15 @@ private const val ADD_DEPRECATION_RESPONSE_PROVIDER_ID = "add_deprecation_respon @Singleton class DeprecationController @Inject constructor( cacheStoreFactory: PersistentCacheStore.Factory, + private val context: Context, private val oppiaLogger: OppiaLogger, - private val dataProviders: DataProviders + private val dataProviders: DataProviders, + @OptionalAppUpdateVersionCode + private val optionalAppUpdateVersionCode: Provider>, + @ForcedAppUpdateVersionCode + private val forcedAppUpdateVersionCode: Provider>, + @LowestSupportedApiLevel + private val lowestSupportedApiLevel: Provider> ) { /** Create an instance of [PersistentCacheStore] that contains a [DeprecationResponseDatabase]. */ private val deprecationStore by lazy { @@ -114,4 +131,49 @@ class DeprecationController @Inject constructor( DeprecationResponseActionStatus.SUCCESS -> AsyncResult.Success(null) } } + + /** + * Process and return either a [StartupMode.OS_IS_DEPRECATED], [StartupMode.APP_IS_DEPRECATED], + * [StartupMode.OPTIONAL_UPDATE_AVAILABLE], [StartupMode.USER_IS_ONBOARDED] or + * [StartupMode.USER_NOT_YET_ONBOARDED] based on the values of [lowestSupportedApiLevel], + * [optionalAppUpdateVersionCode], [forcedAppUpdateVersionCode] and [onboardingState]. + */ + fun processStartUpMode( + onboardingState: OnboardingState, + deprecationDatabase: DeprecationResponseDatabase + ): StartupMode { + val previousDeprecatedAppVersion = deprecationDatabase.appDeprecationResponse.deprecatedVersion + val previousDeprecatedOsVersion = deprecationDatabase.osDeprecationResponse.deprecatedVersion + + val appVersionCode = context.getVersionCode() + val currentApiLevel = Build.VERSION.SDK_INT + val osIsDeprecated = lowestSupportedApiLevel.get().value > currentApiLevel + + val osDeprecationDialogHasNotBeenShown = + previousDeprecatedOsVersion < lowestSupportedApiLevel.get().value + + val forcedAppUpdateIsAvailable = forcedAppUpdateVersionCode.get().value > appVersionCode + val optionalAppUpdateIsAvailable = optionalAppUpdateVersionCode.get().value > appVersionCode + + val optionalAppDeprecationDialogHasNotBeenShown = + previousDeprecatedAppVersion < optionalAppUpdateVersionCode.get().value + val forcedAppDeprecationDialogHasNotBeenShown = + previousDeprecatedAppVersion < forcedAppUpdateVersionCode.get().value + + if (onboardingState.alreadyOnboardedApp) { + if (osIsDeprecated && osDeprecationDialogHasNotBeenShown) { + return StartupMode.OS_IS_DEPRECATED + } + + if (forcedAppUpdateIsAvailable && forcedAppDeprecationDialogHasNotBeenShown) { + return StartupMode.APP_IS_DEPRECATED + } + + if (optionalAppUpdateIsAvailable && optionalAppDeprecationDialogHasNotBeenShown) { + return StartupMode.OPTIONAL_UPDATE_AVAILABLE + } + + return StartupMode.USER_IS_ONBOARDED + } else return StartupMode.USER_NOT_YET_ONBOARDED + } } diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt index 9213fcc5a7f..634755addb5 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt @@ -3,7 +3,7 @@ package org.oppia.android.domain.platformparameter import android.content.Context import dagger.Module import dagger.Provides -import org.oppia.android.app.utility.getVersionCode +import org.oppia.android.util.extensions.getVersionCode import org.oppia.android.util.platformparameter.APP_AND_OS_DEPRECATION import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaModule.kt index e453cf5d94e..8f2b2769bdb 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaModule.kt @@ -3,7 +3,7 @@ package org.oppia.android.domain.platformparameter import android.content.Context import dagger.Module import dagger.Provides -import org.oppia.android.app.utility.getVersionCode +import org.oppia.android.util.extensions.getVersionCode import org.oppia.android.util.platformparameter.APP_AND_OS_DEPRECATION import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt index 6bf45843564..fdca7c4cc41 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt @@ -3,7 +3,7 @@ package org.oppia.android.domain.platformparameter import android.content.Context import dagger.Module import dagger.Provides -import org.oppia.android.app.utility.getVersionCode +import org.oppia.android.util.extensions.getVersionCode import org.oppia.android.util.platformparameter.APP_AND_OS_DEPRECATION import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorker.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorker.kt index 164f7fea914..04f1337b997 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorker.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorker.kt @@ -12,13 +12,13 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import org.oppia.android.app.model.PlatformParameter import org.oppia.android.app.model.PlatformParameter.SyncStatus -import org.oppia.android.app.utility.getVersionName import org.oppia.android.data.backends.gae.api.PlatformParameterService import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.exceptions.ExceptionsController import org.oppia.android.domain.platformparameter.PlatformParameterController import org.oppia.android.domain.util.getStringFromData import org.oppia.android.util.data.AsyncResult +import org.oppia.android.util.extensions.getVersionName import org.oppia.android.util.threading.BackgroundDispatcher import retrofit2.Response import java.lang.IllegalArgumentException diff --git a/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt index 9f924f394dd..813e82f1827 100644 --- a/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt @@ -19,14 +19,26 @@ import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.NO_NOTI import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.SHOW_BETA_NOTICE import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE import org.oppia.android.app.model.AppStartupState.StartupMode.APP_IS_DEPRECATED +import org.oppia.android.app.model.AppStartupState.StartupMode.OPTIONAL_UPDATE_AVAILABLE +import org.oppia.android.app.model.AppStartupState.StartupMode.OS_IS_DEPRECATED import org.oppia.android.app.model.AppStartupState.StartupMode.USER_IS_ONBOARDED import org.oppia.android.app.model.AppStartupState.StartupMode.USER_NOT_YET_ONBOARDED import org.oppia.android.app.model.BuildFlavor +import org.oppia.android.app.model.DeprecationNoticeType +import org.oppia.android.app.model.DeprecationResponse import org.oppia.android.app.model.OnboardingState +import org.oppia.android.app.model.PlatformParameter import org.oppia.android.data.persistence.PersistentCacheStore +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.appDeprecationResponse +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.enableAppAndOsDeprecation +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.forcedAppUpdateVersion +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.lowestApiLevel +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.optionalAppUpdateVersion +import org.oppia.android.domain.onboarding.AppStartupStateControllerTest.TestModule.Companion.osDeprecationResponse import org.oppia.android.domain.oppialogger.LogStorageModule import org.oppia.android.domain.oppialogger.LoggingIdentifierModule import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule +import org.oppia.android.domain.platformparameter.PlatformParameterController import org.oppia.android.domain.platformparameter.PlatformParameterModule import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule import org.oppia.android.testing.TestLogReportingModule @@ -49,6 +61,10 @@ import org.oppia.android.util.logging.GlobalLogLevel import org.oppia.android.util.logging.LogLevel import org.oppia.android.util.logging.SyncStatusModule import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule +import org.oppia.android.util.platformparameter.APP_AND_OS_DEPRECATION +import org.oppia.android.util.platformparameter.FORCED_APP_UPDATE_VERSION_CODE +import org.oppia.android.util.platformparameter.LOWEST_SUPPORTED_API_LEVEL +import org.oppia.android.util.platformparameter.OPTIONAL_APP_UPDATE_VERSION_CODE import org.oppia.android.util.system.OppiaClockModule import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config @@ -69,6 +85,7 @@ import javax.inject.Singleton class AppStartupStateControllerTest { @Inject lateinit var context: Context @Inject lateinit var appStartupStateController: AppStartupStateController + @Inject lateinit var platformParameterController: PlatformParameterController @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory @Parameter lateinit var initialFlavorName: String @@ -765,6 +782,112 @@ class AppStartupStateControllerTest { assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE) } + @Test + fun testController_appAndOsDeprecationEnabled_initialLaunch_startupModeIsUserNotOnboarded() { + executeInPreviousAppInstance { testComponent -> + testComponent.getPlatformParameterController().updatePlatformParameterDatabase( + listOf(enableAppAndOsDeprecation) + ) + testComponent.getTestCoroutineDispatchers().runCurrent() + } + setUpDefaultTestApplicationComponent() + + monitorFactory.ensureDataProviderExecutes(platformParameterController.getParameterDatabase()) + testCoroutineDispatchers.runCurrent() + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(USER_NOT_YET_ONBOARDED) + } + + @Test + fun testController_appAndOsDeprecationEnabled_userIsOnboarded_returnsUserOnboardedStartupMode() { + setUpTestApplicationWithAppAndOSDeprecationEnabled() + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(USER_IS_ONBOARDED) + } + + @Test + fun testController_osIsDeprecated_returnsOsDeprecatedStartupMode() { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + platformParameterToEnable = lowestApiLevel + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(OS_IS_DEPRECATED) + } + + @Test + fun testController_osIsDeprecated_previousResponseExists_returnsUserOnboardedStartupMode() { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + previousResponses = listOf(osDeprecationResponse), + platformParameterToEnable = lowestApiLevel + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(USER_IS_ONBOARDED) + } + + @Test + fun testController_optionalUpdateAvailable_returnsOptionalUpdateStartupMode() { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + platformParameterToEnable = optionalAppUpdateVersion + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(OPTIONAL_UPDATE_AVAILABLE) + } + + @Test + fun testController_optionalUpdateAvailable_previousResponseExists_returnsUserOnboardedStartupMode + () { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + previousResponses = listOf(appDeprecationResponse), + platformParameterToEnable = optionalAppUpdateVersion + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(USER_IS_ONBOARDED) + } + + @Test + fun testController_forcedUpdateAvailable_returnsAppDeprecatedStartupMode() { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + platformParameterToEnable = forcedAppUpdateVersion + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(APP_IS_DEPRECATED) + } + + @Test + fun testController_forcedUpdateAvailable_previousResponseExists_returnsUserOnboardedStartupMode + () { + setUpTestApplicationWithAppAndOSDeprecationEnabled( + previousResponses = listOf(appDeprecationResponse), + platformParameterToEnable = forcedAppUpdateVersion + ) + + val appStartupState = appStartupStateController.getAppStartupState() + + val startupMode = monitorFactory.waitForNextSuccessfulResult(appStartupState) + assertThat(startupMode.startupMode).isEqualTo(USER_IS_ONBOARDED) + } + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } @@ -776,6 +899,32 @@ class AppStartupStateControllerTest { setUpOppiaApplication(expirationEnabled = false, expDate = "9999-12-31") } + private fun setUpTestApplicationWithAppAndOSDeprecationEnabled( + previousResponses: List = emptyList(), + platformParameterToEnable: PlatformParameter? = null + ) { + executeInPreviousAppInstance { testComponent -> + testComponent.getAppStartupStateController().markOnboardingFlowCompleted() + testComponent.getTestCoroutineDispatchers().runCurrent() + + previousResponses.forEach { + testComponent.getDeprecationController().saveDeprecationResponse(it) + testComponent.getTestCoroutineDispatchers().runCurrent() + } + + testComponent.getPlatformParameterController().updatePlatformParameterDatabase( + platformParameterToEnable?.let { listOf(it, enableAppAndOsDeprecation) } + ?: listOf(enableAppAndOsDeprecation) + ) + testComponent.getTestCoroutineDispatchers().runCurrent() + } + + setUpTestApplicationComponent() + + monitorFactory.ensureDataProviderExecutes(platformParameterController.getParameterDatabase()) + testCoroutineDispatchers.runCurrent() + } + /** * Creates a separate test application component and executes the specified block. This should be * called before [setUpTestApplicationComponent] to avoid undefined behavior in production code. @@ -850,6 +999,40 @@ class AppStartupStateControllerTest { class TestModule { companion object { var buildFlavor = BuildFlavor.BUILD_FLAVOR_UNSPECIFIED + + val lowestApiLevel: PlatformParameter = PlatformParameter.newBuilder() + .setName(LOWEST_SUPPORTED_API_LEVEL) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(PlatformParameter.SyncStatus.SYNCED_FROM_SERVER) + .build() + + val optionalAppUpdateVersion: PlatformParameter = PlatformParameter.newBuilder() + .setName(OPTIONAL_APP_UPDATE_VERSION_CODE) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(PlatformParameter.SyncStatus.SYNCED_FROM_SERVER) + .build() + + val forcedAppUpdateVersion: PlatformParameter = PlatformParameter.newBuilder() + .setName(FORCED_APP_UPDATE_VERSION_CODE) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(PlatformParameter.SyncStatus.SYNCED_FROM_SERVER) + .build() + + val enableAppAndOsDeprecation: PlatformParameter = PlatformParameter.newBuilder() + .setName(APP_AND_OS_DEPRECATION) + .setBoolean(true) + .setSyncStatus(PlatformParameter.SyncStatus.SYNCED_FROM_SERVER) + .build() + + val osDeprecationResponse: DeprecationResponse = DeprecationResponse.newBuilder() + .setDeprecationNoticeType(DeprecationNoticeType.OS_DEPRECATION) + .setDeprecatedVersion(Int.MAX_VALUE) + .build() + + val appDeprecationResponse: DeprecationResponse = DeprecationResponse.newBuilder() + .setDeprecationNoticeType(DeprecationNoticeType.APP_DEPRECATION) + .setDeprecatedVersion(Int.MAX_VALUE) + .build() } @Provides @@ -907,6 +1090,10 @@ class AppStartupStateControllerTest { fun getContext(): Context + fun getPlatformParameterController(): PlatformParameterController + + fun getDeprecationController(): DeprecationController + fun inject(appStartupStateControllerTest: AppStartupStateControllerTest) } diff --git a/domain/src/test/java/org/oppia/android/domain/onboarding/DeprecationControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/onboarding/DeprecationControllerTest.kt index 3ef5714961d..ddec4a04d86 100644 --- a/domain/src/test/java/org/oppia/android/domain/onboarding/DeprecationControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/onboarding/DeprecationControllerTest.kt @@ -13,14 +13,19 @@ import dagger.Module import dagger.Provides import org.junit.Test import org.junit.runner.RunWith +import org.oppia.android.app.model.AppStartupState.StartupMode import org.oppia.android.app.model.BuildFlavor import org.oppia.android.app.model.DeprecationNoticeType import org.oppia.android.app.model.DeprecationResponse import org.oppia.android.app.model.DeprecationResponseDatabase +import org.oppia.android.app.model.OnboardingState +import org.oppia.android.app.model.PlatformParameter +import org.oppia.android.app.model.PlatformParameter.SyncStatus import org.oppia.android.data.persistence.PersistentCacheStore import org.oppia.android.domain.oppialogger.LogStorageModule import org.oppia.android.domain.oppialogger.LoggingIdentifierModule import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule +import org.oppia.android.domain.platformparameter.PlatformParameterController import org.oppia.android.domain.platformparameter.PlatformParameterModule import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule import org.oppia.android.testing.TestLogReportingModule @@ -40,10 +45,19 @@ import org.oppia.android.util.logging.GlobalLogLevel import org.oppia.android.util.logging.LogLevel import org.oppia.android.util.logging.SyncStatusModule import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule +import org.oppia.android.util.platformparameter.FORCED_APP_UPDATE_VERSION_CODE +import org.oppia.android.util.platformparameter.ForcedAppUpdateVersionCode +import org.oppia.android.util.platformparameter.LOWEST_SUPPORTED_API_LEVEL +import org.oppia.android.util.platformparameter.LowestSupportedApiLevel +import org.oppia.android.util.platformparameter.OPTIONAL_APP_UPDATE_VERSION_CODE +import org.oppia.android.util.platformparameter.OptionalAppUpdateVersionCode +import org.oppia.android.util.platformparameter.PlatformParameterSingleton +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.system.OppiaClockModule import org.robolectric.Shadows import org.robolectric.annotation.Config import javax.inject.Inject +import javax.inject.Provider import javax.inject.Singleton /** Tests for [DeprecationController]. */ @@ -57,85 +71,162 @@ class DeprecationControllerTest { @Inject lateinit var deprecationController: DeprecationController @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory + @Inject lateinit var platformParameterSingleton: PlatformParameterSingleton + @Inject lateinit var platformParameterController: PlatformParameterController - @Test - fun testController_providesInitialState_indicatesNoUpdatesReceivedFromGatingConsole() { - val defaultDeprecationResponseDatabase = DeprecationResponseDatabase - .getDefaultInstance() + @field:[Inject LowestSupportedApiLevel] + lateinit var lowestSupportedApiLevelProvider: Provider> - setUpDefaultTestApplicationComponent() + @field:[Inject OptionalAppUpdateVersionCode] + lateinit var optionalAppUpdateVersionProvider: Provider> + + @field:[Inject ForcedAppUpdateVersionCode] + lateinit var forcedAppUpdateVersionProvider: Provider> - val deprecationDataProvider = deprecationController - .getDeprecationDatabase() + @Test + fun testController_initialAppLaunch_returnsDefaultDeprecationResponseDatabase() { + setUpDefaultTestApplicationComponent() + val deprecationDataProvider = deprecationController.getDeprecationDatabase() val deprecationResponseDatabase = monitorFactory .waitForNextSuccessfulResult(deprecationDataProvider) assertThat(deprecationResponseDatabase.osDeprecationResponse) .isEqualTo(defaultDeprecationResponseDatabase.osDeprecationResponse) - assertThat(deprecationResponseDatabase.appDeprecationResponse) .isEqualTo(defaultDeprecationResponseDatabase.appDeprecationResponse) } @Test - fun testController_observedAfterSettingAppDeprecation_providesUpdatedDeprecationResponse() { + fun testController_previousResponseSaved_providesDeprecationDatabaseWithAppResponse() { executeInPreviousAppInstance { testComponent -> - val appDeprecationResponse = DeprecationResponse.newBuilder().apply { - deprecatedVersion = 5 - deprecationNoticeType = DeprecationNoticeType.APP_DEPRECATION - }.build() - testComponent.getDeprecationController().saveDeprecationResponse(appDeprecationResponse) testComponent.getTestCoroutineDispatchers().runCurrent() } - // Create the application after previous arrangement to simulate a re-creation. setUpDefaultTestApplicationComponent() - val deprecationDataProvider = deprecationController - .getDeprecationDatabase() - + val deprecationDataProvider = deprecationController.getDeprecationDatabase() val deprecationResponseDatabase = monitorFactory .waitForNextSuccessfulResult(deprecationDataProvider) assertThat(deprecationResponseDatabase.appDeprecationResponse) - .isEqualTo( - DeprecationResponse.newBuilder().apply { - deprecatedVersion = 5 - deprecationNoticeType = DeprecationNoticeType.APP_DEPRECATION - }.build() - ) + .isEqualTo(appDeprecationResponse) } @Test - fun testController_observedAfterSettingOsDeprecation_providesUpdatedDeprecationResponse() { + fun testController_previousResponseSaved_providesDeprecationDatabaseWithOsResponse() { executeInPreviousAppInstance { testComponent -> - val osDeprecationResponse = DeprecationResponse.newBuilder().apply { - deprecatedVersion = 5 - deprecationNoticeType = DeprecationNoticeType.OS_DEPRECATION - }.build() - testComponent.getDeprecationController().saveDeprecationResponse(osDeprecationResponse) testComponent.getTestCoroutineDispatchers().runCurrent() } - // Create the application after previous arrangement to simulate a re-creation. setUpDefaultTestApplicationComponent() - val deprecationDataProvider = deprecationController - .getDeprecationDatabase() - + val deprecationDataProvider = deprecationController.getDeprecationDatabase() val deprecationResponseDatabase = monitorFactory .waitForNextSuccessfulResult(deprecationDataProvider) - assertThat(deprecationResponseDatabase.osDeprecationResponse) - .isEqualTo( - DeprecationResponse.newBuilder().apply { - deprecatedVersion = 5 - deprecationNoticeType = DeprecationNoticeType.OS_DEPRECATION - }.build() - ) + assertThat(deprecationResponseDatabase.osDeprecationResponse).isEqualTo(osDeprecationResponse) + } + + @Test + fun testController_userNotOnboarded_returnsUserNotOnboardedStartUpMode() { + setUpDefaultTestApplicationComponent() + + val onboardingState = OnboardingState.newBuilder().build() + val startUpMode = deprecationController.processStartUpMode( + onboardingState, defaultDeprecationResponseDatabase + ) + + assertThat(startUpMode).isEqualTo(StartupMode.USER_NOT_YET_ONBOARDED) + } + + @Test + fun testController_userIsOnboarded_returnsUserIsOnboardedStartUpMode() { + setUpDefaultTestApplicationComponent() + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, defaultDeprecationResponseDatabase + ) + + assertThat(startupMode).isEqualTo(StartupMode.USER_IS_ONBOARDED) + } + + @Test + fun testController_osIsDeprecated_returnsOsIsDeprecatedStartUpMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(lowestApiLevel) + ) + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, defaultDeprecationResponseDatabase + ) + + assertThat(startupMode).isEqualTo(StartupMode.OS_IS_DEPRECATED) + } + + @Test + fun testController_osIsDeprecated_previousResponseExists_returnsUserIsOnboardedStartUpMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(lowestApiLevel) + ) + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, deprecationResponseDatabaseWithPreviousResponses + ) + assertThat(startupMode).isEqualTo(StartupMode.USER_IS_ONBOARDED) + } + + @Test + fun testController_hasOptionalUpdate_returnsOptionalUpdateAvailableStartupMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(optionalAppUpdateVersion) + ) + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, defaultDeprecationResponseDatabase + ) + assertThat(startupMode).isEqualTo(StartupMode.OPTIONAL_UPDATE_AVAILABLE) + } + + @Test + fun testController_hasOptionalUpdate_previousResponseExists_returnsUserIsOnboardedStartupMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(optionalAppUpdateVersion) + ) + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, deprecationResponseDatabaseWithPreviousResponses + ) + assertThat(startupMode).isEqualTo(StartupMode.USER_IS_ONBOARDED) + } + + @Test + fun testController_hasForcedUpdate_returnsAppIsDeprecatedStartupMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(forcedAppUpdateVersion) + ) + + monitorFactory.ensureDataProviderExecutes(platformParameterController.getParameterDatabase()) + testCoroutineDispatchers.runCurrent() + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, defaultDeprecationResponseDatabase + ) + assertThat(startupMode).isEqualTo(StartupMode.APP_IS_DEPRECATED) + } + + @Test + fun testController_hasForcedUpdate_previousResponseExists_returnsUserIsOnboardedStartupMode() { + setUpTestApplicationComponentWithParameters( + platformParameters = listOf(forcedAppUpdateVersion) + ) + + val startupMode = deprecationController.processStartUpMode( + alreadyOnboardedOnboardingState, deprecationResponseDatabaseWithPreviousResponses + ) + assertThat(startupMode).isEqualTo(StartupMode.USER_IS_ONBOARDED) } private fun setUpTestApplicationComponent() { @@ -146,6 +237,22 @@ class DeprecationControllerTest { setUpOppiaApplicationForContext(context, expirationEnabled, expDate) } + private fun setUpTestApplicationComponentWithParameters( + platformParameters: List + ) { + executeInPreviousAppInstance { testComponent -> + testComponent.getPlatformParameterController().updatePlatformParameterDatabase( + platformParameters + ) + testComponent.getTestCoroutineDispatchers().runCurrent() + } + + setUpDefaultTestApplicationComponent() + + monitorFactory.ensureDataProviderExecutes(platformParameterController.getParameterDatabase()) + testCoroutineDispatchers.runCurrent() + } + /** * Creates a separate test application component and executes the specified block. This should be * called before [setUpTestApplicationComponent] to avoid undefined behavior in production code. @@ -250,6 +357,12 @@ class DeprecationControllerTest { fun getDeprecationController(): DeprecationController + fun getDataProviderTestMonitor(): DataProviderTestMonitor.Factory + + fun getPlatformParameterSingleton(): PlatformParameterSingleton + + fun getPlatformParameterController(): PlatformParameterController + fun getCacheFactory(): PersistentCacheStore.Factory fun getTestCoroutineDispatchers(): TestCoroutineDispatchers @@ -276,4 +389,47 @@ class DeprecationControllerTest { override fun getDataProvidersInjector(): DataProvidersInjector = component } + + companion object { + val alreadyOnboardedOnboardingState: OnboardingState = OnboardingState.newBuilder() + .setAlreadyOnboardedApp(true) + .build() + + val lowestApiLevel: PlatformParameter = PlatformParameter.newBuilder() + .setName(LOWEST_SUPPORTED_API_LEVEL) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(SyncStatus.SYNCED_FROM_SERVER) + .build() + + val optionalAppUpdateVersion: PlatformParameter = PlatformParameter.newBuilder() + .setName(OPTIONAL_APP_UPDATE_VERSION_CODE) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(SyncStatus.SYNCED_FROM_SERVER) + .build() + + val forcedAppUpdateVersion: PlatformParameter = PlatformParameter.newBuilder() + .setName(FORCED_APP_UPDATE_VERSION_CODE) + .setInteger(Int.MAX_VALUE) + .setSyncStatus(SyncStatus.SYNCED_FROM_SERVER) + .build() + + val osDeprecationResponse: DeprecationResponse = DeprecationResponse.newBuilder() + .setDeprecationNoticeType(DeprecationNoticeType.OS_DEPRECATION) + .setDeprecatedVersion(Int.MAX_VALUE) + .build() + + val appDeprecationResponse: DeprecationResponse = DeprecationResponse.newBuilder() + .setDeprecationNoticeType(DeprecationNoticeType.APP_DEPRECATION) + .setDeprecatedVersion(Int.MAX_VALUE) + .build() + + val defaultDeprecationResponseDatabase: DeprecationResponseDatabase = + DeprecationResponseDatabase.getDefaultInstance() + + val deprecationResponseDatabaseWithPreviousResponses: DeprecationResponseDatabase = + DeprecationResponseDatabase.newBuilder() + .setOsDeprecationResponse(osDeprecationResponse) + .setAppDeprecationResponse(appDeprecationResponse) + .build() + } } diff --git a/domain/src/test/java/org/oppia/android/domain/platformparameter/PlatformParameterModuleTest.kt b/domain/src/test/java/org/oppia/android/domain/platformparameter/PlatformParameterModuleTest.kt index 1601eafc8c4..53ccda14f55 100644 --- a/domain/src/test/java/org/oppia/android/domain/platformparameter/PlatformParameterModuleTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/platformparameter/PlatformParameterModuleTest.kt @@ -14,7 +14,6 @@ import dagger.Provides import org.junit.Test import org.junit.runner.RunWith import org.oppia.android.app.model.PlatformParameter -import org.oppia.android.app.utility.getVersionCode import org.oppia.android.testing.platformparameter.TEST_BOOLEAN_PARAM_DEFAULT_VALUE import org.oppia.android.testing.platformparameter.TEST_BOOLEAN_PARAM_NAME import org.oppia.android.testing.platformparameter.TEST_BOOLEAN_PARAM_SERVER_VALUE @@ -28,6 +27,7 @@ import org.oppia.android.testing.platformparameter.TestBooleanParam import org.oppia.android.testing.platformparameter.TestIntegerParam import org.oppia.android.testing.platformparameter.TestPlatformParameterModule import org.oppia.android.testing.platformparameter.TestStringParam +import org.oppia.android.util.extensions.getVersionCode import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation import org.oppia.android.util.platformparameter.ForcedAppUpdateVersionCode import org.oppia.android.util.platformparameter.LowestSupportedApiLevel diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index 604eaf727e4..7eb105d2b97 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -4,7 +4,8 @@ import android.content.Context import androidx.annotation.VisibleForTesting import dagger.Module import dagger.Provides -import org.oppia.android.app.utility.getVersionCode +import org.oppia.android.util.extensions.getVersionCode +import org.oppia.android.util.platformparameter.APP_AND_OS_DEPRECATION import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE import org.oppia.android.util.platformparameter.CacheLatexRendering @@ -211,8 +212,11 @@ class TestPlatformParameterModule { @Provides @EnableAppAndOsDeprecation - fun provideEnableAppAndOsDeprecation(): PlatformParameterValue { - return PlatformParameterValue.createDefaultParameter(enableAppAndOsDeprecation) + fun provideEnableAppAndOsDeprecation( + platformParameterSingleton: PlatformParameterSingleton + ): PlatformParameterValue { + return platformParameterSingleton.getBooleanPlatformParameter(APP_AND_OS_DEPRECATION) + ?: PlatformParameterValue.createDefaultParameter(ENABLE_APP_AND_OS_DEPRECATION_DEFAULT_VALUE) } @Provides @@ -364,6 +368,12 @@ class TestPlatformParameterModule { enableOnboardingFlowV2 = value } + /** Enables forcing [EnableAppAndOsDeprecation] feature flag from tests. */ + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + fun forceEnableAppAndOsDeprecation(value: Boolean) { + enableAppAndOsDeprecation = value + } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun reset() { enableDownloadsSupport = ENABLE_DOWNLOADS_SUPPORT_DEFAULT_VALUE diff --git a/utility/src/main/java/org/oppia/android/util/extensions/ContextExtensions.kt b/utility/src/main/java/org/oppia/android/util/extensions/ContextExtensions.kt index 0aafa694bbf..065ba37f100 100644 --- a/utility/src/main/java/org/oppia/android/util/extensions/ContextExtensions.kt +++ b/utility/src/main/java/org/oppia/android/util/extensions/ContextExtensions.kt @@ -1,4 +1,4 @@ -package org.oppia.android.app.utility +package org.oppia.android.util.extensions import android.content.Context diff --git a/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt b/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt index 1684878eb70..9a31131f91c 100644 --- a/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt +++ b/utility/src/main/java/org/oppia/android/util/logging/EventBundleCreator.kt @@ -57,8 +57,8 @@ import org.oppia.android.app.model.OppiaMetricLog.LoggableMetric.LoggableMetricT import org.oppia.android.app.model.OppiaMetricLog.LoggableMetric.LoggableMetricTypeCase.STORAGE_USAGE_METRIC import org.oppia.android.app.model.ScreenName import org.oppia.android.app.model.WrittenTranslationLanguageSelection -import org.oppia.android.app.utility.getVersionCode -import org.oppia.android.app.utility.getVersionName +import org.oppia.android.util.extensions.getVersionCode +import org.oppia.android.util.extensions.getVersionName import org.oppia.android.util.logging.EventBundleCreator.EventActivityContext.AbandonSurveyContext import org.oppia.android.util.logging.EventBundleCreator.EventActivityContext.CardContext import org.oppia.android.util.logging.EventBundleCreator.EventActivityContext.ConceptCardContext