open extension settings in main browser

This commit is contained in:
Fabian Freund
2026-02-08 05:35:53 +01:00
parent 4241d8700a
commit f2f2ca9f13
8 changed files with 19 additions and 300 deletions
@@ -172,17 +172,6 @@
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonSettingsActivity"
android:label="@string/mozac_feature_addons_addons"
android:exported="false"
android:theme="@style/AppTheme" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity"
android:label="@string/mozac_feature_addons_addons"
android:theme="@style/AppTheme"/>
<receiver
android:name=".SearchBarWidgetReceiver"
android:exported="true">
@@ -22,7 +22,6 @@ import androidx.annotation.CallSuper
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity
import eu.weblibre.flutter_mozilla_components.addons.WebExtensionPromptFeature
import eu.weblibre.flutter_mozilla_components.databinding.FragmentBrowserBinding
import eu.weblibre.flutter_mozilla_components.ext.getPreferenceKey
@@ -32,7 +31,12 @@ import eu.weblibre.flutter_mozilla_components.feature.WebExtensionToolbarFeature
import eu.weblibre.flutter_mozilla_components.integration.ReaderViewIntegration
import eu.weblibre.flutter_mozilla_components.services.DownloadService
import io.flutter.Log
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.action.WebExtensionAction
import mozilla.components.browser.state.state.EngineState
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.WebExtensionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.thumbnails.BrowserThumbnails
import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.app.links.AppLinksFeature
@@ -499,14 +503,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
}
private fun openPopup(webExtensionState: WebExtensionState) {
val intent = Intent(
components.profileApplicationContext,
WebExtensionActionPopupActivity::class.java
val store = components.core.store
val popupSession = store.state.extensions[webExtensionState.id]?.popupSession ?: return
val tab = createTab(url = "", source = SessionState.Source.Internal.None)
.copy(engineState = EngineState(popupSession))
store.dispatch(TabListAction.AddTabAction(tab, select = true))
store.dispatch(
WebExtensionAction.UpdatePopupSessionAction(webExtensionState.id, popupSession = null)
)
intent.putExtra("web_extension_id", webExtensionState.id)
intent.putExtra("web_extension_name", webExtensionState.name)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
@CallSuper
@@ -1,103 +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 eu.weblibre.flutter_mozilla_components.addons
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import eu.weblibre.flutter_mozilla_components.Components
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.ui.translateName
import mozilla.components.support.utils.ext.getParcelableCompat
import mozilla.components.support.utils.ext.getParcelableExtraCompat
import eu.weblibre.flutter_mozilla_components.R
/**
* An activity to show the settings of an add-on.
*/
class AddonSettingsActivity : AppCompatActivity() {
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_on_settings)
val addon = requireNotNull(
intent.getParcelableExtraCompat("add_on", Addon::class.java),
)
title = addon.translateName(this)
supportFragmentManager
.beginTransaction()
.replace(R.id.addonSettingsContainer, AddonSettingsFragment.create(addon))
.commit()
}
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 settings of an add-on with [EngineView].
*/
class AddonSettingsFragment : Fragment() {
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
private lateinit var optionsPageUrl: String
private lateinit var engineSession: EngineSession
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
optionsPageUrl = requireNotNull(
arguments?.getParcelableCompat(
"add_on",
Addon::class.java,
)?.installedState?.optionsPageUrl,
)
engineSession = components.core.engine.createSession()
return inflater.inflate(R.layout.fragment_add_on_settings, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val addonSettingsEngineView = view.findViewById<View>(R.id.addonSettingsEngineView) as EngineView
addonSettingsEngineView.render(engineSession)
engineSession.loadUrl(optionsPageUrl)
}
override fun onDestroyView() {
engineSession.close()
super.onDestroyView()
}
companion object {
/**
* Create an [AddonSettingsFragment] with add_on as a required parameter.
*/
fun create(addon: Addon) = AddonSettingsFragment().apply {
arguments = Bundle().apply {
putParcelable("add_on", addon)
}
}
}
}
}
@@ -136,9 +136,11 @@ class InstalledAddonDetailsActivity : AppCompatActivity() {
view.isVisible = shouldSettingsBeVisible(addon)
view.isEnabled = shouldSettingsBeVisible(addon)
view.setOnClickListener {
val intent = Intent(this, AddonSettingsActivity::class.java)
intent.putExtra("add_on", addon)
this.startActivity(intent)
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)
}
}
@@ -1,131 +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 eu.weblibre.flutter_mozilla_components.addons
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import eu.weblibre.flutter_mozilla_components.Components
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import mozilla.components.browser.state.action.WebExtensionAction
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.engine.window.WindowRequest
import mozilla.components.lib.state.ext.consumeFrom
import eu.weblibre.flutter_mozilla_components.R
/**
* An activity to show the pop up action of a web extension.
*/
class WebExtensionActionPopupActivity : AppCompatActivity() {
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
private lateinit var webExtensionId: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_on_settings)
webExtensionId = requireNotNull(intent.getStringExtra("web_extension_id"))
intent.getStringExtra("web_extension_name")?.let {
title = it
}
supportFragmentManager
.beginTransaction()
.replace(R.id.addonSettingsContainer, WebExtensionActionPopupFragment.create(webExtensionId))
.commit()
}
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 web extension action popup with [EngineView].
*/
class WebExtensionActionPopupFragment : Fragment(), EngineSession.Observer {
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
private var engineSession: EngineSession? = null
private lateinit var webExtensionId: String
private val addonSettingsEngineView: EngineView
get() = requireView().findViewById<View>(R.id.addonSettingsEngineView) as EngineView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
webExtensionId = requireNotNull(arguments?.getString("web_extension_id"))
engineSession = components.core.store.state.extensions[webExtensionId]?.popupSession
return inflater.inflate(R.layout.fragment_add_on_settings, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val session = engineSession
if (session != null) {
addonSettingsEngineView.render(session)
consumePopupSession()
} else {
consumeFrom(components.core.store) { state ->
state.extensions[webExtensionId]?.let { extState ->
extState.popupSession?.let {
if (engineSession == null) {
addonSettingsEngineView.render(it)
consumePopupSession()
engineSession = it
}
}
}
}
}
}
override fun onStart() {
super.onStart()
engineSession?.register(this)
}
override fun onStop() {
super.onStop()
engineSession?.unregister(this)
}
override fun onWindowRequest(windowRequest: WindowRequest) {
if (windowRequest.type == WindowRequest.Type.CLOSE) {
activity?.onBackPressedDispatcher?.onBackPressed()
}
}
private fun consumePopupSession() {
components.core.store.dispatch(
WebExtensionAction.UpdatePopupSessionAction(webExtensionId, popupSession = null),
)
}
companion object {
/**
* Create an [WebExtensionActionPopupFragment] with webExtensionId as a required parameter.
*/
fun create(webExtensionId: String) = WebExtensionActionPopupFragment().apply {
arguments = Bundle().apply {
putString("web_extension_id", webExtensionId)
}
}
}
}
}
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- 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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/addonSettingsContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent">
<mozilla.components.concept.engine.EngineView
tools:ignore="Instantiatable"
android:id="@+id/addonSettingsEngineView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -91,17 +91,6 @@
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonSettingsActivity"
android:label="@string/mozac_feature_addons_addons"
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name="eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity"
android:label="@string/mozac_feature_addons_addons"
android:theme="@style/Theme.AppCompat.Light"/>
<service
android:name="eu.weblibre.flutter_mozilla_components.services.DownloadService"
android:foregroundServiceType="dataSync" />