diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml
index f070dce2..89b79531 100644
--- a/app/android/app/src/main/AndroidManifest.xml
+++ b/app/android/app/src/main/AndroidManifest.xml
@@ -205,6 +205,12 @@
android:exported="false"
android:theme="@style/AppTheme" />
+
+
("add_on"),
+ )
+
+ title = addon.translateName(this)
+
+ val fragment = AddonInternalSettingsFragment.create(addon)
+
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.addonSettingsContainer, fragment)
+ .commit()
+
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (!fragment.onBackPressed()) {
+ finish()
+ }
+ }
+ })
+ }
+
+ override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? =
+ when (name) {
+ EngineView::class.java.name -> components.core.engine.createView(context, attrs).asView()
+ else -> super.onCreateView(parent, name, context, attrs)
+ }
+
+ /**
+ * A fragment to show the internal settings of an add-on with [EngineView].
+ *
+ * Creates a fresh engine session and loads the addon's options page URL into it.
+ */
+ class AddonInternalSettingsFragment : AddonPopupBaseFragment() {
+
+ private val addonSettingsEngineView: EngineView
+ get() = requireView().findViewById(R.id.addonSettingsEngineView) as EngineView
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ initializeSession()
+ return inflater.inflate(R.layout.fragment_add_on_settings, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val optionsPageUrl = arguments?.getParcelableCompat("add_on", Addon::class.java)
+ ?.installedState?.optionsPageUrl
+
+ if (optionsPageUrl != null) {
+ engineSession?.let { session ->
+ addonSettingsEngineView.render(session)
+ session.loadUrl(optionsPageUrl)
+ }
+ } else {
+ activity?.finish()
+ }
+ }
+
+ companion object {
+ fun create(addon: Addon) = AddonInternalSettingsFragment().apply {
+ arguments = Bundle().apply {
+ putParcelable("add_on", addon)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/InstalledAddonDetailsActivity.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/InstalledAddonDetailsActivity.kt
index 2181c91b..cccd498e 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/InstalledAddonDetailsActivity.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/InstalledAddonDetailsActivity.kt
@@ -137,10 +137,21 @@ class InstalledAddonDetailsActivity : AppCompatActivity() {
view.isEnabled = shouldSettingsBeVisible(addon)
view.setOnClickListener {
val optionsPageUrl = addon.installedState?.optionsPageUrl ?: return@setOnClickListener
- components.useCases.tabsUseCases.addTab(optionsPageUrl, selectTab = true)
- val intent = packageManager.getLaunchIntentForPackage(packageName)
- intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- startActivity(intent)
+ if (addon.installedState?.openOptionsPageInTab == true) {
+ // Open settings in a browser tab, reusing an existing tab if already open.
+ components.useCases.tabsUseCases.selectOrAddTab(
+ url = optionsPageUrl,
+ ignoreFragment = true,
+ )
+ val intent = packageManager.getLaunchIntentForPackage(packageName)
+ intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ startActivity(intent)
+ } else {
+ // Open settings in an internal view with proper extension API support.
+ val intent = Intent(this, AddonInternalSettingsActivity::class.java)
+ intent.putExtra("add_on", addon)
+ startActivity(intent)
+ }
}
}
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/WebExtensionActionPopupActivity.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/WebExtensionActionPopupActivity.kt
index bb426c24..16e6e5b3 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/WebExtensionActionPopupActivity.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/addons/WebExtensionActionPopupActivity.kt
@@ -10,6 +10,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.R
@@ -37,10 +38,20 @@ class WebExtensionActionPopupActivity : AppCompatActivity() {
title = it
}
+ val fragment = WebExtensionActionPopupFragment.create(webExtensionId)
+
supportFragmentManager
.beginTransaction()
- .replace(R.id.addonSettingsContainer, WebExtensionActionPopupFragment.create(webExtensionId))
+ .replace(R.id.addonSettingsContainer, fragment)
.commit()
+
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (!fragment.onBackPressed()) {
+ finishAndRemoveTask()
+ }
+ }
+ })
}
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? =
diff --git a/packages/flutter_mozilla_components/example/android/app/src/main/AndroidManifest.xml b/packages/flutter_mozilla_components/example/android/app/src/main/AndroidManifest.xml
index 0f6233c0..f09f6e73 100644
--- a/packages/flutter_mozilla_components/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/flutter_mozilla_components/example/android/app/src/main/AndroidManifest.xml
@@ -91,6 +91,12 @@
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
+
+