@ -7,6 +7,7 @@ package org.mozilla.fenix.components.toolbar
import android.content.Context
import androidx.annotation.ColorRes
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.core.content.ContextCompat.getColor
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
@ -53,11 +54,10 @@ import org.mozilla.fenix.theme.ThemeManager
* /
@Suppress ( " LargeClass " , " LongParameterList " )
@ExperimentalCoroutinesApi
class DefaultToolbarMenu (
open class DefaultToolbarMenu (
private val context : Context ,
private val store : BrowserStore ,
hasAccountProblem : Boolean = false ,
shouldReverseItems : Boolean ,
private val onItemTapped : ( ToolbarMenu . Item ) -> Unit = { } ,
private val lifecycleOwner : LifecycleOwner ,
private val bookmarksStorage : BookmarksStorage ,
@ -66,10 +66,15 @@ class DefaultToolbarMenu(
private var isCurrentUrlBookmarked = false
private var isBookmarkedJob : Job ? = null
private val isTopToolbarSelected = shouldReverseItems
private val shouldUseBottomToolbar = context . settings ( ) . shouldUseBottomToolbar
private val selectedSession : TabSessionState ?
get ( ) = store . state . selectedTab
private val primaryTextColor =
ThemeManager . resolveAttribute ( R . attr . primaryText , context )
override val menuBuilder by lazy {
WebExtensionBrowserMenuBuilder (
items =
@ -78,13 +83,13 @@ class DefaultToolbarMenu(
} else {
oldCoreMenuItems
} ,
endOfMenuAlwaysVisible = ! should ReverseItems ,
endOfMenuAlwaysVisible = ! should UseBottomToolbar ,
store = store ,
webExtIconTintColorResource = primaryTextColor () ,
webExtIconTintColorResource = primaryTextColor ,
onAddonsManagerTapped = {
onItemTapped . invoke ( ToolbarMenu . Item . AddonsManager )
} ,
appendExtensionSubMenuAtStart = ! should ReverseItems
appendExtensionSubMenuAtStart = ! should UseBottomToolbar
)
}
@ -92,7 +97,7 @@ class DefaultToolbarMenu(
val back = BrowserMenuItemToolbar . TwoStateButton (
primaryImageResource = mozilla . components . ui . icons . R . drawable . mozac _ic _back ,
primaryContentDescription = context . getString ( R . string . browser _menu _back ) ,
primaryImageTintResource = primaryTextColor () ,
primaryImageTintResource = primaryTextColor ,
isInPrimaryState = {
selectedSession ?. content ?. canGoBack ?: true
} ,
@ -106,7 +111,7 @@ class DefaultToolbarMenu(
val forward = BrowserMenuItemToolbar . TwoStateButton (
primaryImageResource = mozilla . components . ui . icons . R . drawable . mozac _ic _forward ,
primaryContentDescription = context . getString ( R . string . browser _menu _forward ) ,
primaryImageTintResource = primaryTextColor () ,
primaryImageTintResource = primaryTextColor ,
isInPrimaryState = {
selectedSession ?. content ?. canGoForward ?: true
} ,
@ -120,13 +125,13 @@ class DefaultToolbarMenu(
val refresh = BrowserMenuItemToolbar . TwoStateButton (
primaryImageResource = mozilla . components . ui . icons . R . drawable . mozac _ic _refresh ,
primaryContentDescription = context . getString ( R . string . browser _menu _refresh ) ,
primaryImageTintResource = primaryTextColor () ,
primaryImageTintResource = primaryTextColor ,
isInPrimaryState = {
selectedSession ?. content ?. loading == false
} ,
secondaryImageResource = mozilla . components . ui . icons . R . drawable . mozac _ic _stop ,
secondaryContentDescription = context . getString ( R . string . browser _menu _stop ) ,
secondaryImageTintResource = primaryTextColor () ,
secondaryImageTintResource = primaryTextColor ,
disableInSecondaryState = false ,
longClickListener = { onItemTapped . invoke ( ToolbarMenu . Item . Reload ( bypassCache = true ) ) }
) {
@ -140,7 +145,7 @@ class DefaultToolbarMenu(
val share = BrowserMenuItemToolbar . Button (
imageResource = R . drawable . ic _share _filled ,
contentDescription = context . getString ( R . string . browser _menu _share ) ,
iconTintColorResource = primaryTextColor () ,
iconTintColorResource = primaryTextColor ,
listener = {
onItemTapped . invoke ( ToolbarMenu . Item . Share )
}
@ -154,14 +159,14 @@ class DefaultToolbarMenu(
val bookmark = BrowserMenuItemToolbar . TwoStateButton (
primaryImageResource = R . drawable . ic _bookmark _filled ,
primaryContentDescription = context . getString ( R . string . browser _menu _edit _bookmark ) ,
primaryImageTintResource = primaryTextColor () ,
primaryImageTintResource = primaryTextColor ,
// TwoStateButton.isInPrimaryState must be synchronous, and checking bookmark state is
// relatively slow. The best we can do here is periodically compute and cache a new "is
// bookmarked" state, and use that whenever the menu has been opened.
isInPrimaryState = { isCurrentUrlBookmarked } ,
secondaryImageResource = R . drawable . ic _bookmark _outline ,
secondaryContentDescription = context . getString ( R . string . browser _menu _bookmark ) ,
secondaryImageTintResource = primaryTextColor () ,
secondaryImageTintResource = primaryTextColor ,
disableInSecondaryState = false
) {
handleBookmarkItemTapped ( )
@ -172,20 +177,24 @@ class DefaultToolbarMenu(
}
// Predicates that need to be repeatedly called as the session changes
private fun canAddToHomescreen ( ) : Boolean =
@VisibleForTesting ( otherwise = PRIVATE )
fun canAddToHomescreen ( ) : Boolean =
selectedSession != null && isPinningSupported &&
! context . components . useCases . webAppUseCases . isInstallable ( )
private fun canInstall ( ) : Boolean =
@VisibleForTesting ( otherwise = PRIVATE )
fun canInstall ( ) : Boolean =
selectedSession != null && isPinningSupported &&
context . components . useCases . webAppUseCases . isInstallable ( )
private fun shouldShowOpenInApp ( ) : Boolean = selectedSession ?. let { session ->
@VisibleForTesting ( otherwise = PRIVATE )
fun shouldShowOpenInApp ( ) : Boolean = selectedSession ?. let { session ->
val appLink = context . components . useCases . appLinksUseCases . appLinkRedirect
appLink ( session . content . url ) . hasExternalApp ( )
} ?: false
private fun shouldShowReaderViewCustomization ( ) : Boolean = selectedSession ?. let {
@VisibleForTesting ( otherwise = PRIVATE )
fun shouldShowReaderViewCustomization ( ) : Boolean = selectedSession ?. let {
store . state . findTab ( it . id ) ?. readerState ?. active
} ?: false
// End of predicates //
@ -196,10 +205,10 @@ class DefaultToolbarMenu(
startImageResource = R . drawable . ic _settings ,
iconTintColorResource = if ( hasAccountProblem )
ThemeManager . resolveAttribute ( R . attr . syncDisconnected , context ) else
primaryTextColor () ,
primaryTextColor ,
textColorResource = if ( hasAccountProblem )
ThemeManager . resolveAttribute ( R . attr . primaryText , context ) else
primaryTextColor () ,
primaryTextColor ,
highlight = BrowserMenuHighlight . HighPriority (
endImageResource = R . drawable . ic _sync _disconnected ,
backgroundTint = context . getColorFromAttr ( R . attr . syncDisconnectedBackground ) ,
@ -223,7 +232,7 @@ class DefaultToolbarMenu(
val addToTopSites = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _top _sites ) ,
imageResource = R . drawable . ic _top _sites ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToTopSites )
}
@ -231,7 +240,7 @@ class DefaultToolbarMenu(
val addToHomescreen = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _homescreen ) ,
imageResource = R . drawable . ic _add _to _homescreen ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToHomeScreen )
}
@ -239,7 +248,7 @@ class DefaultToolbarMenu(
val syncedTabs = BrowserMenuImageText (
label = context . getString ( R . string . synced _tabs ) ,
imageResource = R . drawable . ic _synced _tabs ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . SyncedTabs )
}
@ -247,7 +256,7 @@ class DefaultToolbarMenu(
val installToHomescreen = BrowserMenuHighlightableItem (
label = context . getString ( R . string . browser _menu _install _on _homescreen ) ,
startImageResource = R . drawable . ic _add _to _homescreen ,
iconTintColorResource = primaryTextColor () ,
iconTintColorResource = primaryTextColor ,
highlight = BrowserMenuHighlight . LowPriority (
label = context . getString ( R . string . browser _menu _install _on _homescreen ) ,
notificationTint = getColor ( context , R . color . whats _new _notification _color )
@ -262,7 +271,7 @@ class DefaultToolbarMenu(
val findInPage = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _find _in _page ) ,
imageResource = R . drawable . mozac _ic _search ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . FindInPage )
}
@ -274,7 +283,7 @@ class DefaultToolbarMenu(
val saveToCollection = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _save _to _collection _2 ) ,
imageResource = R . drawable . ic _tab _collection ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . SaveToCollection )
}
@ -282,7 +291,7 @@ class DefaultToolbarMenu(
val deleteDataOnQuit = BrowserMenuImageText (
label = context . getString ( R . string . delete _browsing _data _on _quit _action ) ,
imageResource = R . drawable . ic _exit ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . Quit )
}
@ -290,7 +299,7 @@ class DefaultToolbarMenu(
val readerAppearance = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _read _appearance ) ,
imageResource = R . drawable . ic _readermode _appearance ,
iconTintColorResource = primaryTextColor ( )
iconTintColorResource = primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . CustomizeReaderView )
}
@ -298,7 +307,7 @@ class DefaultToolbarMenu(
val openInApp = BrowserMenuHighlightableItem (
label = context . getString ( R . string . browser _menu _open _app _link ) ,
startImageResource = R . drawable . ic _open _in _app ,
iconTintColorResource = primaryTextColor () ,
iconTintColorResource = primaryTextColor ,
highlight = BrowserMenuHighlight . LowPriority (
label = context . getString ( R . string . browser _menu _open _app _link ) ,
notificationTint = getColor ( context , R . color . whats _new _notification _color )
@ -311,7 +320,7 @@ class DefaultToolbarMenu(
val historyItem = BrowserMenuImageText (
context . getString ( R . string . library _history ) ,
R . drawable . ic _history ,
primaryTextColor ( )
primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . History )
}
@ -319,7 +328,7 @@ class DefaultToolbarMenu(
val bookmarksItem = BrowserMenuImageText (
context . getString ( R . string . library _bookmarks ) ,
R . drawable . ic _bookmark _filled ,
primaryTextColor ( )
primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . Bookmarks )
}
@ -327,7 +336,7 @@ class DefaultToolbarMenu(
val downloadsItem = BrowserMenuImageText (
context . getString ( R . string . library _downloads ) ,
R . drawable . ic _download ,
primaryTextColor ( )
primaryTextColor
) {
onItemTapped . invoke ( ToolbarMenu . Item . Downloads )
}
@ -359,154 +368,154 @@ class DefaultToolbarMenu(
menuToolbar
)
if ( shouldReverseItems ) {
menuItems . reversed ( )
} else {
if ( shouldUseBottomToolbar ) {
menuItems
} else {
menuItems . reversed ( )
}
}
val newTabItem = BrowserMenuImageText (
context . getString ( R . string . library _new _tab ) ,
R . drawable . ic _new ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . NewTab )
}
private val newCoreMenuItems by lazy {
val newTabItem = BrowserMenuImageText (
context . getString ( R . string . library _new _tab ) ,
R . drawable . ic _new ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . NewTab )
}
val historyItem = BrowserMenuImageText (
context . getString ( R . string . library _history ) ,
R . drawable . ic _history ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . History )
}
val historyItem = BrowserMenuImageText (
context . getString ( R . string . library _history ) ,
R . drawable . ic _history ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . History )
}
val downloadsItem = BrowserMenuImageText (
context . getString ( R . string . library _downloads ) ,
R . drawable . ic _download ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . Downloads )
}
val downloadsItem = BrowserMenuImageText (
context . getString ( R . string . library _downloads ) ,
R . drawable . ic _download ,
primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . Downloads )
}
val extensionsItem = WebExtensionPlaceholderMenuItem (
id = WebExtensionPlaceholderMenuItem . MAIN _EXTENSIONS _MENU _ID
)
val extensionsItem = WebExtensionPlaceholderMenuItem (
id = WebExtensionPlaceholderMenuItem . MAIN _EXTENSIONS _MENU _ID
)
val syncedTabs = BrowserMenuImageText (
label = context . getString ( R . string . synced _tabs ) ,
imageResource = R . drawable . ic _synced _tabs ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . SyncedTabs )
}
val syncedTabs = BrowserMenuImageText (
label = context . getString ( R . string . synced _tabs ) ,
imageResource = R . drawable . ic _synced _tabs ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . SyncedTabs )
}
val findInPageItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _find _in _page ) ,
imageResource = R . drawable . mozac _ic _search ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . FindInPage )
}
val findInPageItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _find _in _page ) ,
imageResource = R . drawable . mozac _ic _search ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . FindInPage )
}
val desktopSiteItem = BrowserMenuImageSwitch (
imageResource = R . drawable . ic _desktop ,
label = context . getString ( R . string . browser _menu _desktop _site ) ,
initialState = {
selectedSession ?. content ?. desktopMode ?: false
}
) { checked ->
onItemTapped . invoke ( ToolbarMenu . Item . RequestDesktop ( checked ) )
val desktopSiteItem = BrowserMenuImageSwitch (
imageResource = R . drawable . ic _desktop ,
label = context . getString ( R . string . browser _menu _desktop _site ) ,
initialState = {
selectedSession ?. content ?. desktopMode ?: false
}
) { checked ->
onItemTapped . invoke ( ToolbarMenu . Item . RequestDesktop ( checked ) )
}
val customizeReaderView = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _customize _reader _view ) ,
imageResource = R . drawable . ic _readermode _appearance ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . CustomizeReaderView )
}
val customizeReaderView = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _customize _reader _view ) ,
imageResource = R . drawable . ic _readermode _appearance ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . CustomizeReaderView )
}
val openInApp = BrowserMenuHighlightableItem (
val openInApp = BrowserMenuHighlightableItem (
label = context . getString ( R . string . browser _menu _open _app _link ) ,
startImageResource = R . drawable . ic _open _in _app ,
iconTintColorResource = primaryTextColor ( ) ,
highlight = BrowserMenuHighlight . LowPriority (
label = context . getString ( R . string . browser _menu _open _app _link ) ,
startImageResource = R . drawable . ic _open _in _app ,
iconTintColorResource = primaryTextColor ( ) ,
highlight = BrowserMenuHighlight . LowPriority (
label = context . getString ( R . string . browser _menu _open _app _link ) ,
notificationTint = getColor ( context , R . color . whats _new _notification _color )
) ,
isHighlighted = { ! context . settings ( ) . openInAppOpened }
) {
onItemTapped . invoke ( ToolbarMenu . Item . OpenInApp )
}
notificationTint = getColor ( context , R . color . whats _new _notification _color )
) ,
isHighlighted = { ! context . settings ( ) . openInAppOpened }
) {
onItemTapped . invoke ( ToolbarMenu . Item . OpenInApp )
}
val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem (
id = WebCompatReporterFeature . WEBCOMPAT _REPORTER _EXTENSION _ID
)
val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem (
id = WebCompatReporterFeature . WEBCOMPAT _REPORTER _EXTENSION _ID
)
val addToHomeScreenItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _homescreen ) ,
imageResource = R . drawable . ic _add _to _homescreen ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToHomeScreen )
}
val addToHomeScreenItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _homescreen ) ,
imageResource = R . drawable . ic _add _to _homescreen ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToHomeScreen )
}
val addToTopSitesItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _top _sites ) ,
imageResource = R . drawable . ic _top _sites ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToTopSites )
}
val addToTopSitesItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _add _to _top _sites ) ,
imageResource = R . drawable . ic _top _sites ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . AddToTopSites )
}
val saveToCollectionItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _save _to _collection _2 ) ,
imageResource = R . drawable . ic _tab _collection ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . SaveToCollection )
}
val saveToCollectionItem = BrowserMenuImageText (
label = context . getString ( R . string . browser _menu _save _to _collection _2 ) ,
imageResource = R . drawable . ic _tab _collection ,
iconTintColorResource = primaryTextColor ( )
) {
onItemTapped . invoke ( ToolbarMenu . Item . SaveToCollection )
}
val settingsItem = BrowserMenuHighlightableItem (
label = context . getString ( R . string . browser _menu _settings ) ,
startImageResource = R . drawable . ic _settings ,
iconTintColorResource = primaryTextColor ( ) ,
textColorResource = if ( hasAccountProblem )
ThemeManager . resolveAttribute ( R . attr . primaryText , context ) else
primaryTextColor ( ) ,
highlight = BrowserMenuHighlight . HighPriority (
endImageResource = R . drawable . ic _sync _disconnected ,
backgroundTint = context . getColorFromAttr ( R . attr . syncDisconnectedBackground ) ,
canPropagate = false
) ,
isHighlighted = { hasAccountProblem }
) {
onItemTapped . invoke ( ToolbarMenu . Item . Settings )
}
val settingsItem = BrowserMenuHighlightableItem (
label = context . getString ( R . string . browser _menu _settings ) ,
startImageResource = R . drawable . ic _settings ,
iconTintColorResource = primaryTextColor ( ) ,
textColorResource = if ( hasAccountProblem )
ThemeManager . resolveAttribute ( R . attr . primaryText , context ) else
primaryTextColor ,
highlight = BrowserMenuHighlight . HighPriority (
endImageResource = R . drawable . ic _sync _disconnected ,
backgroundTint = context . getColorFromAttr ( R . attr . syncDisconnectedBackground ) ,
canPropagate = false
) ,
isHighlighted = { hasAccountProblem }
) {
onItemTapped . invoke ( ToolbarMenu . Item . Settings )
}
val bookmarksItem = BrowserMenuImageTextCheckboxButton (
imageResource = R . drawable . ic _bookmarks _menu ,
iconTintColorResource = primaryTextColor ( ) ,
label = context . getString ( R . string . library _bookmarks ) ,
labelListener = {
onItemTapped . invoke ( ToolbarMenu . Item . Bookmarks )
} ,
primaryStateIconResource = R . drawable . ic _bookmark _outline ,
secondaryStateIconResource = R . drawable . ic _bookmark _filled ,
tintColorResource = accentBrightTextColor ( ) ,
primaryLabel = context . getString ( R . string . add _label ) ,
secondaryLabel = context . getString ( R . string . edit _label ) ,
isInPrimaryState = { !is CurrentUrlBookmarked }
) {
handleBookmarkItemTapped ( )
}
val bookmarksItem = BrowserMenuImageTextCheckboxButton (
imageResource = R . drawable . ic _bookmarks _menu ,
iconTintColorResource = primaryTextColor ( ) ,
label = context . getString ( R . string . library _bookmarks ) ,
labelListener = {
onItemTapped . invoke ( ToolbarMenu . Item . Bookmarks )
} ,
primaryStateIconResource = R . drawable . ic _bookmark _outline ,
secondaryStateIconResource = R . drawable . ic _bookmark _filled ,
tintColorResource = accentBrightTextColor ( ) ,
primaryLabel = context . getString ( R . string . add _label ) ,
secondaryLabel = context . getString ( R . string . edit _label ) ,
isInPrimaryState = { !is CurrentUrlBookmarked }
) {
handleBookmarkItemTapped ( )
}
@VisibleForTesting ( otherwise = PRIVATE )
val newCoreMenuItems by lazy {
val menuItems =
listOfNotNull (
if ( isTopToolbarSelected) menuToolbar else null ,
if ( shouldUseBottomToolbar) null else menuToolbar ,
newTabItem ,
BrowserMenuDivider ( ) ,
bookmarksItem ,
@ -526,8 +535,8 @@ class DefaultToolbarMenu(
saveToCollectionItem ,
BrowserMenuDivider ( ) ,
settingsItem ,
if ( isTopToolbarSelected) null else BrowserMenuDivider ( ) ,
if ( isTopToolbarSelected) null else menuToolbar
if ( shouldUseBottomToolbar) BrowserMenuDivider ( ) else null ,
if ( shouldUseBottomToolbar) menuToolbar else null
)
menuItems