commit c212381ff1bf4420806af3bc3c967acf49e2520a
parent eab164ba1f92a24404f33d2054da35efc1d8afd9
Author: Andrey Zinovyev <azinovyev@mozilla.com>
Date: Wed, 1 Oct 2025 13:47:22 +0000
Bug 1986714 - Remove (activity as HomeActivity) casts in AddonsManagement r=jonalmeida,android-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D263635
Diffstat:
6 files changed, 97 insertions(+), 97 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt
@@ -26,11 +26,10 @@ import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.AddonManagerException
import mozilla.components.feature.addons.ui.AddonsManagerAdapter
-import org.mozilla.fenix.BrowserDirection
-import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentAddOnsManagementBinding
import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.ext.openToBrowser
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.runIfFragmentIsAttached
import org.mozilla.fenix.ext.showToolbar
@@ -50,19 +49,10 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
private var adapter: AddonsManagerAdapter? = null
- private val browsingModeManager by lazy {
- (activity as HomeActivity).browsingModeManager
- }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentAddOnsManagementBinding.bind(view)
bindRecyclerView()
- (activity as HomeActivity).webExtensionPromptFeature.onAddonChanged = {
- runIfFragmentIsAttached {
- adapter?.updateAddon(it)
- }
- }
}
override fun onResume() {
@@ -75,7 +65,6 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
// letting go of the resources to avoid memory leak.
adapter = null
binding = null
- (activity as HomeActivity).webExtensionPromptFeature.onAddonChanged = {}
}
private fun bindRecyclerView() {
@@ -84,12 +73,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
onInstallButtonClicked = ::installAddon,
onMoreAddonsButtonClicked = ::openAMO,
onLearnMoreClicked = { link, addon ->
- openLearnMoreLink(
- activity as HomeActivity,
- link,
- addon,
- BrowserDirection.FromAddonsManagementFragment,
- )
+ binding?.root?.openLearnMoreLink(link, addon)
},
)
@@ -217,10 +201,12 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
}
private fun openAMO() {
- openLinkInNewTab(
- activity as HomeActivity,
- AMO_HOMEPAGE_FOR_ANDROID,
- BrowserDirection.FromAddonsManagementFragment,
+ findNavController().openToBrowser()
+ val isPrivate = requireComponents.appStore.state.mode.isPrivate
+ requireComponents.useCases.fenixBrowserUseCases.loadUrlOrSearch(
+ searchTermOrURL = AMO_HOMEPAGE_FOR_ANDROID,
+ newTab = true,
+ private = isPrivate,
)
}
}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/Extensions.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/Extensions.kt
@@ -5,13 +5,14 @@
package org.mozilla.fenix.addons
import android.view.View
+import androidx.navigation.findNavController
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.ui.AddonsManagerAdapterDelegate
-import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig
-import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.compose.snackbar.Snackbar
import org.mozilla.fenix.compose.snackbar.SnackbarState
+import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.ext.openToBrowser
import org.mozilla.fenix.settings.SupportUtils
/**
@@ -35,21 +36,28 @@ internal fun showSnackBar(
).show()
}
-internal fun openLearnMoreLink(
- activity: HomeActivity,
+internal fun View.openLearnMoreLink(
link: AddonsManagerAdapterDelegate.LearnMoreLinks,
addon: Addon,
- from: BrowserDirection,
) {
- val url = when (link) {
+ val url = resolveLearnMoreUrl(link, addon) ?: return
+ findNavController().openToBrowser()
+ val isPrivate = context.components.appStore.state.mode.isPrivate
+ context.components.useCases.fenixBrowserUseCases.loadUrlOrSearch(
+ searchTermOrURL = url,
+ newTab = true,
+ private = isPrivate,
+ )
+}
+
+private fun resolveLearnMoreUrl(
+ link: AddonsManagerAdapterDelegate.LearnMoreLinks,
+ addon: Addon,
+): String? {
+ return when (link) {
AddonsManagerAdapterDelegate.LearnMoreLinks.BLOCKLISTED_ADDON ->
"${BuildConfig.AMO_BASE_URL}/android/blocked-addon/${addon.id}/${addon.version}/"
AddonsManagerAdapterDelegate.LearnMoreLinks.ADDON_NOT_CORRECTLY_SIGNED ->
- SupportUtils.getSumoURLForTopic(activity.baseContext, SupportUtils.SumoTopic.UNSIGNED_ADDONS)
+ SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.UNSIGNED_ADDONS)
}
- openLinkInNewTab(activity, url, from)
-}
-
-internal fun openLinkInNewTab(activity: HomeActivity, url: String, from: BrowserDirection) {
- activity.openToBrowserAndLoad(searchTermOrURL = url, newTab = true, from = from)
}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt
@@ -28,9 +28,7 @@ import mozilla.components.feature.addons.ui.translateName
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.ktx.android.content.appName
import mozilla.components.support.ktx.android.content.appVersionName
-import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig
-import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.databinding.FragmentInstalledAddOnDetailsBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.runIfFragmentIsAttached
@@ -168,12 +166,7 @@ class InstalledAddonDetailsFragment : Fragment() {
messageBarWarningView,
messageBarErrorView,
onLearnMoreLinkClicked = { link ->
- openLearnMoreLink(
- activity as HomeActivity,
- link,
- addon,
- BrowserDirection.FromAddonDetailsFragment,
- )
+ binding.root.openLearnMoreLink(link, addon)
},
addon,
addon.translateName(it),
@@ -228,12 +221,12 @@ class InstalledAddonDetailsFragment : Fragment() {
switch.isClickable = true
enableButtons()
switch.isChecked = addon.isEnabled()
- context?.let {
+ context?.let { ctx ->
showSnackBar(
binding.root,
getString(
addonsR.string.mozac_feature_addons_failed_to_enable,
- addon.translateName(it),
+ addon.translateName(ctx),
),
)
}
@@ -257,12 +250,12 @@ class InstalledAddonDetailsFragment : Fragment() {
switch.isClickable = true
switch.isChecked = addon.isEnabled()
enableButtons()
- context?.let {
+ context?.let { ctx ->
showSnackBar(
binding.root,
getString(
addonsR.string.mozac_feature_addons_failed_to_disable,
- addon.translateName(it),
+ addon.translateName(ctx),
),
)
}
@@ -337,17 +330,17 @@ class InstalledAddonDetailsFragment : Fragment() {
}
private fun bindReportButton() {
- binding.reportAddOn.setOnClickListener {
- val shouldCreatePrivateSession = (activity as HomeActivity).browsingModeManager.mode.isPrivate
+ binding.reportAddOn.setOnClickListener { v ->
+ val shouldCreatePrivateSession = v.context.components.appStore.state.mode.isPrivate
- it.context.components.useCases.tabsUseCases.selectOrAddTab(
+ v.context.components.useCases.tabsUseCases.selectOrAddTab(
url = "${BuildConfig.AMO_BASE_URL}/android/feedback/addon/${addon.id}/",
private = shouldCreatePrivateSession,
ignoreFragment = true,
)
// Send user to the newly open tab.
- it.findNavController().navigate(
+ v.findNavController().navigate(
InstalledAddonDetailsFragmentDirections.actionGlobalBrowser(null),
)
}
@@ -356,12 +349,11 @@ class InstalledAddonDetailsFragment : Fragment() {
private fun bindSettings() {
binding.settings.apply {
isVisible = shouldSettingsBeVisible()
- setOnClickListener {
+ setOnClickListener { v ->
val settingUrl = addon.installedState?.optionsPageUrl ?: return@setOnClickListener
val directions = if (addon.installedState?.openOptionsPageInTab == true) {
- val components = it.context.components
- val shouldCreatePrivateSession =
- (activity as HomeActivity).browsingModeManager.mode.isPrivate
+ val components = v.context.components
+ val shouldCreatePrivateSession = v.context.components.appStore.state.mode.isPrivate
// If the addon settings page is already open in a tab, select that one
components.useCases.tabsUseCases.selectOrAddTab(
@@ -414,12 +406,12 @@ class InstalledAddonDetailsFragment : Fragment() {
onError = { _, _ ->
runIfFragmentIsAttached {
setAllInteractiveViewsClickable(binding, true)
- context?.let {
+ context?.let { ctx ->
showSnackBar(
binding.root,
getString(
addonsR.string.mozac_feature_addons_failed_to_uninstall,
- addon.translateName(it),
+ addon.translateName(ctx),
),
)
}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/NavController.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/NavController.kt
@@ -12,6 +12,7 @@ import androidx.navigation.NavOptions
import mozilla.components.concept.base.crash.Breadcrumb
import mozilla.components.lib.crash.CrashReporter
import mozilla.components.support.base.log.logger.Logger
+import org.mozilla.fenix.R
/**
* Navigate from the fragment with [id] using the given [directions].
@@ -72,3 +73,13 @@ fun NavController.hasTopDestination(fragmentClassName: String): Boolean {
true,
) == true
}
+
+/**
+ * Navigate into the Browser destination without casting to HomeActivity.
+ * Fragments can call: findNavController().openToBrowser(direction)
+ */
+fun NavController.openToBrowser() {
+ if (alreadyOnDestination(R.id.browserFragment)) return
+ val dest = R.id.browserFragment
+ navigate(dest)
+}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/extension/WebExtensionPromptFeature.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/extension/WebExtensionPromptFeature.kt
@@ -55,13 +55,6 @@ class WebExtensionPromptFeature(
) : LifecycleAwareFeature {
/**
- * (optional) callback invoked when an add-on was updated due to an interaction with a
- * [WebExtensionPromptRequest].
- * Won't be needed after https://bugzilla.mozilla.org/show_bug.cgi?id=1858484.
- */
- var onAddonChanged: (Addon) -> Unit = {}
-
- /**
* Whether or not an add-on installation is in progress.
*/
private var isInstallationInProgress = false
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragmentTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragmentTest.kt
@@ -33,9 +33,12 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.R
+import org.mozilla.fenix.components.AppStore
+import org.mozilla.fenix.components.appstate.AppState
+import org.mozilla.fenix.components.usecases.FenixBrowserUseCases
import org.mozilla.fenix.databinding.FragmentInstalledAddOnDetailsBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.settings.SupportUtils
@@ -167,7 +170,6 @@ class InstalledAddonDetailsFragmentTest {
fun `GIVEN an add-on WHEN clicking the report button THEN a new tab is open`() {
val addon = mockAddon()
every { fragment.addon } returns addon
- every { fragment.activity } returns mockk<HomeActivity>(relaxed = true)
val useCases = mockk<TabsUseCases>()
val selectOrAddTab = mockk<TabsUseCases.SelectOrAddUseCase>()
every { selectOrAddTab.invoke(any(), any(), any(), any(), any()) } returns "some-tab-id"
@@ -185,6 +187,15 @@ class InstalledAddonDetailsFragmentTest {
val navController = mockk<NavController>(relaxed = true)
Navigation.setViewNavController(fragment.binding.root, navController)
+ val appStore = mockk<AppStore>()
+ val appState = mockk<AppState>()
+ every { appState.mode } returns mockk(relaxed = true) {
+ every { isPrivate } returns false // or true
+ }
+
+ every { appStore.state } returns appState
+ every { testContext.components.appStore } returns appStore
+
// Click the report button.
fragment.binding.reportAddOn.performClick()
@@ -206,14 +217,12 @@ class InstalledAddonDetailsFragmentTest {
fun `GIVEN an add-on and private browsing mode is used WHEN clicking the report button THEN a new private tab is open`() {
val addon = mockAddon()
every { fragment.addon } returns addon
- val homeActivity = mockk<HomeActivity>(relaxed = true)
- every { homeActivity.browsingModeManager.mode.isPrivate } returns true
- every { fragment.activity } returns homeActivity
val useCases = mockk<TabsUseCases>()
val selectOrAddTab = mockk<TabsUseCases.SelectOrAddUseCase>()
every { selectOrAddTab.invoke(any(), any(), any(), any(), any()) } returns "some-tab-id"
every { useCases.selectOrAddTab } returns selectOrAddTab
every { testContext.components.useCases.tabsUseCases } returns useCases
+ every { testContext.components.appStore.state.mode.isPrivate } returns true
// We create the `binding` instance and bind the UI here because `onCreateView()` checks a late init variable
// and we cannot easily mock it to skip the check.
fragment.setBindingAndBindUI(
@@ -319,13 +328,7 @@ class InstalledAddonDetailsFragmentTest {
every { addon.isDisabledAsBlocklisted() } returns true
every { fragment.addon } returns addon
every { fragment.context } returns testContext
- every {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
- searchTermOrURL = any(),
- newTab = any(),
- from = any(),
- )
- } returns Unit
+
// We create the `binding` instance and bind the UI here because `onCreateView()` checks a late init variable
// and we cannot easily mock it to skip the check.
val binding = FragmentInstalledAddOnDetailsBinding.inflate(
@@ -334,6 +337,12 @@ class InstalledAddonDetailsFragmentTest {
false,
)
fragment.setBindingAndBindUI(binding)
+ val navController = mockk<NavController>(relaxed = true)
+ Navigation.setViewNavController(binding.root, navController)
+
+ val fenix = mockk<FenixBrowserUseCases>(relaxed = true)
+ every { testContext.components.useCases.fenixBrowserUseCases } returns fenix
+ every { testContext.components.appStore.state.mode.isPrivate } returns false
val warningView =
binding.root.findViewById<View>(addonsR.id.add_on_messagebar_warning)
@@ -344,11 +353,12 @@ class InstalledAddonDetailsFragmentTest {
errorView.findViewById<TextView>(addonsR.id.add_on_messagebar_error_learn_more_link)
.performClick()
+ verify { navController.navigate(R.id.browserFragment) }
verify {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
+ fenix.loadUrlOrSearch(
searchTermOrURL = "${BuildConfig.AMO_BASE_URL}/android/blocked-addon/some-addon-id/1.2.3/",
newTab = true,
- from = BrowserDirection.FromAddonDetailsFragment,
+ private = false,
)
}
}
@@ -359,14 +369,7 @@ class InstalledAddonDetailsFragmentTest {
every { addon.isDisabledAsNotCorrectlySigned() } returns true
every { fragment.addon } returns addon
every { fragment.context } returns testContext
- every {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
- searchTermOrURL = any(),
- newTab = any(),
- from = any(),
- )
- } returns Unit
- every { (fragment.activity as HomeActivity).baseContext } returns testContext
+
// We create the `binding` instance and bind the UI here because `onCreateView()` checks a late init variable
// and we cannot easily mock it to skip the check.
val binding = FragmentInstalledAddOnDetailsBinding.inflate(
@@ -375,6 +378,12 @@ class InstalledAddonDetailsFragmentTest {
false,
)
fragment.setBindingAndBindUI(binding)
+ val navController = mockk<NavController>(relaxed = true)
+ Navigation.setViewNavController(binding.root, navController)
+
+ val fenix = mockk<FenixBrowserUseCases>(relaxed = true)
+ every { testContext.components.useCases.fenixBrowserUseCases } returns fenix
+ every { testContext.components.appStore.state.mode.isPrivate } returns false
val warningView =
binding.root.findViewById<View>(addonsR.id.add_on_messagebar_warning)
@@ -385,11 +394,12 @@ class InstalledAddonDetailsFragmentTest {
errorView.findViewById<TextView>(addonsR.id.add_on_messagebar_error_learn_more_link)
.performClick()
+ verify { navController.navigate(R.id.browserFragment) }
verify {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
- searchTermOrURL = SupportUtils.getSumoURLForTopic(testContext, SupportUtils.SumoTopic.UNSIGNED_ADDONS),
+ fenix.loadUrlOrSearch(
+ searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.UNSIGNED_ADDONS),
newTab = true,
- from = BrowserDirection.FromAddonDetailsFragment,
+ private = false,
)
}
}
@@ -400,13 +410,6 @@ class InstalledAddonDetailsFragmentTest {
every { addon.isSoftBlocked() } returns true
every { fragment.addon } returns addon
every { fragment.context } returns testContext
- every {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
- searchTermOrURL = any(),
- newTab = any(),
- from = any(),
- )
- } returns Unit
// We create the `binding` instance and bind the UI here because `onCreateView()` checks a late init variable
// and we cannot easily mock it to skip the check.
@@ -416,6 +419,12 @@ class InstalledAddonDetailsFragmentTest {
false,
)
fragment.setBindingAndBindUI(binding)
+ val navController = mockk<NavController>(relaxed = true)
+ Navigation.setViewNavController(binding.root, navController)
+
+ val fenix = mockk<FenixBrowserUseCases>(relaxed = true)
+ every { testContext.components.useCases.fenixBrowserUseCases } returns fenix
+ every { testContext.components.appStore.state.mode.isPrivate } returns false
val warningView = binding.root.findViewById<View>(addonsR.id.add_on_messagebar_warning)
assertTrue(warningView.isVisible)
@@ -425,11 +434,12 @@ class InstalledAddonDetailsFragmentTest {
warningView.findViewById<TextView>(addonsR.id.add_on_messagebar_warning_learn_more_link)
.performClick()
+ verify { navController.navigate(R.id.browserFragment) }
verify {
- (fragment.activity as HomeActivity).openToBrowserAndLoad(
+ fenix.loadUrlOrSearch(
searchTermOrURL = "${BuildConfig.AMO_BASE_URL}/android/blocked-addon/some-addon-id/1.2.3/",
newTab = true,
- from = BrowserDirection.FromAddonDetailsFragment,
+ private = false,
)
}
}