1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.fenix.customtabs
7 import android.content.Context
8 import android.graphics.Typeface
9 import androidx.annotation.ColorRes
10 import androidx.annotation.VisibleForTesting
11 import androidx.core.content.ContextCompat.getColor
12 import mozilla.components.browser.menu.BrowserMenuBuilder
13 import mozilla.components.browser.menu.BrowserMenuHighlight
14 import mozilla.components.browser.menu.item.BrowserMenuCategory
15 import mozilla.components.browser.menu.item.BrowserMenuDivider
16 import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
17 import mozilla.components.browser.menu.item.BrowserMenuImageSwitch
18 import mozilla.components.browser.menu.item.BrowserMenuImageText
19 import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
20 import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
21 import mozilla.components.browser.state.selector.findCustomTab
22 import mozilla.components.browser.state.state.CustomTabSessionState
23 import mozilla.components.browser.state.store.BrowserStore
24 import org.mozilla.fenix.R
25 import org.mozilla.fenix.components.toolbar.ToolbarMenu
26 import org.mozilla.fenix.ext.components
27 import org.mozilla.fenix.ext.getStringWithArgSafe
28 import org.mozilla.fenix.ext.settings
29 import org.mozilla.fenix.theme.ThemeManager
30 import java.util.Locale
33 * Builds the toolbar object used with the 3-dot menu in the custom tab browser fragment.
34 * @param store reference to the application's [BrowserStore].
35 * @param sessionId ID of the open custom tab session.
36 * @param shouldReverseItems If true, reverse the menu items.
37 * @param onItemTapped Called when a menu item is tapped.
39 class CustomTabToolbarMenu(
40 private val context: Context,
41 private val store: BrowserStore,
42 private val sessionId: String?,
43 private val shouldReverseItems: Boolean,
44 private val onItemTapped: (ToolbarMenu.Item) -> Unit = {}
47 override val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
49 /** Gets the current custom tab session */
51 internal val session: CustomTabSessionState? get() = sessionId?.let { store.state.findCustomTab(it) }
52 private val appName = context.getString(R.string.app_name)
54 override val menuToolbar by lazy {
55 val back = BrowserMenuItemToolbar.TwoStateButton(
56 primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_back,
57 primaryContentDescription = context.getString(R.string.browser_menu_back),
58 primaryImageTintResource = primaryTextColor(),
60 session?.content?.canGoBack ?: true
62 secondaryImageTintResource = ThemeManager.resolveAttribute(
66 disableInSecondaryState = true,
67 longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Back(viewHistory = true)) }
69 onItemTapped.invoke(ToolbarMenu.Item.Back(viewHistory = false))
72 val forward = BrowserMenuItemToolbar.TwoStateButton(
73 primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
74 primaryContentDescription = context.getString(R.string.browser_menu_forward),
75 primaryImageTintResource = primaryTextColor(),
77 session?.content?.canGoForward ?: true
79 secondaryImageTintResource = ThemeManager.resolveAttribute(
83 disableInSecondaryState = true,
84 longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
86 onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
89 val refresh = BrowserMenuItemToolbar.TwoStateButton(
90 primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
91 primaryContentDescription = context.getString(R.string.browser_menu_refresh),
92 primaryImageTintResource = primaryTextColor(),
94 session?.content?.loading == false
96 secondaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_stop,
97 secondaryContentDescription = context.getString(R.string.browser_menu_stop),
98 secondaryImageTintResource = primaryTextColor(),
99 disableInSecondaryState = false,
100 longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Reload(bypassCache = true)) }
102 if (session?.content?.loading == true) {
103 onItemTapped.invoke(ToolbarMenu.Item.Stop)
105 onItemTapped.invoke(ToolbarMenu.Item.Reload(bypassCache = false))
109 BrowserMenuItemToolbar(listOf(back, forward, refresh))
112 private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
113 val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
114 appLink(session.content.url).hasExternalApp()
117 private val menuItems by lazy {
118 val menuItems = listOf(
120 BrowserMenuDivider(),
123 openInApp.apply { visible = ::shouldShowOpenInApp },
125 BrowserMenuDivider(),
128 if (shouldReverseItems) {
135 private val desktopMode = BrowserMenuImageSwitch(
136 imageResource = R.drawable.ic_desktop,
137 label = context.getString(R.string.browser_menu_desktop_site),
138 initialState = { session?.content?.desktopMode ?: false }
140 onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
143 private val findInPage = BrowserMenuImageText(
144 label = context.getString(R.string.browser_menu_find_in_page),
145 imageResource = R.drawable.mozac_ic_search,
146 iconTintColorResource = primaryTextColor()
148 onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
151 private val openInApp = BrowserMenuHighlightableItem(
152 label = context.getString(R.string.browser_menu_open_app_link),
153 startImageResource = R.drawable.ic_open_in_app,
154 iconTintColorResource = primaryTextColor(),
155 highlight = BrowserMenuHighlight.LowPriority(
156 label = context.getString(R.string.browser_menu_open_app_link),
157 notificationTint = getColor(context, R.color.fx_mobile_icon_color_information)
159 isHighlighted = { !context.settings().openInAppOpened }
161 onItemTapped.invoke(ToolbarMenu.Item.OpenInApp)
164 private val openInFenix = SimpleBrowserMenuItem(
165 label = context.getString(R.string.browser_menu_open_in_fenix, appName),
166 textColorResource = primaryTextColor()
168 onItemTapped.invoke(ToolbarMenu.Item.OpenInFenix)
171 private val poweredBy = BrowserMenuCategory(
172 label = context.getStringWithArgSafe(R.string.browser_menu_powered_by, appName)
173 .uppercase(Locale.getDefault()),
174 textSize = CAPTION_TEXT_SIZE,
175 textColorResource = primaryTextColor(),
176 textStyle = Typeface.NORMAL
180 private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.textPrimary, context)
183 private const val CAPTION_TEXT_SIZE = 12f