commit ff26f7f669f92fe543ca9e1ebf4c1955648d4156 parent c29a4b75c2a9b6bd101a7ee16ea7a1e618f70656 Author: t-p-white <towhite@mozilla.com> Date: Wed, 12 Nov 2025 13:18:25 +0000 Bug 1993968 - Refactor the PackagerManager in mozilla.components.support.utils.ext to implement an interface for simpler testing r=android-reviewers,pollymce Differential Revision: https://phabricator.services.mozilla.com/D270475 Diffstat:
38 files changed, 394 insertions(+), 160 deletions(-)
diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt @@ -20,8 +20,7 @@ import mozilla.components.support.ktx.android.content.pm.isPackageInstalled import mozilla.components.support.ktx.android.net.isHttpOrHttps import mozilla.components.support.utils.Browsers import mozilla.components.support.utils.BrowsersCache -import mozilla.components.support.utils.ext.queryIntentActivitiesCompat -import mozilla.components.support.utils.ext.resolveActivityCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import java.net.URISyntaxException @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @@ -63,7 +62,7 @@ class AppLinksUseCases( @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal fun findActivities(intent: Intent): List<ResolveInfo> { return try { - context.packageManager + context.packageManagerCompatHelper .queryIntentActivitiesCompat(intent, PackageManager.GET_RESOLVED_FILTER) } catch (e: RuntimeException) { Logger("AppLinksUseCases").error("failed to query activities", e) @@ -72,7 +71,10 @@ class AppLinksUseCases( } private fun findDefaultActivity(intent: Intent): ResolveInfo? { - return context.packageManager.resolveActivityCompat(intent, PackageManager.MATCH_DEFAULT_ONLY) + return context.packageManagerCompatHelper.resolveActivityCompat( + intent, + PackageManager.MATCH_DEFAULT_ONLY, + ) } /** @@ -143,7 +145,7 @@ class AppLinksUseCases( val marketplaceIntent = intent?.`package`?.let { if (includeInstallAppFallback && - !context.packageManager.isPackageInstalled(it) + !context.packageManagerCompatHelper.isPackageInstalled(it) ) { safeParseUri(MARKET_INTENT_URI_PACKAGE_PREFIX + it, 0) } else { diff --git a/mobile/android/android-components/components/feature/autofill/src/main/java/mozilla/components/feature/autofill/verify/CredentialAccessVerifier.kt b/mobile/android/android-components/components/feature/autofill/src/main/java/mozilla/components/feature/autofill/verify/CredentialAccessVerifier.kt @@ -9,6 +9,7 @@ import mozilla.components.service.digitalassetlinks.AndroidAssetFinder import mozilla.components.service.digitalassetlinks.AssetDescriptor import mozilla.components.service.digitalassetlinks.Relation import mozilla.components.service.digitalassetlinks.local.StatementRelationChecker +import mozilla.components.support.utils.ext.packageManagerCompatHelper /** * Helper to verify that a specific application is allowed to receive get the login credentials for @@ -34,7 +35,9 @@ class CredentialAccessVerifier( domain: String, packageName: String, ): Boolean { - val assets = assetsFinder.getAndroidAppAsset(packageName, context.packageManager).toList() + val assets = + assetsFinder.getAndroidAppAsset(packageName, context.packageManagerCompatHelper) + .toList() // I was expecting us to need to verify all signatures here. But If I understand the usage // in `OriginVerifier` and the spec (see link in class comment) correctly then verifying one diff --git a/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/AbstractCustomTabsService.kt b/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/AbstractCustomTabsService.kt @@ -23,6 +23,7 @@ import mozilla.components.feature.customtabs.store.SaveCreatorPackageNameAction import mozilla.components.service.digitalassetlinks.RelationChecker import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.utils.ext.getParcelableCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper /** * Maximum number of speculative connections we will open when an app calls into @@ -44,7 +45,10 @@ abstract class AbstractCustomTabsService : CustomTabsService() { @VisibleForTesting internal val verifier by lazy { relationChecker?.let { checker -> - OriginVerifierFeature(packageManager, checker) { customTabsServiceStore.dispatch(it) } + OriginVerifierFeature( + packageManagerCompatHelper, + checker, + ) { customTabsServiceStore.dispatch(it) } } } diff --git a/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/feature/OriginVerifierFeature.kt b/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/feature/OriginVerifierFeature.kt @@ -4,7 +4,6 @@ package mozilla.components.feature.customtabs.feature -import android.content.pm.PackageManager import android.net.Uri import androidx.annotation.VisibleForTesting import androidx.browser.customtabs.CustomTabsService.Relation @@ -18,9 +17,10 @@ import mozilla.components.feature.customtabs.store.VerificationStatus.PENDING import mozilla.components.feature.customtabs.store.VerificationStatus.SUCCESS import mozilla.components.feature.customtabs.verify.OriginVerifier import mozilla.components.service.digitalassetlinks.RelationChecker +import mozilla.components.support.utils.ext.PackageManagerCompatHelper class OriginVerifierFeature( - private val packageManager: PackageManager, + private val packageManager: PackageManagerCompatHelper, private val relationChecker: RelationChecker, private val dispatch: (CustomTabsAction) -> Unit, ) { diff --git a/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/verify/OriginVerifier.kt b/mobile/android/android-components/components/feature/customtabs/src/main/java/mozilla/components/feature/customtabs/verify/OriginVerifier.kt @@ -4,7 +4,6 @@ package mozilla.components.feature.customtabs.verify -import android.content.pm.PackageManager import android.net.Uri import androidx.annotation.VisibleForTesting import androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS @@ -18,6 +17,7 @@ import mozilla.components.service.digitalassetlinks.AssetDescriptor import mozilla.components.service.digitalassetlinks.Relation.HANDLE_ALL_URLS import mozilla.components.service.digitalassetlinks.Relation.USE_AS_ORIGIN import mozilla.components.service.digitalassetlinks.RelationChecker +import mozilla.components.support.utils.ext.PackageManagerCompatHelper /** * Used to verify postMessage origin for a designated package name. @@ -29,7 +29,7 @@ import mozilla.components.service.digitalassetlinks.RelationChecker class OriginVerifier( private val packageName: String, @param:Relation private val relation: Int, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, private val relationChecker: RelationChecker, ) { diff --git a/mobile/android/android-components/components/feature/customtabs/src/test/java/mozilla/components/feature/customtabs/verify/OriginVerifierTest.kt b/mobile/android/android-components/components/feature/customtabs/src/test/java/mozilla/components/feature/customtabs/verify/OriginVerifierTest.kt @@ -4,7 +4,6 @@ package mozilla.components.feature.customtabs.verify -import android.content.pm.PackageManager import androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS import androidx.browser.customtabs.CustomTabsService.RELATION_USE_AS_ORIGIN import androidx.core.net.toUri @@ -14,6 +13,7 @@ import mozilla.components.concept.fetch.Response import mozilla.components.service.digitalassetlinks.AssetDescriptor import mozilla.components.service.digitalassetlinks.Relation import mozilla.components.service.digitalassetlinks.RelationChecker +import mozilla.components.support.utils.ext.PackageManagerCompatHelper import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -32,7 +32,7 @@ class OriginVerifierTest { sha256CertFingerprint = "AA:BB:CC:10:20:30:01:02", ) - @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var packageManager: PackageManagerCompatHelper @Mock private lateinit var response: Response diff --git a/mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt b/mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt @@ -43,6 +43,7 @@ import mozilla.components.support.ktx.android.content.appName import mozilla.components.support.ktx.android.content.isPermissionGranted import mozilla.components.support.ktx.kotlin.isSameOriginAs import mozilla.components.support.utils.Browsers +import mozilla.components.support.utils.ext.packageManagerCompatHelper /** * The name of the file to be downloaded. @@ -492,7 +493,7 @@ class DownloadsFeature( */ @VisibleForTesting internal fun getDownloaderApps(context: Context, download: DownloadState): List<DownloaderApp> { - val packageManager = context.packageManager + val packageManager = context.packageManagerCompatHelper val browsers = Browsers.findResolvers(context, packageManager, includeThisApp = true) .associateBy { it.activityInfo.identifier } diff --git a/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/intent/TrustedWebActivityIntentProcessor.kt b/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/intent/TrustedWebActivityIntentProcessor.kt @@ -6,7 +6,6 @@ package mozilla.components.feature.pwa.intent import android.content.Intent import android.content.Intent.ACTION_VIEW -import android.content.pm.PackageManager import android.net.Uri import androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS import androidx.browser.customtabs.CustomTabsSessionToken @@ -28,6 +27,7 @@ import mozilla.components.feature.pwa.ext.toOrigin import mozilla.components.feature.tabs.CustomTabsUseCases import mozilla.components.service.digitalassetlinks.RelationChecker import mozilla.components.support.utils.SafeIntent +import mozilla.components.support.utils.ext.PackageManagerCompatHelper import mozilla.components.support.utils.toSafeIntent /** @@ -36,7 +36,7 @@ import mozilla.components.support.utils.toSafeIntent @Deprecated("TWAs are not supported. See https://github.com/mozilla-mobile/android-components/issues/12024") class TrustedWebActivityIntentProcessor( private val addNewTabUseCase: CustomTabsUseCases.AddCustomTabUseCase, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, relationChecker: RelationChecker, private val store: CustomTabsServiceStore, ) : IntentProcessor { diff --git a/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt b/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/MozillaSocorroService.kt @@ -18,7 +18,7 @@ import mozilla.components.lib.crash.service.CrashReport.Annotation import mozilla.components.support.base.ext.getStacktraceAsJsonString import mozilla.components.support.base.ext.getStacktraceAsString import mozilla.components.support.base.log.logger.Logger -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.json.JSONArray import org.json.JSONException import org.json.JSONObject @@ -109,7 +109,10 @@ class MozillaSocorroService( init { val packageInfo = try { - applicationContext.packageManager.getPackageInfoCompat(applicationContext.packageName, 0) + applicationContext.packageManagerCompatHelper.getPackageInfoCompat( + applicationContext.packageName, + 0, + ) } catch (e: PackageManager.NameNotFoundException) { logger.error("package name not found, failed to get application version") null @@ -462,7 +465,7 @@ class MozillaSocorroService( } fun sendPackageInstallTime(applicationContext: Context) { - val packageManager = applicationContext.packageManager + val packageManager = applicationContext.packageManagerCompatHelper try { val packageInfo = packageManager.getPackageInfoCompat(applicationContext.packageName, 0) sendAnnotation( diff --git a/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/socorro/MozillaSocorroService.kt b/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/service/socorro/MozillaSocorroService.kt @@ -18,7 +18,7 @@ import mozilla.components.lib.crash.service.LIB_CRASH_INFO_PREFIX import mozilla.components.support.base.ext.getStacktraceAsJsonString import mozilla.components.support.base.ext.getStacktraceAsString import mozilla.components.support.base.log.logger.Logger -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.json.JSONException import org.json.JSONObject import java.io.BufferedReader @@ -380,7 +380,7 @@ class MozillaSocorroService( } fun sendPackageInstallTime(applicationContext: Context) { - val packageManager = applicationContext.packageManager + val packageManager = applicationContext.packageManagerCompatHelper try { val packageInfo = packageManager.getPackageInfoCompat(applicationContext.packageName, 0) sendAnnotation( diff --git a/mobile/android/android-components/components/service/digitalassetlinks/src/main/java/mozilla/components/service/digitalassetlinks/AndroidAssetFinder.kt b/mobile/android/android-components/components/service/digitalassetlinks/src/main/java/mozilla/components/service/digitalassetlinks/AndroidAssetFinder.kt @@ -11,7 +11,7 @@ import android.content.pm.Signature import android.os.Build import android.os.Build.VERSION.SDK_INT import androidx.annotation.VisibleForTesting -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.PackageManagerCompatHelper import java.io.ByteArrayInputStream import java.security.MessageDigest import java.security.NoSuchAlgorithmException @@ -34,7 +34,7 @@ class AndroidAssetFinder { */ fun getAndroidAppAsset( packageName: String, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, ): Sequence<AssetDescriptor.Android> { return packageManager.getSignatures(packageName).asSequence() .mapNotNull { signature -> getCertificateSHA256Fingerprint(signature) } @@ -65,7 +65,7 @@ class AndroidAssetFinder { @Suppress("PackageManagerGetSignatures") // https://stackoverflow.com/questions/39192844/android-studio-warning-when-using-packagemanager-get-signatures - private fun PackageManager.getSignatures(packageName: String): Array<Signature> { + private fun PackageManagerCompatHelper.getSignatures(packageName: String): Array<Signature> { val packageInfo = getPackageSignatureInfo(packageName) ?: return emptyArray() val signatures = if (SDK_INT >= Build.VERSION_CODES.P) { @@ -89,13 +89,13 @@ class AndroidAssetFinder { } @SuppressLint("PackageManagerGetSignatures") - private fun PackageManager.getPackageSignatureInfo(packageName: String): PackageInfo? { + private fun PackageManagerCompatHelper.getPackageSignatureInfo(packageName: String): PackageInfo? { return try { if (SDK_INT >= Build.VERSION_CODES.P) { getPackageInfoCompat(packageName, PackageManager.GET_SIGNING_CERTIFICATES) } else { @Suppress("Deprecation") - getPackageInfo(packageName, PackageManager.GET_SIGNATURES) + getPackageInfoCompat(packageName, PackageManager.GET_SIGNATURES) } } catch (e: PackageManager.NameNotFoundException) { // Will return null if there is no package found. diff --git a/mobile/android/android-components/components/service/digitalassetlinks/src/test/java/mozilla/components/service/digitalassetlinks/AndroidAssetFinderTest.kt b/mobile/android/android-components/components/service/digitalassetlinks/src/test/java/mozilla/components/service/digitalassetlinks/AndroidAssetFinderTest.kt @@ -10,19 +10,17 @@ import android.content.pm.Signature import android.content.pm.SigningInfo import android.os.Build import androidx.test.ext.junit.runners.AndroidJUnit4 -import mozilla.components.support.test.any import mozilla.components.support.test.mock +import mozilla.components.support.utils.ext.PackageManagerCompatHelper import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.doReturn import org.mockito.Mockito.spy -import org.mockito.Mockito.times import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.robolectric.annotation.Config @@ -33,7 +31,7 @@ class AndroidAssetFinderTest { private lateinit var assetFinder: AndroidAssetFinder private lateinit var packageInfo: PackageInfo - @Mock lateinit var packageManager: PackageManager + @Mock lateinit var packageManager: PackageManagerCompatHelper @Mock lateinit var signingInfo: SigningInfo @@ -44,14 +42,14 @@ class AndroidAssetFinderTest { MockitoAnnotations.openMocks(this) packageInfo = PackageInfo() @Suppress("DEPRECATION") - `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) + `when`(packageManager.getPackageInfoCompat(anyString(), anyInt())).thenReturn(packageInfo) } @Test @Config(sdk = [28]) fun `test getAndroidAppAsset returns empty list if name not found on SDK 28 or less`() { @Suppress("DEPRECATION") - `when`(packageManager.getPackageInfo(anyString(), anyInt())) + `when`(packageManager.getPackageInfoCompat(anyString(), anyInt())) .thenThrow(PackageManager.NameNotFoundException::class.java) assertEquals( @@ -63,9 +61,9 @@ class AndroidAssetFinderTest { @Test fun `test getAndroidAppAsset returns empty list if name not found`() { `when`( - packageManager.getPackageInfo( + packageManager.getPackageInfoCompat( anyString(), - ArgumentMatchers.any(PackageManager.PackageInfoFlags::class.java), + anyInt(), ), ) .thenThrow(PackageManager.NameNotFoundException::class.java) diff --git a/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/Context.kt b/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/Context.kt @@ -47,7 +47,7 @@ import mozilla.components.support.base.log.Log import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.ktx.R import mozilla.components.support.ktx.android.content.res.resolveAttribute -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import java.io.File /** @@ -55,7 +55,7 @@ import java.io.File * attribute. E.g. "2.0". Returns an empty string if versionName is null. */ val Context.appVersionName: String - get() = packageManager.getPackageInfoCompat(packageName, 0).versionName ?: "" + get() = packageManagerCompatHelper.getPackageInfoCompat(packageName, 0).versionName ?: "" /** * Returns the name (label) of the application or the package name as a fallback. diff --git a/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/Intent.kt b/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/Intent.kt @@ -7,7 +7,7 @@ package mozilla.components.support.ktx.android.content import android.content.ComponentName import android.content.Context import android.content.Intent -import mozilla.components.support.utils.ext.queryIntentActivitiesCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper /** * Modify the current intent to be used in an intent chooser excluding the current app. @@ -22,7 +22,8 @@ fun Intent.createChooserExcludingCurrentApp( title: CharSequence, ): Intent { val chooserIntent: Intent - val resolveInfos = context.packageManager.queryIntentActivitiesCompat(this, 0).toHashSet() + val resolveInfos = + context.packageManagerCompatHelper.queryIntentActivitiesCompat(this, 0).toHashSet() val excludedComponentNames = resolveInfos .map { it.activityInfo } diff --git a/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/pm/PackageManager.kt b/mobile/android/android-components/components/support/ktx/src/main/java/mozilla/components/support/ktx/android/content/pm/PackageManager.kt @@ -5,14 +5,14 @@ package mozilla.components.support.ktx.android.content.pm import android.content.pm.PackageManager -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.PackageManagerCompatHelper /** * Check if a package is installed * * @param packageName The name of the package to check for. */ -fun PackageManager.isPackageInstalled(packageName: String): Boolean { +fun PackageManagerCompatHelper.isPackageInstalled(packageName: String): Boolean { return try { // Turn off all the flags since we don't need the return value getPackageInfoCompat(packageName, 0) diff --git a/mobile/android/android-components/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/pm/PackageManagerTest.kt b/mobile/android/android-components/components/support/ktx/src/test/java/mozilla/components/support/ktx/android/content/pm/PackageManagerTest.kt @@ -9,6 +9,7 @@ import android.content.pm.PackageInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.junit.Assert.assertFalse import org.junit.Test import org.junit.runner.RunWith @@ -41,9 +42,9 @@ class PackageManagerTest { fun `isPackageInstalled() returns true when package is installed, false otherwise`() { val context = createContext(listOf("com.example", "com.test")) - assert(context.packageManager.isPackageInstalled("com.example")) - assert(context.packageManager.isPackageInstalled("com.test")) - assertFalse(context.packageManager.isPackageInstalled("com.mozilla")) - assertFalse(context.packageManager.isPackageInstalled("com.example.com")) + assert(context.packageManagerCompatHelper.isPackageInstalled("com.example")) + assert(context.packageManagerCompatHelper.isPackageInstalled("com.test")) + assertFalse(context.packageManagerCompatHelper.isPackageInstalled("com.mozilla")) + assertFalse(context.packageManagerCompatHelper.isPackageInstalled("com.example.com")) } } diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/Browsers.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/Browsers.kt @@ -13,9 +13,8 @@ import android.content.pm.ResolveInfo import android.net.Uri import androidx.annotation.VisibleForTesting import androidx.core.net.toUri -import mozilla.components.support.utils.ext.getPackageInfoCompat -import mozilla.components.support.utils.ext.queryIntentActivitiesCompat -import mozilla.components.support.utils.ext.resolveActivityCompat +import mozilla.components.support.utils.ext.PackageManagerCompatHelper +import mozilla.components.support.utils.ext.packageManagerCompatHelper /** * Helpful tools for dealing with other browsers on this device. @@ -97,7 +96,7 @@ class Browsers private constructor( private val packageName = context.packageName private val browsers: Map<String, ActivityInfo> = { - val packageManager = context.packageManager + val packageManager = context.packageManagerCompatHelper val browsers = resolveBrowsers(context, packageManager, uri) @@ -113,7 +112,8 @@ class Browsers private constructor( /** * The [ActivityInfo] of the default browser of the user (or null if none could be found). */ - val defaultBrowser: ActivityInfo? = findDefault(context, context.packageManager, uri) + val defaultBrowser: ActivityInfo? = + findDefault(context, context.packageManagerCompatHelper, uri) /** * The [ActivityInfo] of the installed Firefox, including Focus, browser (or null if none could be found). @@ -259,7 +259,7 @@ class Browsers private constructor( private fun resolveBrowsers( context: Context, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, uri: Uri, ): MutableMap<String, ActivityInfo> { val browsers = HashMap<String, ActivityInfo>() @@ -273,7 +273,7 @@ class Browsers private constructor( } private fun findKnownBrowsers( - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, browsers: MutableMap<String, ActivityInfo>, uri: Uri, ) { @@ -308,7 +308,11 @@ class Browsers private constructor( } } - private fun findDefault(context: Context, packageManager: PackageManager, uri: Uri): ActivityInfo? { + private fun findDefault( + context: Context, + packageManager: PackageManagerCompatHelper, + uri: Uri, + ): ActivityInfo? { val intent = Intent(Intent.ACTION_VIEW, uri) intent.addCategory(Intent.CATEGORY_BROWSABLE) @@ -365,7 +369,7 @@ class Browsers private constructor( */ fun findResolvers( context: Context, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, includeThisApp: Boolean = true, ): List<ResolveInfo> { val httpIntent = Intent.parseUri(SAMPLE_BROWSER_HTTP_URL, Intent.URI_INTENT_SCHEME) @@ -396,7 +400,7 @@ class Browsers private constructor( */ fun findResolvers( context: Context, - packageManager: PackageManager, + packageManager: PackageManagerCompatHelper, url: String, includeThisApp: Boolean = true, contentType: String? = null, diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/Context.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/Context.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.content.res.Configuration import android.os.Build import android.provider.Settings @@ -28,6 +29,16 @@ const val ACTION_MANAGE_DEFAULT_APPS_SETTINGS_HUAWEI = "com.android.settings.PRE private val logger = Logger("navigateToDefaultBrowserAppsSettings") /** + * The default [PackageManagerCompatHelper] for this [Context]. + * + * @returns a [DefaultPackageManagerCompatHelper] created with the context's [PackageManager]. + */ +val Context.packageManagerCompatHelper: PackageManagerCompatHelper + get() = DefaultPackageManagerCompatHelper( + DefaultPackageManagerWrapper(packageManager), + ) + +/** * Open OS settings for default browser. */ fun Context.navigateToDefaultBrowserAppsSettings(buildManufacturerChecker: BuildManufacturerChecker) { diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManager.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManager.kt @@ -1,71 +0,0 @@ -/* 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 mozilla.components.support.utils.ext - -import android.content.Intent -import android.content.pm.ApplicationInfo -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.content.pm.ResolveInfo -import android.os.Build - -/** - * Get [ResolveInfo] list for an [Intent] with a specified flag - * - * @param intent The name of the package to check for. - */ -fun PackageManager.queryIntentActivitiesCompat(intent: Intent, flag: Int): MutableList<ResolveInfo> { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(flag.toLong())) - } else { - @Suppress("DEPRECATION") - queryIntentActivities(intent, flag) - } -} - -/** - * Get [ResolveInfo] for an [Intent] with a specified flag - * - * @param intent The name of the package to check for. - */ -fun PackageManager.resolveActivityCompat(intent: Intent, flag: Int): ResolveInfo? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - resolveActivity(intent, PackageManager.ResolveInfoFlags.of(flag.toLong())) - } else { - @Suppress("DEPRECATION") - resolveActivity(intent, flag) - } -} - -/** - * Get a package info with a specified flag. - * - * @param packageName The name of the package to check for. - * - * @throws PackageManager.NameNotFoundException (API >= 33) if the package for [packageName] is not installed. - * @throws UnsupportedOperationException (API <= 32) if getPackageInfo is not implemented in subclass. - */ -fun PackageManager.getPackageInfoCompat(packageName: String, flag: Int): PackageInfo { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flag.toLong())) - } else { - @Suppress("DEPRECATION") - getPackageInfo(packageName, flag) - } -} - -/** - * Get a application info with a specified flag - * - * @param host The URI host. - */ -fun PackageManager.getApplicationInfoCompat(host: String, flag: Int): ApplicationInfo { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getApplicationInfo(host, PackageManager.ApplicationInfoFlags.of(flag.toLong())) - } else { - @Suppress("DEPRECATION") - getApplicationInfo(host, flag) - } -} diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManagerCompatHelper.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManagerCompatHelper.kt @@ -0,0 +1,131 @@ +/* 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 mozilla.components.support.utils.ext + +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.PackageInfoFlags +import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ResolveInfo +import android.os.Build + +/** + * Compatibility layer over [PackageManagerWrapper] APIs, providing safe access to common + * package management operations across Android versions. + */ +interface PackageManagerCompatHelper { + /** + * Get [ResolveInfo] list for an [Intent] with the specified flag. + * + * @param intent the [intent] to resolve. + * + * @return a list of [ResolveInfo] objects containing one entry for each matching activity, + * ordered from best to worst. If there are no matching activities, an empty list is returned. + * + * @see PackageManager.queryIntentActivities + */ + fun queryIntentActivitiesCompat(intent: Intent, flag: Int): List<ResolveInfo> + + /** + * Get [ResolveInfo] for an [Intent] with a specified flag + * + * @param intent the [intent] to resolve. + * + * @return a [ResolveInfo] object containing the final activity intent that was determined to be + * the best action. Returns `null` if no matching activity was found. + * + * @see PackageManager.resolveActivity + */ + fun resolveActivityCompat(intent: Intent, flag: Int): ResolveInfo? + + /** + * Get a [PackageInfo] with a specified flag. + * + * @param packageName The name of the package to check for. + * + * @return a [PackageInfo] object containing information about the package. + * + * @throws PackageManager.NameNotFoundException if the package for [packageName] is not installed. + * @throws UnsupportedOperationException if [PackageManager.getPackageInfo] is not implemented in subclass. + * + * @see PackageManager.getPackageInfo + */ + @Throws(PackageManager.NameNotFoundException::class, UnsupportedOperationException::class) + fun getPackageInfoCompat(packageName: String, flag: Int): PackageInfo + + /** + * Get [ApplicationInfo] with the specified flag + * + * @param packageName The URI host. + * + * @return An [ApplicationInfo] containing information about the package. + * + * @throws PackageManager.NameNotFoundException if the package for [packageName] is not installed. + * @throws UnsupportedOperationException if [PackageManager.getApplicationInfo] is not implemented in subclass. + * + * @see PackageManager.getApplicationInfo + */ + @Throws(PackageManager.NameNotFoundException::class, UnsupportedOperationException::class) + fun getApplicationInfoCompat(packageName: String, flag: Int): ApplicationInfo +} + +/** + * The default implementation of [PackageManagerCompatHelper]. + * + * @param packageManagerWrapper the [PackageManagerWrapper] to use. + */ +class DefaultPackageManagerCompatHelper( + val packageManagerWrapper: PackageManagerWrapper, +) : PackageManagerCompatHelper { + override fun queryIntentActivitiesCompat(intent: Intent, flag: Int): List<ResolveInfo> { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManagerWrapper.queryIntentActivities( + intent, + ResolveInfoFlags.of(flag.toLong()), + ) + } else { + @Suppress("DEPRECATION") + packageManagerWrapper.queryIntentActivities(intent, flag) + } + } + + override fun resolveActivityCompat(intent: Intent, flag: Int): ResolveInfo? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManagerWrapper.resolveActivity( + intent, + ResolveInfoFlags.of(flag.toLong()), + ) + } else { + @Suppress("DEPRECATION") + packageManagerWrapper.resolveActivity(intent, flag) + } + } + + override fun getPackageInfoCompat(packageName: String, flag: Int): PackageInfo { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManagerWrapper.getPackageInfo( + packageName, + PackageInfoFlags.of(flag.toLong()), + ) + } else { + @Suppress("DEPRECATION") + packageManagerWrapper.getPackageInfo(packageName, flag) + } + } + + override fun getApplicationInfoCompat(packageName: String, flag: Int): ApplicationInfo { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManagerWrapper.getApplicationInfo( + packageName, + PackageManager.ApplicationInfoFlags.of(flag.toLong()), + ) + } else { + @Suppress("DEPRECATION") + packageManagerWrapper.getApplicationInfo(packageName, flag) + } + } +} diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManagerWrapper.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/PackageManagerWrapper.kt @@ -0,0 +1,134 @@ +/* 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 mozilla.components.support.utils.ext + +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.os.Build +import androidx.annotation.RequiresApi + +/** + * Wrapper for [PackageManager] APIs. + * + * See the linked documentation for full details of each API function. + * This is not exhaustive of all available public APIs. + */ +interface PackageManagerWrapper { + /** + * @see [PackageManager.queryIntentActivities]. + */ + fun queryIntentActivities( + intent: Intent, + flags: PackageManager.ResolveInfoFlags, + ): List<ResolveInfo> + + /** + * @see [PackageManager.queryIntentActivities]. + */ + fun queryIntentActivities(intent: Intent, flags: Int): List<ResolveInfo> + + /** + * @see [PackageManager.resolveActivity]. + */ + fun resolveActivity(intent: Intent, flags: PackageManager.ResolveInfoFlags): ResolveInfo? + + /** + * @see [PackageManager.resolveActivity]. + */ + fun resolveActivity(intent: Intent, flags: Int): ResolveInfo? + + /** + * @throws PackageManager.NameNotFoundException if the package for [packageName] is not installed. + * @throws UnsupportedOperationException if [PackageManager.getPackageInfo] is not implemented + * in subclass. + * + * @see [PackageManager.getPackageInfo]. + */ + fun getPackageInfo(packageName: String, flags: PackageManager.PackageInfoFlags): PackageInfo + + /** + * @throws PackageManager.NameNotFoundException if the package for [packageName] is not installed. + * + * @see [PackageManager.getPackageInfo]. + */ + fun getPackageInfo(packageName: String, flags: Int): PackageInfo + + /** + * @throws PackageManager.NameNotFoundException if the package for [packageName] is not installed. + * @throws UnsupportedOperationException if [PackageManager.getApplicationInfo] is not + * implemented in subclass. + * + * @see [PackageManager.getApplicationInfo]. + */ + fun getApplicationInfo( + packageName: String, + flags: PackageManager.ApplicationInfoFlags, + ): ApplicationInfo + + /** + * @throws PackageManager.NameNotFoundException if a package with the given name cannot be + * found on the system. + * + * @see [PackageManager.getApplicationInfo]. + */ + fun getApplicationInfo(packageName: String, flags: Int): ApplicationInfo +} + +/** + * Default implementation of [PackageManagerWrapper]. + * + * @param packageManager [PackageManager] to wrap. + */ +class DefaultPackageManagerWrapper( + private val packageManager: PackageManager, +) : PackageManagerWrapper { + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun queryIntentActivities( + intent: Intent, + flags: PackageManager.ResolveInfoFlags, + ): List<ResolveInfo> = packageManager.queryIntentActivities(intent, flags) + + override fun queryIntentActivities( + intent: Intent, + flags: Int, + ): List<ResolveInfo> = packageManager.queryIntentActivities(intent, flags) + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun resolveActivity( + intent: Intent, + flags: PackageManager.ResolveInfoFlags, + ): ResolveInfo? = packageManager.resolveActivity(intent, flags) + + override fun resolveActivity( + intent: Intent, + flags: Int, + ): ResolveInfo? = packageManager.resolveActivity(intent, flags) + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun getPackageInfo( + packageName: String, + flags: PackageManager.PackageInfoFlags, + ): PackageInfo = packageManager.getPackageInfo(packageName, flags) + + override fun getPackageInfo( + packageName: String, + flags: Int, + ): PackageInfo = packageManager.getPackageInfo(packageName, flags) + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun getApplicationInfo( + packageName: String, + flags: PackageManager.ApplicationInfoFlags, + ): ApplicationInfo = packageManager.getApplicationInfo(packageName, flags) + + override fun getApplicationInfo( + packageName: String, + flags: Int, + ): ApplicationInfo = packageManager.getApplicationInfo(packageName, flags) +} diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt @@ -24,7 +24,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.mozilla.fenix.R @@ -50,7 +50,8 @@ class SettingsSubMenuAboutRobot { fun verifyVersionNumber() { val context = InstrumentationRegistry.getInstrumentation().targetContext - val packageInfo = context.packageManager.getPackageInfoCompat(context.packageName, 0) + val packageInfo = + context.packageManagerCompatHelper.getPackageInfoCompat(context.packageName, 0) val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString() val buildNVersion = "${packageInfo.versionName} (Build #$versionCode)\n" val geckoVersion = diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt @@ -18,7 +18,7 @@ import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.utils.EXTRA_ACTIVITY_REFERRER_CATEGORY import mozilla.components.support.utils.EXTRA_ACTIVITY_REFERRER_PACKAGE import mozilla.components.support.utils.INTENT_TYPE_PDF -import mozilla.components.support.utils.ext.getApplicationInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import mozilla.components.support.utils.toSafeIntent import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE @@ -165,7 +165,7 @@ class IntentReceiverActivity : Activity() { intent.putExtra(EXTRA_ACTIVITY_REFERRER_PACKAGE, r.host) r.host?.let { host -> try { - val category = packageManager.getApplicationInfoCompat(host, 0).category + val category = packageManagerCompatHelper.getApplicationInfoCompat(host, 0).category intent.putExtra(EXTRA_ACTIVITY_REFERRER_CATEGORY, category) } catch (_: PackageManager.NameNotFoundException) { // At least we tried. diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -23,7 +23,7 @@ import mozilla.components.lib.crash.store.CrashReportOption import mozilla.components.support.ktx.android.content.isMainProcess import mozilla.components.support.utils.BrowsersCache import mozilla.components.support.utils.RunWhenReadyQueue -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity @@ -200,6 +200,9 @@ private fun getSentryProjectUrl(): String? { private val Context.versionInfoProvider: VersionInfoProvider get() { - val packageInfo = applicationContext.packageManager.getPackageInfoCompat(applicationContext.packageName, 0) + val packageInfo = applicationContext.packageManagerCompatHelper.getPackageInfoCompat( + applicationContext.packageName, + 0, + ) return VersionInfoProvider.fromPackageInfo(packageInfo) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/GrowthDataWorker.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/GrowthDataWorker.kt @@ -10,7 +10,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.settings import java.util.concurrent.TimeUnit @@ -71,7 +71,7 @@ class GrowthDataWorker( return (FULL_WEEK_MILLIS <= timeDifference) } - private fun getInstalledTime(context: Context): Long = context.packageManager + private fun getInstalledTime(context: Context): Long = context.packageManagerCompatHelper .getPackageInfoCompat(context.packageName, 0) .firstInstallTime } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt @@ -10,7 +10,7 @@ import android.content.Context import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.android.DefaultActivityLifecycleCallbacks import org.mozilla.fenix.ext.settings import org.mozilla.fenix.nimbus.FxNimbus @@ -278,7 +278,7 @@ internal class DefaultMetricsStorage( FxNimbus.features.growthData.value().enabled } - fun getInstalledTime(context: Context): Long = context.packageManager + fun getInstalledTime(context: Context): Long = context.packageManagerCompatHelper .getPackageInfoCompat(context.packageName, 0) .firstInstallTime } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/MozillaProductDetector.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/MozillaProductDetector.kt @@ -7,7 +7,7 @@ package org.mozilla.fenix.components.metrics import android.content.Context import android.content.pm.PackageManager import mozilla.components.support.utils.BrowsersCache -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper object MozillaProductDetector { enum class MozillaProducts(val productName: String) { @@ -46,7 +46,7 @@ object MozillaProductDetector { fun packageIsInstalled(context: Context, packageName: String): Boolean { try { - context.packageManager.getPackageInfoCompat(packageName, 0) + context.packageManagerCompatHelper.getPackageInfoCompat(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { return false } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/RecordedNimbusContext.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/RecordedNimbusContext.kt @@ -9,7 +9,7 @@ import android.os.Build import androidx.annotation.VisibleForTesting import mozilla.components.support.locale.LocaleManager import mozilla.components.support.locale.LocaleManager.getSystemDefault -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.json.JSONArray import org.json.JSONObject import org.mozilla.experiments.nimbus.NIMBUS_DATA_DIR @@ -186,7 +186,8 @@ class RecordedNimbusContext( langTag, ) - val packageInfo = context.packageManager.getPackageInfoCompat(context.packageName, 0) + val packageInfo = + context.packageManagerCompatHelper.getPackageInfoCompat(context.packageName, 0) val deviceInfo = NimbusDeviceInfo.default() val db = File(context.applicationInfo.dataDir, NIMBUS_DATA_DIR) val calculatedAttributes = getCalculatedAttributes( 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 @@ -20,7 +20,7 @@ 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.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.FenixApplication import org.mozilla.fenix.R import org.mozilla.fenix.components.Components @@ -225,7 +225,7 @@ fun Context.pixelSizeFor(@DimenRes resId: Int) = resources.getDimensionPixelSize * @return The installation time in milliseconds since epoch, or `0L` if unavailable. */ fun Context.getApplicationInstalledTime(logger: Logger): Long = try { - packageManager.getPackageInfoCompat(packageName, 0).firstInstallTime + packageManagerCompatHelper.getPackageInfoCompat(packageName, 0).firstInstallTime } catch (e: PackageManager.NameNotFoundException) { logger.warn("Unable to retrieve package info for $packageName", e) 0L diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -18,7 +18,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.GleanMetrics.Events @@ -155,8 +155,10 @@ class AboutFragment( private fun populateAboutHeader() { val aboutText = try { - val packageInfo = - requireContext().packageManager.getPackageInfoCompat(requireContext().packageName, 0) + val packageInfo = requireContext().packageManagerCompatHelper.getPackageInfoCompat( + requireContext().packageName, + 0, + ) val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString() val maybeFenixVcsHash = if (BuildConfig.VCS_HASH.isNotBlank()) ", ${BuildConfig.VCS_HASH}" else "" val maybeGecko = getString(R.string.gecko_view_abbreviation) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt @@ -24,7 +24,7 @@ import kotlinx.coroutines.launch import mozilla.components.concept.sync.DeviceCapability import mozilla.components.feature.share.RecentAppsStorage import mozilla.components.service.fxa.manager.FxaAccountManager -import mozilla.components.support.utils.ext.queryIntentActivitiesCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.isOnline @@ -163,7 +163,7 @@ class ShareViewModel(application: Application) : AndroidViewModel(application) { @VisibleForTesting @WorkerThread fun getIntentActivities(shareIntent: Intent, context: Context): List<ResolveInfo>? { - return context.packageManager.queryIntentActivitiesCompat(shareIntent, 0) + return context.packageManagerCompatHelper.queryIntentActivitiesCompat(shareIntent, 0) } /** diff --git a/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/SettingsMozillaMenuRobot.kt b/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/SettingsMozillaMenuRobot.kt @@ -16,7 +16,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector import junit.framework.TestCase.assertTrue -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.hamcrest.Matchers.allOf import org.mozilla.focus.R import org.mozilla.focus.helpers.TestHelper.appName @@ -42,7 +42,8 @@ class SettingsMozillaMenuRobot { fun verifyVersionNumbers() { val context = InstrumentationRegistry.getInstrumentation().targetContext - val packageInfo = context.packageManager.getPackageInfoCompat(context.packageName, 0) + val packageInfo = + context.packageManagerCompatHelper.getPackageInfoCompat(context.packageName, 0) val versionName = packageInfo.versionName ?: "" val gvBuildId = org.mozilla.geckoview.BuildConfig.MOZ_APP_BUILDID val gvVersion = org.mozilla.geckoview.BuildConfig.MOZ_APP_VERSION diff --git a/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/helpers/TestHelper.kt b/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/helpers/TestHelper.kt @@ -38,7 +38,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import junit.framework.AssertionFailedError -import mozilla.components.support.utils.ext.getApplicationInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import okio.Buffer import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf @@ -101,7 +101,7 @@ object TestHelper { fun isPackageInstalled(packageName: String): Boolean { return try { - val packageManager = getInstrumentation().context.packageManager + val packageManager = getInstrumentation().context.packageManagerCompatHelper packageManager.getApplicationInfoCompat(packageName, 0).enabled } catch (exception: PackageManager.NameNotFoundException) { Log.d("TestLog", exception.message.toString()) diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/activity/FirefoxInstallationHelper.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/activity/FirefoxInstallationHelper.kt @@ -9,7 +9,7 @@ import android.content.Intent import android.content.pm.ActivityInfo import androidx.core.net.toUri import mozilla.components.support.utils.Browsers -import mozilla.components.support.utils.ext.resolveActivityCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import mozilla.telemetry.glean.private.NoExtras import org.mozilla.focus.GleanMetrics.OpenWith @@ -30,7 +30,7 @@ object FirefoxInstallationHelper { */ fun resolveAppStore(context: Context): ActivityInfo? { val resolveInfo = - context.packageManager.resolveActivityCompat(storeIntent, 0) ?: return null + context.packageManagerCompatHelper.resolveActivityCompat(storeIntent, 0) ?: return null return if (resolveInfo.activityInfo.exported) { resolveInfo.activityInfo diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/ext/Context.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/ext/Context.kt @@ -9,7 +9,7 @@ import android.app.Activity import android.content.Context import android.view.ContextThemeWrapper import android.view.accessibility.AccessibilityManager -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.focus.Components import org.mozilla.focus.FocusApplication import org.mozilla.focus.utils.Settings @@ -45,7 +45,8 @@ val Context.accessibilityManager: AccessibilityManager */ val Context.installedDate: String get() { - val installTime = this.packageManager.getPackageInfoCompat(this.packageName, 0).firstInstallTime + val installTime = + this.packageManagerCompatHelper.getPackageInfoCompat(packageName, 0).firstInstallTime return DateFormat.getDateInstance().format(installTime) } diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/about/AboutFragment.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/about/AboutFragment.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.unit.dp import androidx.core.content.pm.PackageInfoCompat import mozilla.components.browser.state.state.SessionState -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.focus.BuildConfig import org.mozilla.focus.R import org.mozilla.focus.ext.components @@ -172,7 +172,7 @@ private fun getAboutHeader(context: Context): String { val servicesAbbreviation = context.getString(R.string.services_abbreviation) val servicesIndicator = mozilla.components.Build.APPLICATION_SERVICES_VERSION val packageInfo = - context.packageManager.getPackageInfoCompat(context.packageName, 0) + context.packageManagerCompatHelper.getPackageInfoCompat(context.packageName, 0) val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString() val vcsHash = if (BuildConfig.VCS_HASH.isNotBlank()) ", ${BuildConfig.VCS_HASH}" else "" diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/telemetry/FenixProductDetector.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/telemetry/FenixProductDetector.kt @@ -7,7 +7,7 @@ package org.mozilla.focus.telemetry import android.content.Context import android.content.pm.ActivityInfo import android.content.pm.PackageManager -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper object FenixProductDetector { enum class FenixVersion(val packageName: String) { @@ -39,7 +39,7 @@ object FenixProductDetector { private fun packageIsInstalled(context: Context, packageName: String): Boolean { try { - context.packageManager.getPackageInfoCompat(packageName, 0) + context.packageManagerCompatHelper.getPackageInfoCompat(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { return false } diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/utils/SupportUtils.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/utils/SupportUtils.kt @@ -14,7 +14,7 @@ import androidx.fragment.app.FragmentActivity import mozilla.components.browser.state.state.ExternalAppType import mozilla.components.browser.state.state.SessionState import mozilla.components.feature.customtabs.createCustomTabConfigFromIntent -import mozilla.components.support.utils.ext.getPackageInfoCompat +import mozilla.components.support.utils.ext.packageManagerCompatHelper import org.mozilla.focus.BuildConfig import org.mozilla.focus.R import org.mozilla.focus.activity.CustomTabActivity @@ -105,7 +105,10 @@ object SupportUtils { */ fun getAppVersion(context: Context): String { try { - return context.packageManager.getPackageInfoCompat(context.packageName, 0).versionName ?: "" + return context.packageManagerCompatHelper.getPackageInfoCompat( + context.packageName, + 0, + ).versionName ?: "" } catch (e: PackageManager.NameNotFoundException) { // This should be impossible - we should always be able to get information about ourselves: throw IllegalStateException("Unable find package details for Focus", e)