commit 5e8844a9dd264a537994ed9a0105f917b03b4f56
parent bd0770a10424c19480d1a453af4d3efd6b04e56d
Author: t-p-white <towhite@mozilla.com>
Date: Fri, 14 Nov 2025 10:06:10 +0000
Bug 1993949 - Refactor the applicationInstalledTime var in Settings used for testing, to use the PackageManagerCompatHelper r=android-reviewers,gmalekpour
Differential Revision: https://phabricator.services.mozilla.com/D272476
Diffstat:
6 files changed, 165 insertions(+), 35 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/Context.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/Context.kt
@@ -7,7 +7,6 @@ package org.mozilla.fenix.ext
import android.app.Activity
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.provider.Settings
@@ -20,7 +19,6 @@ import androidx.annotation.StringRes
import mozilla.components.compose.base.theme.layout.AcornWindowSize
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.locale.LocaleManager
-import mozilla.components.support.utils.ext.packageManagerCompatHelper
import org.mozilla.fenix.FenixApplication
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
@@ -217,19 +215,3 @@ fun Context.isToolbarAtBottom() =
* @return The pixel size corresponding to the given dimension resource.
*/
fun Context.pixelSizeFor(@DimenRes resId: Int) = resources.getDimensionPixelSize(resId)
-
-/**
- * Returns the installation time of this application (in milliseconds).
- *
- * @param logger Used to log a warning if package information cannot be retrieved.
- * @return The installation time in milliseconds since epoch, or `0L` if unavailable.
- */
-fun Context.getApplicationInstalledTime(logger: Logger): Long = try {
- packageManagerCompatHelper.getPackageInfoCompat(packageName, 0).firstInstallTime
-} catch (e: PackageManager.NameNotFoundException) {
- logger.warn("Unable to retrieve package info for $packageName", e)
- 0L
-} catch (e: UnsupportedOperationException) {
- logger.warn("Unable to retrieve package info for $packageName", e)
- 0L
-}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/termsofuse/GetApplicationInstalledTime.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/termsofuse/GetApplicationInstalledTime.kt
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.termsofuse
+
+import android.content.pm.PackageManager
+import mozilla.components.support.base.log.logger.Logger
+import mozilla.components.support.utils.ext.PackageManagerCompatHelper
+
+/**
+ * Returns the installation time of this application (in milliseconds).
+ *
+ * @return The installation time in milliseconds since epoch, or `0L` if unavailable.
+ */
+fun getApplicationInstalledTime(
+ packageManagerCompatHelper: PackageManagerCompatHelper,
+ packageName: String,
+ logger: Logger,
+): Long = try {
+ packageManagerCompatHelper.getPackageInfoCompat(packageName, 0).firstInstallTime
+} catch (e: PackageManager.NameNotFoundException) {
+ logger.warn("Unable to retrieve package info for $packageName", e)
+ 0L
+} catch (e: UnsupportedOperationException) {
+ logger.warn("Unable to retrieve package info for $packageName", e)
+ 0L
+}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
@@ -34,6 +34,8 @@ import mozilla.components.support.ktx.android.content.stringPreference
import mozilla.components.support.ktx.android.content.stringSetPreference
import mozilla.components.support.locale.LocaleManager
import mozilla.components.support.utils.BrowsersCache
+import mozilla.components.support.utils.ext.PackageManagerCompatHelper
+import mozilla.components.support.utils.ext.packageManagerCompatHelper
import org.mozilla.experiments.nimbus.NimbusEventStore
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.Config
@@ -51,7 +53,6 @@ import org.mozilla.fenix.debugsettings.addresses.SharedPrefsAddressesDebugRegion
import org.mozilla.fenix.ext.TALL_SCREEN_HEIGHT_DP
import org.mozilla.fenix.ext.WIDE_SCREEN_WIDTH_DP
import org.mozilla.fenix.ext.components
-import org.mozilla.fenix.ext.getApplicationInstalledTime
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.pixelSizeFor
import org.mozilla.fenix.home.pocket.ContentRecommendationsFeatureHelper
@@ -74,6 +75,7 @@ import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_AUDIBLE
import org.mozilla.fenix.tabstray.DefaultTabManagementFeatureHelper
import org.mozilla.fenix.termsofuse.TOU_VERSION
+import org.mozilla.fenix.termsofuse.getApplicationInstalledTime
import org.mozilla.fenix.wallpapers.Wallpaper
import java.security.InvalidParameterException
import java.util.UUID
@@ -85,10 +87,15 @@ private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING"
* A simple wrapper for SharedPreferences that makes reading preference a little bit easier.
*
* @param appContext Reference to application context.
+ * @param packageName Package name of the application.
+ * @param packageManagerCompatHelper Helper for accessing [android.content.pm.PackageManager] methods.
*/
@Suppress("LargeClass", "TooManyFunctions")
-class Settings(private val appContext: Context) : PreferencesHolder {
-
+class Settings(
+ private val appContext: Context,
+ private val packageName: String = appContext.packageName,
+ private val packageManagerCompatHelper: PackageManagerCompatHelper = appContext.packageManagerCompatHelper,
+) : PreferencesHolder {
companion object {
const val FENIX_PREFERENCES = "fenix_preferences"
@@ -579,21 +586,20 @@ class Settings(private val appContext: Context) : PreferencesHolder {
*/
var termsOfUseAcceptedTimeInMillis by longPreference(
key = appContext.getPreferenceKey(R.string.pref_key_terms_accepted_date),
- default = { if (hasAcceptedTermsOfService) applicationInstalledTime else 0L },
+ default = {
+ if (hasAcceptedTermsOfService) {
+ getApplicationInstalledTime(
+ packageManagerCompatHelper = packageManagerCompatHelper,
+ packageName = packageName,
+ logger = logger,
+ )
+ } else {
+ 0L
+ }
+ },
)
/**
- * Temporary testing helper to set the date the user accepted the Terms of Use.
- *
- * Will be addressed in a more permanent refactor as part of
- * https://bugzilla.mozilla.org/show_bug.cgi?id=1993949.
- *
- * ⚠️ Only mutate from tests.
- */
- @VisibleForTesting
- internal var applicationInstalledTime = appContext.getApplicationInstalledTime(logger)
-
- /**
* The version of the Terms of Use that the user has accepted.
*/
var termsOfUseAcceptedVersion by intPreference(
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/termsofuse/GetApplicationInstalledTimeTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/termsofuse/GetApplicationInstalledTimeTest.kt
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.termsofuse
+
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import mozilla.components.support.base.log.logger.Logger
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.mozilla.fenix.utils.FakePackageManagerCompatHelper
+
+class GetApplicationInstalledTimeTest {
+
+ @Test
+ fun `WHEN getPackageInfoCompat returns a time THEN getApplicationInstalledTime returns the same time`() {
+ val installTime = 12345L
+
+ val result = getApplicationInstalledTime(
+ packageManagerCompatHelper = FakePackageManagerCompatHelper(
+ packageInfo = PackageInfo().apply {
+ firstInstallTime = installTime
+ },
+ ),
+ packageName = "UNUSED",
+ logger = Logger(),
+ )
+
+ assertEquals(installTime, result)
+ }
+
+ @Test
+ fun `WHEN getPackageInfoCompat throws NameNotFoundException THEN getApplicationInstalledTime returns 0`() {
+ val installTime = 12345L
+
+ val result = getApplicationInstalledTime(
+ packageManagerCompatHelper = FakePackageManagerCompatHelper(
+ packageInfo = PackageInfo().apply {
+ firstInstallTime = installTime
+ },
+ getPackageInfoThrowable = PackageManager.NameNotFoundException(),
+ ),
+ packageName = "UNUSED",
+ logger = Logger(),
+ )
+
+ assertEquals(0, result)
+ }
+
+ @Test
+ fun `WHEN getPackageInfoCompat throws UnsupportedOperationException THEN getApplicationInstalledTime returns 0`() {
+ val installTime = 12345L
+
+ val result = getApplicationInstalledTime(
+ packageManagerCompatHelper = FakePackageManagerCompatHelper(
+ packageInfo = PackageInfo().apply {
+ firstInstallTime = installTime
+ },
+ getPackageInfoThrowable = UnsupportedOperationException(),
+ ),
+ packageName = "UNUSED",
+ logger = Logger(),
+ )
+
+ assertEquals(0, result)
+ }
+}
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/utils/FakePackageManagerCompatHelper.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/utils/FakePackageManagerCompatHelper.kt
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.utils
+
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.ResolveInfo
+import mozilla.components.support.utils.ext.PackageManagerCompatHelper
+import java.util.Collections.emptyList
+
+class FakePackageManagerCompatHelper(
+ val packageInfo: PackageInfo = PackageInfo(),
+ val getPackageInfoThrowable: Throwable? = null,
+ val applicationInfo: ApplicationInfo = ApplicationInfo(),
+) : PackageManagerCompatHelper {
+ override fun queryIntentActivitiesCompat(intent: Intent, flag: Int): MutableList<ResolveInfo> =
+ emptyList()
+
+ override fun resolveActivityCompat(intent: Intent, flag: Int): ResolveInfo? = null
+
+ override fun getPackageInfoCompat(packageName: String, flag: Int): PackageInfo {
+ getPackageInfoThrowable?.let { throw it }
+
+ return packageInfo
+ }
+
+ override fun getApplicationInfoCompat(packageName: String, flag: Int) = applicationInfo
+}
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt
@@ -4,6 +4,7 @@
package org.mozilla.fenix.utils
+import android.content.pm.PackageInfo
import androidx.core.content.edit
import io.mockk.every
import io.mockk.spyk
@@ -1273,20 +1274,34 @@ class SettingsTest {
@Test
fun `WHEN user has accepted the ToU THEN termsOfUseAcceptedTimeInMillis returns the app installed time`() {
val installTime = 12345L
- settings.applicationInstalledTime = installTime
+ val settings = Settings(
+ appContext = testContext,
+ packageName = "test",
+ packageManagerCompatHelper = FakePackageManagerCompatHelper(
+ packageInfo = PackageInfo().apply { firstInstallTime = installTime },
+ ),
+ )
settings.hasAcceptedTermsOfService = true
val result = settings.termsOfUseAcceptedTimeInMillis
+
assertEquals(installTime, result)
}
@Test
fun `WHEN user has not accepted the ToU THEN termsOfUseAcceptedTimeInMillis returns 0L`() {
val installTime = 12345L
- settings.applicationInstalledTime = installTime
+ val settings = Settings(
+ appContext = testContext,
+ packageName = "test",
+ packageManagerCompatHelper = FakePackageManagerCompatHelper(
+ packageInfo = PackageInfo().apply { firstInstallTime = installTime },
+ ),
+ )
settings.hasAcceptedTermsOfService = false
val result = settings.termsOfUseAcceptedTimeInMillis
+
assertEquals(0L, result)
}