Loading components/browser/menu/build.gradle +4 −1 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ android { } dependencies { implementation project(':concept-engine') implementation project(':browser-state') implementation project(':support-base') implementation project(':support-ktx') implementation project(':ui-colors') implementation project(':ui-icons') implementation Dependencies.androidx_appcompat implementation Dependencies.androidx_core_ktx Loading @@ -40,7 +43,7 @@ dependencies { testImplementation Dependencies.androidx_test_junit testImplementation Dependencies.testing_robolectric testImplementation Dependencies.testing_mockito testImplementation Dependencies.androidx_test_core testImplementation Dependencies.testing_coroutines } Loading components/browser/menu/src/main/java/mozilla/components/browser/menu/BrowserMenu.kt +7 −5 Original line number Diff line number Diff line Loading @@ -28,10 +28,10 @@ import mozilla.components.support.ktx.android.view.onNextGlobalLayout /** * A popup menu composed of BrowserMenuItem objects. */ class BrowserMenu internal constructor( private val adapter: BrowserMenuAdapter open class BrowserMenu internal constructor( internal val adapter: BrowserMenuAdapter ) { private var currentPopup: PopupWindow? = null protected var currentPopup: PopupWindow? = null private var menuList: RecyclerView? = null /** Loading @@ -41,7 +41,7 @@ class BrowserMenu internal constructor( * the top of the menu is always visible. */ @SuppressLint("InflateParams") fun show( open fun show( anchor: View, orientation: Orientation = DOWN, endOfMenuAlwaysVisible: Boolean = false, Loading @@ -61,7 +61,9 @@ class BrowserMenu internal constructor( menuList?.setAccessibilityDelegate(object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(host, info) info?.collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(adapter.interactiveCount, 0, false) info?.collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain( adapter.interactiveCount, 0, false ) } }) Loading components/browser/menu/src/main/java/mozilla/components/browser/menu/BrowserMenuBuilder.kt +5 −2 Original line number Diff line number Diff line Loading @@ -14,12 +14,15 @@ import android.content.Context * @param endOfMenuAlwaysVisible when is set to true makes sure the bottom of the menu is always visible otherwise, * the top of the menu is always visible. */ class BrowserMenuBuilder( open class BrowserMenuBuilder( val items: List<BrowserMenuItem>, val extras: Map<String, Any> = emptyMap(), val endOfMenuAlwaysVisible: Boolean = false ) { fun build(context: Context): BrowserMenu { /** * Builds and returns a browser menu with [items] */ open fun build(context: Context): BrowserMenu { val adapter = BrowserMenuAdapter(context, items) return BrowserMenu(adapter) } Loading components/browser/menu/src/main/java/mozilla/components/browser/menu/WebExtensionBrowserMenu.kt 0 → 100644 +95 −0 Original line number Diff line number Diff line /* 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.browser.menu import android.view.View import android.widget.PopupWindow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.collect import mozilla.components.browser.menu.item.WebExtensionBrowserMenuItem import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.SessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.webextension.BrowserAction import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged typealias WebExtensionBrowserAction = BrowserAction /** * A popup menu for [WebExtensionBrowserAction] menu items. */ class WebExtensionBrowserMenu internal constructor( adapter: BrowserMenuAdapter, private val store: BrowserStore ) : BrowserMenu(adapter) { private var scope: CoroutineScope? = null @kotlinx.coroutines.ExperimentalCoroutinesApi override fun show( anchor: View, orientation: Orientation, endOfMenuAlwaysVisible: Boolean, onDismiss: () -> Unit ): PopupWindow { scope = store.flowScoped { flow -> flow.ifChanged { it.selectedTab } .collect { state -> getOrUpdateWebExtensionMenuItems(state, state.selectedTab) invalidate() } } return super.show( anchor, orientation, endOfMenuAlwaysVisible, onDismiss ).apply { setOnDismissListener { adapter.menu = null currentPopup = null scope?.cancel() webExtensionBrowserActions.clear() onDismiss() } } } companion object { internal val webExtensionBrowserActions = HashMap<String, WebExtensionBrowserMenuItem>() internal fun getOrUpdateWebExtensionMenuItems( state: BrowserState, tab: SessionState? = null ): List<BrowserMenuItem> { val menuItems = ArrayList<BrowserMenuItem>() val extensions = state.extensions.values.toList() extensions.filter { it.enabled }.forEach { extension -> extension.browserAction?.let { browserAction -> // Add the global browser action if it doesn't exist val browserMenuItem = webExtensionBrowserActions.getOrPut(extension.id) { val browserMenuItem = WebExtensionBrowserMenuItem( browserAction = browserAction, listener = browserAction.onClick ) browserMenuItem } // Apply tab-specific override of browser action tab?.extensionState?.get(extension.id)?.browserAction?.let { browserMenuItem.browserAction = browserAction.copyWithOverride(it) } menuItems.add(browserMenuItem) } } return menuItems } } } components/browser/menu/src/main/java/mozilla/components/browser/menu/WebExtensionBrowserMenuBuilder.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* 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.browser.menu import android.content.Context import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.store.BrowserStore /** * Browser menu builder with web extension support. It allows [WebExtensionBrowserMenu] to add * web extension browser actions. * * @param store [BrowserStore] required to render web extension browser actions * @param appendExtensionActionAtStart true if web extensions appear at the top (start) of the menu, * false if web extensions appear at the bottom of the menu. Default to false (bottom). */ class WebExtensionBrowserMenuBuilder( items: List<BrowserMenuItem>, extras: Map<String, Any> = emptyMap(), endOfMenuAlwaysVisible: Boolean = false, private val store: BrowserStore, private val appendExtensionActionAtStart: Boolean = false ) : BrowserMenuBuilder(items, extras, endOfMenuAlwaysVisible) { /** * Builds and returns a browser menu with combination of [items] and web extension browser actions. */ override fun build(context: Context): BrowserMenu { val extensionMenuItems = WebExtensionBrowserMenu.getOrUpdateWebExtensionMenuItems(store.state, store.state.selectedTab) val menuItems = if (appendExtensionActionAtStart) { extensionMenuItems + items } else { items + extensionMenuItems } val adapter = BrowserMenuAdapter(context, menuItems) return WebExtensionBrowserMenu(adapter, store) } } Loading
components/browser/menu/build.gradle +4 −1 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ android { } dependencies { implementation project(':concept-engine') implementation project(':browser-state') implementation project(':support-base') implementation project(':support-ktx') implementation project(':ui-colors') implementation project(':ui-icons') implementation Dependencies.androidx_appcompat implementation Dependencies.androidx_core_ktx Loading @@ -40,7 +43,7 @@ dependencies { testImplementation Dependencies.androidx_test_junit testImplementation Dependencies.testing_robolectric testImplementation Dependencies.testing_mockito testImplementation Dependencies.androidx_test_core testImplementation Dependencies.testing_coroutines } Loading
components/browser/menu/src/main/java/mozilla/components/browser/menu/BrowserMenu.kt +7 −5 Original line number Diff line number Diff line Loading @@ -28,10 +28,10 @@ import mozilla.components.support.ktx.android.view.onNextGlobalLayout /** * A popup menu composed of BrowserMenuItem objects. */ class BrowserMenu internal constructor( private val adapter: BrowserMenuAdapter open class BrowserMenu internal constructor( internal val adapter: BrowserMenuAdapter ) { private var currentPopup: PopupWindow? = null protected var currentPopup: PopupWindow? = null private var menuList: RecyclerView? = null /** Loading @@ -41,7 +41,7 @@ class BrowserMenu internal constructor( * the top of the menu is always visible. */ @SuppressLint("InflateParams") fun show( open fun show( anchor: View, orientation: Orientation = DOWN, endOfMenuAlwaysVisible: Boolean = false, Loading @@ -61,7 +61,9 @@ class BrowserMenu internal constructor( menuList?.setAccessibilityDelegate(object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(host, info) info?.collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(adapter.interactiveCount, 0, false) info?.collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain( adapter.interactiveCount, 0, false ) } }) Loading
components/browser/menu/src/main/java/mozilla/components/browser/menu/BrowserMenuBuilder.kt +5 −2 Original line number Diff line number Diff line Loading @@ -14,12 +14,15 @@ import android.content.Context * @param endOfMenuAlwaysVisible when is set to true makes sure the bottom of the menu is always visible otherwise, * the top of the menu is always visible. */ class BrowserMenuBuilder( open class BrowserMenuBuilder( val items: List<BrowserMenuItem>, val extras: Map<String, Any> = emptyMap(), val endOfMenuAlwaysVisible: Boolean = false ) { fun build(context: Context): BrowserMenu { /** * Builds and returns a browser menu with [items] */ open fun build(context: Context): BrowserMenu { val adapter = BrowserMenuAdapter(context, items) return BrowserMenu(adapter) } Loading
components/browser/menu/src/main/java/mozilla/components/browser/menu/WebExtensionBrowserMenu.kt 0 → 100644 +95 −0 Original line number Diff line number Diff line /* 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.browser.menu import android.view.View import android.widget.PopupWindow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.collect import mozilla.components.browser.menu.item.WebExtensionBrowserMenuItem import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.SessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.webextension.BrowserAction import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged typealias WebExtensionBrowserAction = BrowserAction /** * A popup menu for [WebExtensionBrowserAction] menu items. */ class WebExtensionBrowserMenu internal constructor( adapter: BrowserMenuAdapter, private val store: BrowserStore ) : BrowserMenu(adapter) { private var scope: CoroutineScope? = null @kotlinx.coroutines.ExperimentalCoroutinesApi override fun show( anchor: View, orientation: Orientation, endOfMenuAlwaysVisible: Boolean, onDismiss: () -> Unit ): PopupWindow { scope = store.flowScoped { flow -> flow.ifChanged { it.selectedTab } .collect { state -> getOrUpdateWebExtensionMenuItems(state, state.selectedTab) invalidate() } } return super.show( anchor, orientation, endOfMenuAlwaysVisible, onDismiss ).apply { setOnDismissListener { adapter.menu = null currentPopup = null scope?.cancel() webExtensionBrowserActions.clear() onDismiss() } } } companion object { internal val webExtensionBrowserActions = HashMap<String, WebExtensionBrowserMenuItem>() internal fun getOrUpdateWebExtensionMenuItems( state: BrowserState, tab: SessionState? = null ): List<BrowserMenuItem> { val menuItems = ArrayList<BrowserMenuItem>() val extensions = state.extensions.values.toList() extensions.filter { it.enabled }.forEach { extension -> extension.browserAction?.let { browserAction -> // Add the global browser action if it doesn't exist val browserMenuItem = webExtensionBrowserActions.getOrPut(extension.id) { val browserMenuItem = WebExtensionBrowserMenuItem( browserAction = browserAction, listener = browserAction.onClick ) browserMenuItem } // Apply tab-specific override of browser action tab?.extensionState?.get(extension.id)?.browserAction?.let { browserMenuItem.browserAction = browserAction.copyWithOverride(it) } menuItems.add(browserMenuItem) } } return menuItems } } }
components/browser/menu/src/main/java/mozilla/components/browser/menu/WebExtensionBrowserMenuBuilder.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* 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.browser.menu import android.content.Context import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.store.BrowserStore /** * Browser menu builder with web extension support. It allows [WebExtensionBrowserMenu] to add * web extension browser actions. * * @param store [BrowserStore] required to render web extension browser actions * @param appendExtensionActionAtStart true if web extensions appear at the top (start) of the menu, * false if web extensions appear at the bottom of the menu. Default to false (bottom). */ class WebExtensionBrowserMenuBuilder( items: List<BrowserMenuItem>, extras: Map<String, Any> = emptyMap(), endOfMenuAlwaysVisible: Boolean = false, private val store: BrowserStore, private val appendExtensionActionAtStart: Boolean = false ) : BrowserMenuBuilder(items, extras, endOfMenuAlwaysVisible) { /** * Builds and returns a browser menu with combination of [items] and web extension browser actions. */ override fun build(context: Context): BrowserMenu { val extensionMenuItems = WebExtensionBrowserMenu.getOrUpdateWebExtensionMenuItems(store.state, store.state.selectedTab) val menuItems = if (appendExtensionActionAtStart) { extensionMenuItems + items } else { items + extensionMenuItems } val adapter = BrowserMenuAdapter(context, menuItems) return WebExtensionBrowserMenu(adapter, store) } }