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.components.toolbar
7 import android.content.Context
8 import android.content.res.Configuration
9 import androidx.appcompat.content.res.AppCompatResources
10 import androidx.core.content.ContextCompat
11 import androidx.lifecycle.LifecycleOwner
12 import kotlinx.coroutines.ExperimentalCoroutinesApi
13 import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
14 import mozilla.components.browser.state.selector.normalTabs
15 import mozilla.components.browser.state.selector.privateTabs
16 import mozilla.components.browser.toolbar.BrowserToolbar
17 import mozilla.components.browser.toolbar.display.DisplayToolbar
18 import mozilla.components.concept.engine.Engine
19 import mozilla.components.concept.storage.HistoryStorage
20 import mozilla.components.feature.tabs.toolbar.TabCounterToolbarButton
21 import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
22 import mozilla.components.feature.toolbar.ToolbarBehaviorController
23 import mozilla.components.feature.toolbar.ToolbarFeature
24 import mozilla.components.feature.toolbar.ToolbarPresenter
25 import mozilla.components.support.base.feature.LifecycleAwareFeature
26 import mozilla.components.support.ktx.android.view.hideKeyboard
27 import org.mozilla.fenix.R
28 import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor
29 import org.mozilla.fenix.ext.components
30 import org.mozilla.fenix.ext.settings
31 import org.mozilla.fenix.theme.ThemeManager
33 @ExperimentalCoroutinesApi
34 abstract class ToolbarIntegration(
36 toolbar: BrowserToolbar,
37 toolbarMenu: ToolbarMenu,
40 renderStyle: ToolbarFeature.RenderStyle
41 ) : LifecycleAwareFeature {
43 val store = context.components.core.store
44 private val toolbarPresenter: ToolbarPresenter = ToolbarPresenter(
48 ToolbarFeature.UrlRenderConfiguration(
49 context.components.publicSuffixList,
50 ThemeManager.resolveAttribute(R.attr.primaryText, context),
51 renderStyle = renderStyle
55 private val menuPresenter =
56 MenuPresenter(toolbar, context.components.core.store, sessionId)
58 private val toolbarController = ToolbarBehaviorController(toolbar, store, sessionId)
61 toolbar.display.menuBuilder = toolbarMenu.menuBuilder
62 toolbar.private = isPrivate
65 override fun start() {
67 toolbarPresenter.start()
68 toolbarController.start()
73 toolbarPresenter.stop()
74 toolbarController.stop()
77 fun invalidateMenu() {
78 menuPresenter.invalidateActions()
82 @ExperimentalCoroutinesApi
83 class DefaultToolbarIntegration(
85 toolbar: BrowserToolbar,
86 toolbarMenu: ToolbarMenu,
87 domainAutocompleteProvider: DomainAutocompleteProvider,
88 historyStorage: HistoryStorage,
89 lifecycleOwner: LifecycleOwner,
90 sessionId: String? = null,
92 interactor: BrowserToolbarInteractor,
94 ) : ToolbarIntegration(
97 toolbarMenu = toolbarMenu,
98 sessionId = sessionId,
99 isPrivate = isPrivate,
100 renderStyle = ToolbarFeature.RenderStyle.UncoloredUrl
104 toolbar.display.menuBuilder = toolbarMenu.menuBuilder
105 toolbar.private = isPrivate
108 if (isPrivate) AppCompatResources.getDrawable(
110 R.drawable.shield_dark
111 ) else when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
112 Configuration.UI_MODE_NIGHT_UNDEFINED, // We assume light here per Android doc's recommendation
113 Configuration.UI_MODE_NIGHT_NO -> {
114 AppCompatResources.getDrawable(context, R.drawable.shield_light)
116 Configuration.UI_MODE_NIGHT_YES -> {
117 AppCompatResources.getDrawable(context, R.drawable.shield_dark)
119 else -> AppCompatResources.getDrawable(context, R.drawable.shield_light)
122 toolbar.display.indicators =
123 if (context.settings().shouldUseTrackingProtection) {
125 DisplayToolbar.Indicators.TRACKING_PROTECTION,
126 DisplayToolbar.Indicators.SECURITY,
127 DisplayToolbar.Indicators.EMPTY,
128 DisplayToolbar.Indicators.HIGHLIGHT
132 DisplayToolbar.Indicators.SECURITY,
133 DisplayToolbar.Indicators.EMPTY,
134 DisplayToolbar.Indicators.HIGHLIGHT
137 context.settings().shouldUseTrackingProtection
139 toolbar.display.icons = toolbar.display.icons.copy(
141 trackingProtectionTrackersBlocked = drawable!!,
142 trackingProtectionNothingBlocked = AppCompatResources.getDrawable(
144 R.drawable.ic_tracking_protection_enabled
146 trackingProtectionException = AppCompatResources.getDrawable(
148 R.drawable.ic_tracking_protection_disabled
152 val tabCounterMenu = FenixTabCounterMenu(
155 interactor.onTabCounterMenuItemTapped(it)
159 ContextCompat.getColor(context, R.color.primary_text_private_theme)
164 it.updateMenu(context.settings().toolbarPosition)
167 val tabsAction = TabCounterToolbarButton(
168 lifecycleOwner = lifecycleOwner,
170 toolbar.hideKeyboard()
171 interactor.onTabCounterClicked()
174 menu = tabCounterMenu
177 val tabCount = if (isPrivate) {
178 store.state.privateTabs.size
180 store.state.normalTabs.size
183 tabsAction.updateCount(tabCount)
185 toolbar.addBrowserAction(tabsAction)
187 val engineForSpeculativeConnects = if (!isPrivate) engine else null
188 ToolbarAutocompleteFeature(
190 engineForSpeculativeConnects
192 addDomainProvider(domainAutocompleteProvider)
193 if (context.settings().shouldShowHistorySuggestions) {
194 addHistoryStorageProvider(historyStorage)