For #11045: Add reader mode to urlView
parent
1a19b06227
commit
e9189dc089
|
@ -10,6 +10,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
@ -17,6 +18,8 @@ import kotlinx.android.synthetic.main.fragment_browser.*
|
|||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.state.selector.findTab
|
||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||
import mozilla.components.feature.app.links.AppLinksUseCases
|
||||
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
||||
import mozilla.components.feature.readerview.ReaderViewFeature
|
||||
|
@ -52,6 +55,8 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
|||
private val windowFeature = ViewBoundFeatureWrapper<WindowFeature>()
|
||||
private val searchFeature = ViewBoundFeatureWrapper<SearchFeature>()
|
||||
|
||||
private var readerModeAvailable = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -68,17 +73,39 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
|||
val components = context.components
|
||||
|
||||
return super.initializeUI(view)?.also {
|
||||
val readerModeAction =
|
||||
BrowserToolbar.ToggleButton(
|
||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_readermode)!!,
|
||||
imageSelected = ContextCompat.getDrawable(requireContext(), R.drawable.ic_readermode_selected)!!,
|
||||
contentDescription = requireContext().getString(R.string.browser_menu_read),
|
||||
contentDescriptionSelected = requireContext().getString(R.string.browser_menu_read_close),
|
||||
visible = {
|
||||
readerModeAvailable
|
||||
},
|
||||
selected = getSessionById()?.let {
|
||||
activity?.components?.core?.store?.state?.findTab(it.id)?.readerState?.active
|
||||
} ?: false,
|
||||
listener = browserInteractor::onReaderModePressed
|
||||
)
|
||||
|
||||
browserToolbarView.view.addPageAction(readerModeAction)
|
||||
|
||||
readerViewFeature.set(
|
||||
feature = ReaderViewFeature(
|
||||
context,
|
||||
components.core.engine,
|
||||
components.core.store,
|
||||
view.readerViewControlsBar
|
||||
) { available, _ ->
|
||||
) { available, active ->
|
||||
if (available) {
|
||||
components.analytics.metrics.track(Event.ReaderModeAvailable)
|
||||
}
|
||||
|
||||
readerModeAvailable = available
|
||||
readerModeAction.setSelected(active)
|
||||
|
||||
runIfFragmentIsAttached {
|
||||
browserToolbarView.view.invalidateActions()
|
||||
browserToolbarView.toolbarIntegration.invalidateMenu()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -35,4 +35,8 @@ open class BrowserInteractor(
|
|||
override fun onScrolled(offset: Int) {
|
||||
browserToolbarController.handleScroll(offset)
|
||||
}
|
||||
|
||||
override fun onReaderModePressed(enabled: Boolean) {
|
||||
browserToolbarController.handleReaderModePressed(enabled)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,9 +53,10 @@ interface BrowserToolbarController {
|
|||
fun handleToolbarClick()
|
||||
fun handleTabCounterClick()
|
||||
fun handleBrowserMenuDismissed(lowPrioHighlightItems: List<ToolbarMenu.Item>)
|
||||
fun handleReaderModePressed(enabled: Boolean)
|
||||
}
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Suppress("LargeClass", "TooManyFunctions")
|
||||
class DefaultBrowserToolbarController(
|
||||
private val activity: Activity,
|
||||
private val navController: NavController,
|
||||
|
@ -124,6 +125,14 @@ class DefaultBrowserToolbarController(
|
|||
onTabCounterClicked.invoke()
|
||||
}
|
||||
|
||||
override fun handleReaderModePressed(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
readerModeController.showReaderView()
|
||||
} else {
|
||||
readerModeController.hideReaderView()
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleBrowserMenuDismissed(lowPrioHighlightItems: List<ToolbarMenu.Item>) {
|
||||
lowPrioHighlightItems.forEach {
|
||||
when (it) {
|
||||
|
|
|
@ -52,6 +52,7 @@ interface BrowserToolbarViewInteractor {
|
|||
fun onTabCounterClicked()
|
||||
fun onBrowserMenuDismissed(lowPrioHighlightItems: List<ToolbarMenu.Item>)
|
||||
fun onScrolled(offset: Int)
|
||||
fun onReaderModePressed(enabled: Boolean)
|
||||
}
|
||||
@SuppressWarnings("LargeClass")
|
||||
class BrowserToolbarView(
|
||||
|
|
|
@ -15,7 +15,6 @@ import mozilla.components.browser.menu.BrowserMenuHighlight
|
|||
import mozilla.components.browser.menu.WebExtensionBrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.BrowserMenuDivider
|
||||
import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
|
||||
import mozilla.components.browser.menu.item.BrowserMenuHighlightableSwitch
|
||||
import mozilla.components.browser.menu.item.BrowserMenuImageSwitch
|
||||
import mozilla.components.browser.menu.item.BrowserMenuImageText
|
||||
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
|
||||
|
@ -143,9 +142,6 @@ class DefaultToolbarMenu(
|
|||
if (canInstall() && installToHomescreen.isHighlighted()) {
|
||||
lowPrioHighlightItems.add(ToolbarMenu.Item.InstallToHomeScreen)
|
||||
}
|
||||
if (shouldShowReaderMode() && readerMode.isHighlighted()) {
|
||||
lowPrioHighlightItems.add(ToolbarMenu.Item.ReaderMode(false))
|
||||
}
|
||||
if (shouldShowOpenInApp() && openInApp.isHighlighted()) {
|
||||
lowPrioHighlightItems.add(ToolbarMenu.Item.OpenInApp)
|
||||
}
|
||||
|
@ -161,10 +157,6 @@ class DefaultToolbarMenu(
|
|||
session != null && context.components.useCases.webAppUseCases.isPinningSupported() &&
|
||||
context.components.useCases.webAppUseCases.isInstallable()
|
||||
|
||||
private fun shouldShowReaderMode(): Boolean = session?.let {
|
||||
store.state.findTab(it.id)?.readerState?.readerable
|
||||
} ?: false
|
||||
|
||||
private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
|
||||
val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
|
||||
appLink(session.url).hasExternalApp()
|
||||
|
@ -196,7 +188,6 @@ class DefaultToolbarMenu(
|
|||
if (shouldShowSaveToCollection) saveToCollection else null,
|
||||
desktopMode,
|
||||
openInApp.apply { visible = ::shouldShowOpenInApp },
|
||||
readerMode.apply { visible = ::shouldShowReaderMode },
|
||||
readerAppearance.apply { visible = ::shouldShowReaderAppearance },
|
||||
BrowserMenuDivider(),
|
||||
menuToolbar
|
||||
|
@ -301,23 +292,6 @@ class DefaultToolbarMenu(
|
|||
onItemTapped.invoke(ToolbarMenu.Item.Quit)
|
||||
}
|
||||
|
||||
private val readerMode = BrowserMenuHighlightableSwitch(
|
||||
label = context.getString(R.string.browser_menu_read),
|
||||
startImageResource = R.drawable.ic_readermode,
|
||||
initialState = {
|
||||
session?.let {
|
||||
store.state.findTab(it.id)?.readerState?.active
|
||||
} ?: false
|
||||
},
|
||||
highlight = BrowserMenuHighlight.LowPriority(
|
||||
label = context.getString(R.string.browser_menu_read),
|
||||
notificationTint = getColor(context, R.color.whats_new_notification_color)
|
||||
),
|
||||
isHighlighted = { !context.settings().readerModeOpened }
|
||||
) { checked ->
|
||||
onItemTapped.invoke(ToolbarMenu.Item.ReaderMode(checked))
|
||||
}
|
||||
|
||||
private val readerAppearance = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_read_appearance),
|
||||
imageResource = R.drawable.ic_readermode_appearance,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M16.7 21H7.3A3.3 3.3 0 0 1 4 17.7V6.3C4 4.5 5.5 3 7.3 3h9.4C18.5 3 20 4.5 20 6.3v11.4c0 1.8-1.5 3.3-3.3 3.3zM7.3 5C6.6 5 6 5.6 6 6.3v11.4c0 0.7 0.6 1.3 1.3 1.3h9.4c0.7 0 1.3-0.6 1.3-1.3V6.3c0-0.7-0.6-1.3-1.3-1.3H7.3zM15 8H9a0.5 0.5 0 0 1 0-1h6a0.5 0.5 0 0 1 0 1zm0 3H9a0.5 0.5 0 0 1 0-1h6a0.5 0.5 0 0 1 0 1zm0 3H9a0.5 0.5 0 0 1 0-1h6a0.5 0.5 0 0 1 0 1zm-3.4 3H9a0.5 0.5 0 0 1 0-1h2.6a0.5 0.5 0 0 1 0 1z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="24"
|
||||
android:startY="0"
|
||||
android:endX="0"
|
||||
android:endY="24"
|
||||
android:startColor="?readerModeStartGradient"
|
||||
android:endColor="?readerModeEndGradient"/>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
|
@ -45,6 +45,8 @@
|
|||
<color name="add_on_private_browsing_exterior_circle_background_normal_theme">@color/add_on_private_browsing_exterior_circle_background_dark_theme</color>
|
||||
<color name="add_on_private_browsing_interior_icon_background_normal_theme">@color/add_on_private_browsing_interior_icon_background_dark_theme</color>
|
||||
<color name="prompt_login_edit_text_cursor_color_normal_theme">@color/prompt_login_edit_text_cursor_color_dark_theme</color>
|
||||
<color name="readermode_start_gradient_normal_theme">#C689FF</color>
|
||||
<color name="readermode_end_gradient_normal_theme">#00B3F4</color>
|
||||
|
||||
<!-- Tab tray -->
|
||||
<color name="tab_tray_item_text_normal_theme">@color/tab_tray_item_text_dark_theme</color>
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
<attr name="onboardingDeselected" format="reference"/>
|
||||
<attr name="addOnPrivateBrowsingExteriorCircleBackground" format="reference"/>
|
||||
<attr name="addOnPrivateBrowsingInteriorIconBackground" format="reference"/>
|
||||
<attr name="readerModeStartGradient" format="reference"/>
|
||||
<attr name="readerModeEndGradient" format="reference"/>
|
||||
|
||||
<!-- Tab tray -->
|
||||
<attr name="tabTrayItemBackground" format="reference" />
|
||||
|
|
|
@ -63,6 +63,8 @@
|
|||
<color name="add_on_private_browsing_exterior_circle_background_light_theme">@color/accent_bright_light_theme</color>
|
||||
<color name="add_on_private_browsing_interior_icon_background_light_theme">#FFFFFF</color>
|
||||
<color name="prompt_login_edit_text_cursor_color_light_theme">#312a64</color>
|
||||
<color name="readermode_start_gradient_normal_theme">#FF9059ff</color>
|
||||
<color name="readermode_end_gradient_normal_theme">#FF0250bb</color>
|
||||
|
||||
<!-- Tab Tray -->
|
||||
<color name="tab_tray_item_text_light_theme">@color/ink_80</color>
|
||||
|
|
|
@ -118,8 +118,10 @@
|
|||
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
|
||||
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
|
||||
<string name="browser_menu_powered_by2">Powered by %1$s</string>
|
||||
<!-- Browser menu button to put the the current page in reader mode -->
|
||||
<!-- Browser menu button to put the current page in reader mode -->
|
||||
<string name="browser_menu_read">Reader view</string>
|
||||
<!-- Browser menu button content description to close reader mode and return the user to the regular browser -->
|
||||
<string name="browser_menu_read_close">Close reader view</string>
|
||||
<!-- Browser menu button to open the current page in an external app -->
|
||||
<string name="browser_menu_open_app_link">Open in app</string>
|
||||
<!-- Browser menu button to configure reader mode appearance e.g. the used font type and size -->
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
<item name="addOnPrivateBrowsingExteriorCircleBackground">@color/add_on_private_browsing_exterior_circle_background_normal_theme</item>
|
||||
<item name="addOnPrivateBrowsingInteriorIconBackground">@color/add_on_private_browsing_interior_icon_background_normal_theme</item>
|
||||
<item name="mozacPromptLoginEditTextCursorColor">@color/prompt_login_edit_text_cursor_color_normal_theme</item>
|
||||
<item name="readerModeStartGradient">@color/readermode_start_gradient_normal_theme</item>
|
||||
<item name="readerModeEndGradient">@color/readermode_end_gradient_normal_theme</item>
|
||||
|
||||
|
||||
<item name="tabTrayItemBackground">@color/tab_tray_item_background_normal_theme</item>
|
||||
<item name="tabTrayItemSelectedBackground">@color/tab_tray_item_selected_background_normal_theme</item>
|
||||
|
|
|
@ -6,7 +6,6 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.spyk
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.ReaderState
|
||||
|
@ -69,15 +68,10 @@ class DefaultToolbarMenuTest {
|
|||
every { appLinkRedirect.hasExternalApp() } returns true
|
||||
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
||||
|
||||
val session: Session = mockk(relaxed = true)
|
||||
every { session.id } returns "readerable-tab"
|
||||
every { sessionManager.selectedSession } returns session
|
||||
|
||||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||
|
||||
assertEquals(ToolbarMenu.Item.InstallToHomeScreen, list[0])
|
||||
assertEquals(ToolbarMenu.Item.ReaderMode(false), list[1])
|
||||
assertEquals(ToolbarMenu.Item.OpenInApp, list[2])
|
||||
assertEquals(ToolbarMenu.Item.OpenInApp, list[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,29 +88,9 @@ class DefaultToolbarMenuTest {
|
|||
every { appLinkRedirect.hasExternalApp() } returns true
|
||||
every { getAppLinkRedirect(any()) } returns appLinkRedirect
|
||||
|
||||
val session: Session = mockk(relaxed = true)
|
||||
every { session.id } returns "readerable-tab"
|
||||
every { sessionManager.selectedSession } returns session
|
||||
|
||||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||
|
||||
assertEquals(ToolbarMenu.Item.ReaderMode(false), list[0])
|
||||
assertEquals(ToolbarMenu.Item.OpenInApp, list[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get all low prio highlight items without ReaderMode`() {
|
||||
every { context.components.useCases.webAppUseCases.isPinningSupported() } returns true
|
||||
every { context.components.useCases.webAppUseCases.isInstallable() } returns true
|
||||
|
||||
val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true)
|
||||
every { context.components.useCases.appLinksUseCases.appLinkRedirect } returns getAppLinkRedirect
|
||||
every { getAppLinkRedirect(any()).hasExternalApp() } returns true
|
||||
|
||||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||
|
||||
assertEquals(ToolbarMenu.Item.InstallToHomeScreen, list[0])
|
||||
assertEquals(ToolbarMenu.Item.OpenInApp, list[1])
|
||||
assertEquals(ToolbarMenu.Item.OpenInApp, list[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -124,10 +98,6 @@ class DefaultToolbarMenuTest {
|
|||
every { context.components.useCases.webAppUseCases.isPinningSupported() } returns true
|
||||
every { context.components.useCases.webAppUseCases.isInstallable() } returns true
|
||||
|
||||
val session: Session = mockk(relaxed = true)
|
||||
every { session.id } returns "readerable-tab"
|
||||
every { sessionManager.selectedSession } returns session
|
||||
|
||||
val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true)
|
||||
every { context.components.useCases.appLinksUseCases.appLinkRedirect } returns getAppLinkRedirect
|
||||
every { getAppLinkRedirect(any()).hasExternalApp() } returns false
|
||||
|
@ -135,6 +105,5 @@ class DefaultToolbarMenuTest {
|
|||
val list = defaultToolbarMenu.getLowPrioHighlightItems()
|
||||
|
||||
assertEquals(ToolbarMenu.Item.InstallToHomeScreen, list[0])
|
||||
assertEquals(ToolbarMenu.Item.ReaderMode(false), list[1])
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue