Bug 1848527 - Part 1: Enable OutdatedDocumentation detekt rule in Fenix
[gecko.git] / mobile / android / fenix / app / src / main / java / org / mozilla / fenix / customtabs / CustomTabToolbarMenu.kt
blob7863adc050a356f8e80472ecda6d87e8d768e285
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
32 /**
33  * Builds the toolbar object used with the 3-dot menu in the custom tab browser fragment.
34  *
35  * @property context An Android [Context].
36  * @property store reference to the application's [BrowserStore].
37  * @property sessionId ID of the open custom tab session.
38  * @property shouldReverseItems If true, reverse the menu items.
39  * @property isSandboxCustomTab If true, menu should not show the "Open in Firefox" and "POWERED BY FIREFOX" items.
40  * @property onItemTapped Called when a menu item is tapped.
41  */
42 class CustomTabToolbarMenu(
43     private val context: Context,
44     private val store: BrowserStore,
45     private val sessionId: String?,
46     private val shouldReverseItems: Boolean,
47     private val isSandboxCustomTab: Boolean,
48     private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
49 ) : ToolbarMenu {
51     override val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
53     /** Gets the current custom tab session */
54     @VisibleForTesting
55     internal val session: CustomTabSessionState? get() = sessionId?.let { store.state.findCustomTab(it) }
56     private val appName = context.getString(R.string.app_name)
58     override val menuToolbar by lazy {
59         val back = BrowserMenuItemToolbar.TwoStateButton(
60             primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_back_24,
61             primaryContentDescription = context.getString(R.string.browser_menu_back),
62             primaryImageTintResource = primaryTextColor(),
63             isInPrimaryState = {
64                 session?.content?.canGoBack ?: true
65             },
66             secondaryImageTintResource = ThemeManager.resolveAttribute(
67                 R.attr.textDisabled,
68                 context,
69             ),
70             disableInSecondaryState = true,
71             longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Back(viewHistory = true)) },
72         ) {
73             onItemTapped.invoke(ToolbarMenu.Item.Back(viewHistory = false))
74         }
76         val forward = BrowserMenuItemToolbar.TwoStateButton(
77             primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_forward_24,
78             primaryContentDescription = context.getString(R.string.browser_menu_forward),
79             primaryImageTintResource = primaryTextColor(),
80             isInPrimaryState = {
81                 session?.content?.canGoForward ?: true
82             },
83             secondaryImageTintResource = ThemeManager.resolveAttribute(
84                 R.attr.textDisabled,
85                 context,
86             ),
87             disableInSecondaryState = true,
88             longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) },
89         ) {
90             onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
91         }
93         val refresh = BrowserMenuItemToolbar.TwoStateButton(
94             primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_arrow_clockwise_24,
95             primaryContentDescription = context.getString(R.string.browser_menu_refresh),
96             primaryImageTintResource = primaryTextColor(),
97             isInPrimaryState = {
98                 session?.content?.loading == false
99             },
100             secondaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_stop,
101             secondaryContentDescription = context.getString(R.string.browser_menu_stop),
102             secondaryImageTintResource = primaryTextColor(),
103             disableInSecondaryState = false,
104             longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Reload(bypassCache = true)) },
105         ) {
106             if (session?.content?.loading == true) {
107                 onItemTapped.invoke(ToolbarMenu.Item.Stop)
108             } else {
109                 onItemTapped.invoke(ToolbarMenu.Item.Reload(bypassCache = false))
110             }
111         }
113         BrowserMenuItemToolbar(listOf(back, forward, refresh))
114     }
116     private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
117         val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
118         appLink(session.content.url).hasExternalApp()
119     } ?: false
121     private val menuItems by lazy {
122         val menuItems = listOf(
123             poweredBy.apply { visible = { !isSandboxCustomTab } },
124             BrowserMenuDivider().apply { visible = { !isSandboxCustomTab } },
125             desktopMode,
126             findInPage,
127             openInApp.apply { visible = ::shouldShowOpenInApp },
128             openInFenix.apply { visible = { !isSandboxCustomTab } },
129             BrowserMenuDivider(),
130             menuToolbar,
131         )
132         if (shouldReverseItems) {
133             menuItems.reversed()
134         } else {
135             menuItems
136         }
137     }
139     private val desktopMode = BrowserMenuImageSwitch(
140         imageResource = R.drawable.ic_desktop,
141         label = context.getString(R.string.browser_menu_desktop_site),
142         initialState = { session?.content?.desktopMode ?: false },
143     ) { checked ->
144         onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
145     }
147     private val findInPage = BrowserMenuImageText(
148         label = context.getString(R.string.browser_menu_find_in_page),
149         imageResource = R.drawable.mozac_ic_search_24,
150         iconTintColorResource = primaryTextColor(),
151     ) {
152         onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
153     }
155     private val openInApp = BrowserMenuHighlightableItem(
156         label = context.getString(R.string.browser_menu_open_app_link),
157         startImageResource = R.drawable.ic_open_in_app,
158         iconTintColorResource = primaryTextColor(),
159         highlight = BrowserMenuHighlight.LowPriority(
160             label = context.getString(R.string.browser_menu_open_app_link),
161             notificationTint = getColor(context, R.color.fx_mobile_icon_color_information),
162         ),
163         isHighlighted = { !context.settings().openInAppOpened },
164     ) {
165         onItemTapped.invoke(ToolbarMenu.Item.OpenInApp)
166     }
168     private val openInFenix = SimpleBrowserMenuItem(
169         label = context.getString(R.string.browser_menu_open_in_fenix, appName),
170         textColorResource = primaryTextColor(),
171     ) {
172         onItemTapped.invoke(ToolbarMenu.Item.OpenInFenix)
173     }
175     private val poweredBy = BrowserMenuCategory(
176         label = context.getStringWithArgSafe(R.string.browser_menu_powered_by, appName)
177             .uppercase(Locale.getDefault()),
178         textSize = CAPTION_TEXT_SIZE,
179         textColorResource = primaryTextColor(),
180         textStyle = Typeface.NORMAL,
181     )
183     @ColorRes
184     private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.textPrimary, context)
186     companion object {
187         private const val CAPTION_TEXT_SIZE = 12f
188     }