4281 remove qab (#6310)
* For #4281: small ToolbarMenu refactor This makes it easier to see how items are ordered in the menuItems list * For 4281: add QAB buttons to menu * For 4281: removed menu back button per mocks I double checked with UX, and we'll be relying on the hardware back button for its functionality * For 4281: add content descriptions for bookmarking * For 4281: updated BrowserToolbarController for new functionality * For 4281: provided simple dependencies to browser controller More complex changes will be in a following commit, for review readability * For 4281: move toolbar controller dependencies up to BaseBrowserFragment The functionality they control is being moved into the toolbar menu, which is shared by both normal tabs and custom ones * For 4281: removed (now unused) code related to QAB * For 4281: fix test compilation after QAB removal Tests still need to be expanded to include added functionality * For 4281: updated menu to show if url is bookmarked This sloppy workaround is required because TwoStateButton requires that `isInPrimaryState` be a synchronous call, and checking whether or not the current site is bookmarked is quite slow (10-50 MS, in my tests). After days of work and many attempted solutions, this was the least abhorrent among them. https://github.com/mozilla-mobile/android-components/issues/4915 was opened against AC to evaluate potentially supporting async `isInPrimaryState` functions. https://github.com/mozilla-mobile/fenix/issues/6370 was opened against Fenix to investigate the unexpectedly slow call to `BookmarkStorage`. * For 4281: update reader mode switch * For 4281: selectively show/hide menu items * For 4281: add reader mode appearance * For 4281: update bookmark button when it is clicked * For 4281: removed unused QAB code * For 4281: removed QAB robot, updated UI tests * For 4281: removed QuickActionSheet metrics Since this behavior now lives in the toolbar, it is tracked via Event.BrowserMenuItemTapped * For 4281: fixed lint errors * For 4281: add new strings for buttons added to menu This is necessary because the location change (from QAB to toolbar menu) could affect the grammar in some languages * For 4281: remove outdated TODOs * For 4281: removed QAB container * For 4281: removed back button reference from UI test This button no longer exists * For 4821: Fixes a visual defect (extra padding on top of toolbar) * For 4281: update copy on reader mode * For 4281: fixed review nitsmaster
parent
11ad1010a9
commit
6909a76bcb
|
@ -276,74 +276,6 @@ find_in_page:
|
|||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
|
||||
quick_action_sheet:
|
||||
opened:
|
||||
type: event
|
||||
description: >
|
||||
A user opened the quick action sheet UI
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
closed:
|
||||
type: event
|
||||
description: >
|
||||
A user closed the quick action sheet UI
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
share_tapped:
|
||||
type: event
|
||||
description: >
|
||||
A user tapped the share button
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
bookmark_tapped:
|
||||
type: event
|
||||
description: >
|
||||
A user tapped the bookmark button
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
download_tapped:
|
||||
type: event
|
||||
description: >
|
||||
A user tapped the download button
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
open_app_tapped:
|
||||
type: event
|
||||
description: >
|
||||
A user tapped the open in app button
|
||||
bugs:
|
||||
- https://github.com/mozilla-mobile/fenix/issue/1195
|
||||
data_reviews:
|
||||
- https://github.com/mozilla-mobile/fenix/pull/4629
|
||||
notification_emails:
|
||||
- fenix-core@mozilla.com
|
||||
expires: "2020-03-01"
|
||||
|
||||
metrics:
|
||||
default_browser:
|
||||
type: boolean
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.mozilla.fenix.helpers.TestAssetHelper
|
|||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.quickActionBar
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of bookmarks
|
||||
|
@ -66,12 +65,12 @@ class BookmarksTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openQuickActionBar {
|
||||
}.openThreeDotMenu {
|
||||
verifyAddBookmarkButton()
|
||||
clickBookmarkButton()
|
||||
clickAddBookmarkButton()
|
||||
}
|
||||
browserScreen {
|
||||
}.openQuickActionBar {
|
||||
}.openThreeDotMenu {
|
||||
verifyEditBookmarkButton()
|
||||
}
|
||||
}
|
||||
|
@ -80,11 +79,8 @@ class BookmarksTest {
|
|||
fun addBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
@ -109,9 +105,8 @@ class BookmarksTest {
|
|||
fun editBookmarkViewTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar { createBookmark(defaultWebPage.url) }
|
||||
|
||||
navigationToolbar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
@ -130,9 +125,8 @@ class BookmarksTest {
|
|||
fun copyBookmarkURLTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar { createBookmark(defaultWebPage.url) }
|
||||
|
||||
navigationToolbar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
@ -146,9 +140,8 @@ class BookmarksTest {
|
|||
fun openBookmarkInNewTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar { createBookmark(defaultWebPage.url) }
|
||||
|
||||
navigationToolbar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
@ -164,9 +157,8 @@ class BookmarksTest {
|
|||
fun openBookmarkInPrivateTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar { createBookmark(defaultWebPage.url) }
|
||||
|
||||
navigationToolbar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
@ -182,9 +174,8 @@ class BookmarksTest {
|
|||
fun deleteBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
quickActionBar { createBookmark(defaultWebPage.url) }
|
||||
|
||||
navigationToolbar {
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openLibrary {
|
||||
}.openBookmarks {
|
||||
|
|
|
@ -65,7 +65,6 @@ class NavigationToolbarTest {
|
|||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
verifyThreeDotMenuExists()
|
||||
verifyBackButton()
|
||||
}.goBack {
|
||||
verifyPageContent(defaultWebPage.content)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
package org.mozilla.fenix.ui.robots
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
|
@ -67,6 +68,14 @@ class BrowserRobot {
|
|||
TestAssetHelper.waitingTime)
|
||||
}
|
||||
|
||||
fun createBookmark(url: Uri) {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(url) {
|
||||
}.openThreeDotMenu {
|
||||
clickAddBookmarkButton()
|
||||
}
|
||||
}
|
||||
|
||||
class Transition {
|
||||
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
private fun threeDotButton() = onView(
|
||||
|
@ -106,16 +115,6 @@ class BrowserRobot {
|
|||
HomeScreenRobot().interact()
|
||||
return HomeScreenRobot.Transition()
|
||||
}
|
||||
|
||||
fun openQuickActionBar(interact: QuickActionBarRobot.() -> Unit): QuickActionBarRobot.Transition {
|
||||
mDevice.waitNotNull(Until.gone(By.res("org.mozilla.fenix.debug:id/quick_action_sheet")),
|
||||
TestAssetHelper.waitingTime
|
||||
)
|
||||
quickActionBarHandle().click()
|
||||
|
||||
QuickActionBarRobot().interact()
|
||||
return QuickActionBarRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,4 +132,3 @@ fun dismissTrackingOnboarding() {
|
|||
fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view))
|
||||
|
||||
private fun tabsCounter() = onView(withId(R.id.counter_box))
|
||||
private fun quickActionBarHandle() = onView(withId(R.id.quick_action_sheet_handle))
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package org.mozilla.fenix.ui.robots
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import org.hamcrest.Matchers.allOf
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.click
|
||||
import org.mozilla.fenix.helpers.isSelected
|
||||
|
||||
/**
|
||||
* Implementation of Robot Pattern for the quick action bar.
|
||||
*/
|
||||
class QuickActionBarRobot {
|
||||
|
||||
fun verifyAddBookmarkButton() = assertAddBookmarkButton()
|
||||
|
||||
fun verifyEditBookmarkButton() = assertEditBookmarkButton()
|
||||
|
||||
fun clickBookmarkButton() {
|
||||
addBookmarkButton().click()
|
||||
}
|
||||
|
||||
fun createBookmark(url: Uri) {
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(url) {
|
||||
}.openQuickActionBar {
|
||||
clickBookmarkButton()
|
||||
}
|
||||
}
|
||||
|
||||
class Transition {
|
||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
|
||||
fun closeQuickActionBar(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||
quickActionBarHandle().click()
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun quickActionBar(interact: QuickActionBarRobot.() -> Unit): QuickActionBarRobot.Transition {
|
||||
QuickActionBarRobot().interact()
|
||||
return QuickActionBarRobot.Transition()
|
||||
}
|
||||
|
||||
private fun quickActionBarHandle() = onView(withId(R.id.quick_action_sheet_handle))
|
||||
|
||||
private fun addBookmarkButton() =
|
||||
onView(allOf(withId(R.id.quick_action_bookmark), isSelected(false)))
|
||||
|
||||
private fun editBookmarkButton() =
|
||||
onView(allOf(withId(R.id.quick_action_bookmark), isSelected(true)))
|
||||
|
||||
private fun snackBarText() = onView(withId(R.id.snackbar_text))
|
||||
|
||||
private fun assertAddBookmarkButton() = addBookmarkButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
.check(matches(withText("Bookmark")))
|
||||
|
||||
private fun assertBookmarkSavedSnackBarText() =
|
||||
snackBarText().check(matches(withText("Bookmark saved!")))
|
||||
|
||||
private fun assertEditBookmarkButton() = editBookmarkButton().check(matches(withEffectiveVisibility(
|
||||
ViewMatchers.Visibility.VISIBLE)))
|
||||
.check(matches(withText("Edit Bookmark")))
|
|
@ -11,11 +11,11 @@ import androidx.test.espresso.action.ViewActions
|
|||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasFocus
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
|
@ -38,7 +38,8 @@ class ThreeDotMenuMainRobot {
|
|||
fun verifyHelpButton() = assertHelpButton()
|
||||
fun verifyThreeDotMenuExists() = threeDotMenuRecyclerViewExists()
|
||||
fun verifyForwardButton() = assertForwardButton()
|
||||
fun verifyBackButton() = assertBackButton()
|
||||
fun verifyAddBookmarkButton() = assertAddBookmarkButton()
|
||||
fun verifyEditBookmarkButton() = assertEditBookmarkButton()
|
||||
fun verifyRefreshButton() = assertRefreshButton()
|
||||
fun verifyCloseAllTabsButton() = assertCloseAllTabsButton()
|
||||
fun verifyShareButton() = assertShareButton()
|
||||
|
@ -55,6 +56,7 @@ class ThreeDotMenuMainRobot {
|
|||
fun clickAddNewCollection() {
|
||||
addNewCollectionButton().click()
|
||||
}
|
||||
fun clickAddBookmarkButton() = addBookmarkButton().click()
|
||||
fun verifyCollectionNameTextField() = assertCollectionNameTextField()
|
||||
fun verifyFindInPageButton() = assertFindInPageButton()
|
||||
fun verifyShareScrim() = assertShareScrim()
|
||||
|
@ -115,8 +117,10 @@ class ThreeDotMenuMainRobot {
|
|||
}
|
||||
|
||||
fun goBack(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||
mDevice.waitNotNull(Until.findObject(By.desc("Back")), waitingTime)
|
||||
backButton().click()
|
||||
// Close three dot
|
||||
mDevice.pressBack()
|
||||
// Nav back to previous page
|
||||
mDevice.pressBack()
|
||||
|
||||
BrowserRobot().interact()
|
||||
return BrowserRobot.Transition()
|
||||
|
@ -194,8 +198,12 @@ private fun forwardButton() = onView(ViewMatchers.withContentDescription("Forwar
|
|||
private fun assertForwardButton() = forwardButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun backButton() = onView(ViewMatchers.withContentDescription("Back"))
|
||||
private fun assertBackButton() = backButton()
|
||||
private fun addBookmarkButton() = onView(ViewMatchers.withContentDescription("Bookmark"))
|
||||
private fun assertAddBookmarkButton() = addBookmarkButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun editBookmarkButton() = onView(ViewMatchers.withContentDescription("Edit bookmark"))
|
||||
private fun assertEditBookmarkButton() = editBookmarkButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun refreshButton() = onView(ViewMatchers.withContentDescription("Refresh"))
|
||||
|
@ -210,7 +218,7 @@ private fun shareTabButton() = onView(allOf(withText("Share tabs")))
|
|||
private fun assertShareTabButton() = shareTabButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun shareButton() = onView(allOf(withText("Share")))
|
||||
private fun shareButton() = onView(ViewMatchers.withContentDescription("Share"))
|
||||
private fun assertShareButton() = shareButton()
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.component_search.*
|
||||
import kotlinx.android.synthetic.main.component_search.toolbar
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
@ -32,6 +32,7 @@ import kotlinx.coroutines.Job
|
|||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.accounts.FxaCapability
|
||||
|
@ -44,6 +45,7 @@ import mozilla.components.feature.downloads.DownloadsFeature
|
|||
import mozilla.components.feature.downloads.manager.FetchDownloadManager
|
||||
import mozilla.components.feature.intent.ext.EXTRA_SESSION_ID
|
||||
import mozilla.components.feature.prompts.PromptFeature
|
||||
import mozilla.components.feature.readerview.ReaderViewFeature
|
||||
import mozilla.components.feature.session.FullScreenFeature
|
||||
import mozilla.components.feature.session.SessionFeature
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
|
@ -61,17 +63,17 @@ import org.mozilla.fenix.FeatureFlags
|
|||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.FindInPageIntegration
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.toolbar.BrowserFragmentState
|
||||
import org.mozilla.fenix.components.toolbar.BrowserFragmentStore
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
|
||||
import org.mozilla.fenix.components.toolbar.BrowserInteractor
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarView
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
|
||||
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController
|
||||
import org.mozilla.fenix.components.toolbar.QuickActionSheetState
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
|
||||
import org.mozilla.fenix.downloads.DownloadNotificationBottomSheetDialog
|
||||
import org.mozilla.fenix.downloads.DownloadService
|
||||
|
@ -84,7 +86,6 @@ import org.mozilla.fenix.ext.requireComponents
|
|||
import org.mozilla.fenix.ext.sessionsOfType
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.isInExperiment
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
|
||||
|
@ -95,10 +96,12 @@ import org.mozilla.fenix.theme.ThemeManager
|
|||
*/
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Observer {
|
||||
protected lateinit var browserStore: BrowserFragmentStore
|
||||
protected lateinit var browserFragmentStore: BrowserFragmentStore
|
||||
protected lateinit var browserInteractor: BrowserToolbarViewInteractor
|
||||
protected lateinit var browserToolbarView: BrowserToolbarView
|
||||
|
||||
protected val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
|
||||
|
||||
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
|
||||
private val windowFeature = ViewBoundFeatureWrapper<WindowFeature>()
|
||||
private val contextMenuFeature = ViewBoundFeatureWrapper<ContextMenuFeature>()
|
||||
|
@ -131,20 +134,9 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
val activity = activity as HomeActivity
|
||||
activity.themeManager.applyStatusBarTheme(activity)
|
||||
|
||||
val appLink = requireComponents.useCases.appLinksUseCases.appLinkRedirect
|
||||
browserStore = StoreProvider.get(this) {
|
||||
browserFragmentStore = StoreProvider.get(this) {
|
||||
BrowserFragmentStore(
|
||||
BrowserFragmentState(
|
||||
quickActionSheetState = QuickActionSheetState(
|
||||
readable = getSessionById()?.readerable ?: false,
|
||||
bookmarked = false,
|
||||
readerActive = getSessionById()?.readerMode ?: false,
|
||||
bounceNeeded = false,
|
||||
isAppLink = getSessionById()?.let {
|
||||
appLink.invoke(it.url).hasExternalApp()
|
||||
} ?: false
|
||||
)
|
||||
)
|
||||
BrowserFragmentState()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -173,10 +165,13 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
}
|
||||
|
||||
val browserToolbarController = DefaultBrowserToolbarController(
|
||||
requireActivity(),
|
||||
snackbar,
|
||||
findNavController(),
|
||||
(activity as HomeActivity).browsingModeManager,
|
||||
store = browserFragmentStore,
|
||||
activity = requireActivity(),
|
||||
snackbar = snackbar,
|
||||
navController = findNavController(),
|
||||
readerModeController = DefaultReaderModeController(readerViewFeature),
|
||||
browsingModeManager = (activity as HomeActivity).browsingModeManager,
|
||||
sessionManager = requireComponents.core.sessionManager,
|
||||
findInPageLauncher = { findInPageIntegration.withFeature { it.launch() } },
|
||||
browserLayout = view.browserLayout,
|
||||
engineView = engineView,
|
||||
|
@ -193,15 +188,14 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
action = Intent.ACTION_VIEW
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
},
|
||||
bottomSheetBehavior = QuickActionSheetBehavior.from(nestedScrollQuickAction),
|
||||
bookmarkTapped = { lifecycleScope.launch { bookmarkTapped(it) } },
|
||||
scope = lifecycleScope,
|
||||
tabCollectionStorage = requireComponents.core.tabCollectionStorage
|
||||
)
|
||||
|
||||
browserInteractor =
|
||||
createBrowserToolbarViewInteractor(
|
||||
browserToolbarController,
|
||||
customTabSessionId?.let { sessionManager.findSessionById(it) })
|
||||
browserInteractor = BrowserInteractor(
|
||||
browserToolbarController = browserToolbarController
|
||||
)
|
||||
|
||||
browserToolbarView = BrowserToolbarView(
|
||||
container = view.browserLayout,
|
||||
|
@ -368,7 +362,6 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
activity?.enterToImmersiveMode()
|
||||
toolbar.visibility = View.GONE
|
||||
nestedScrollQuickAction.visibility = View.GONE
|
||||
} else {
|
||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
||||
activity?.exitImmersiveModeIfNeeded()
|
||||
|
@ -376,7 +369,6 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
activity.themeManager.applyStatusBarTheme(activity)
|
||||
}
|
||||
toolbar.visibility = View.VISIBLE
|
||||
nestedScrollQuickAction.visibility = View.VISIBLE
|
||||
}
|
||||
updateLayoutMargins(inFullScreen)
|
||||
},
|
||||
|
@ -557,11 +549,6 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
return false
|
||||
}
|
||||
|
||||
protected abstract fun createBrowserToolbarViewInteractor(
|
||||
browserToolbarController: BrowserToolbarController,
|
||||
session: Session?
|
||||
): BrowserToolbarViewInteractor
|
||||
|
||||
protected abstract fun navToQuickSettingsSheet(
|
||||
session: Session,
|
||||
sitePermissions: SitePermissions?
|
||||
|
@ -644,6 +631,48 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun bookmarkTapped(session: Session) = withContext(IO) {
|
||||
val bookmarksStorage = requireComponents.core.bookmarksStorage
|
||||
val existing =
|
||||
bookmarksStorage.getBookmarksWithUrl(session.url).firstOrNull { it.url == session.url }
|
||||
if (existing != null) {
|
||||
// Bookmark exists, go to edit fragment
|
||||
withContext(Main) {
|
||||
nav(
|
||||
R.id.browserFragment,
|
||||
BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment(existing.guid)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Save bookmark, then go to edit fragment
|
||||
val guid = bookmarksStorage.addItem(
|
||||
BookmarkRoot.Mobile.id,
|
||||
url = session.url,
|
||||
title = session.title,
|
||||
position = null
|
||||
)
|
||||
|
||||
withContext(Main) {
|
||||
requireComponents.analytics.metrics.track(Event.AddBookmark)
|
||||
|
||||
view?.let { view ->
|
||||
FenixSnackbar.make(view, Snackbar.LENGTH_LONG)
|
||||
.setAnchorView(browserToolbarView.view)
|
||||
.setAction(getString(R.string.edit_bookmark_snackbar_action)) {
|
||||
nav(
|
||||
R.id.browserFragment,
|
||||
BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment(
|
||||
guid
|
||||
)
|
||||
)
|
||||
}
|
||||
.setText(getString(R.string.bookmark_saved_snackbar))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_CUSTOM_TAB_SESSION_ID = "custom_tab_session_id"
|
||||
private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1
|
||||
|
|
|
@ -20,19 +20,13 @@ import android.widget.RadioButton
|
|||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.browserLayout
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.readerViewControlsBar
|
||||
import kotlinx.android.synthetic.main.fragment_home.bottom_bar
|
||||
import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.onboarding_message
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
||||
import mozilla.components.feature.readerview.ReaderViewFeature
|
||||
|
@ -40,18 +34,12 @@ import mozilla.components.feature.session.TrackingProtectionUseCases
|
|||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import mozilla.components.support.base.feature.BackHandler
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import org.jetbrains.anko.dimen
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.toolbar.BrowserInteractor
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
|
||||
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getDimenInDip
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
|
@ -61,9 +49,6 @@ import org.mozilla.fenix.ext.settings
|
|||
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import org.mozilla.fenix.quickactionsheet.DefaultQuickActionSheetController
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetSessionObserver
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetView
|
||||
|
||||
/**
|
||||
* Fragment used for browsing the web within the main app.
|
||||
|
@ -71,10 +56,6 @@ import org.mozilla.fenix.quickactionsheet.QuickActionSheetView
|
|||
@ExperimentalCoroutinesApi
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
||||
private lateinit var quickActionSheetView: QuickActionSheetView
|
||||
private var quickActionSheetSessionObserver: QuickActionSheetSessionObserver? = null
|
||||
|
||||
private val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -114,15 +95,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
if (available) {
|
||||
context.components.analytics.metrics.track(Event.ReaderModeAvailable)
|
||||
}
|
||||
|
||||
browserStore.apply {
|
||||
dispatch(QuickActionSheetAction.ReadableStateChange(available))
|
||||
dispatch(
|
||||
QuickActionSheetAction.ReaderActiveStateChange(
|
||||
sessionManager.selectedSession?.readerMode ?: false
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
owner = this,
|
||||
view = view
|
||||
|
@ -134,8 +106,7 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
themeReaderViewControlsForPrivateMode(view.readerViewControlsBar)
|
||||
}
|
||||
|
||||
consumeFrom(browserStore) {
|
||||
quickActionSheetView.update(it)
|
||||
consumeFrom(browserFragmentStore) {
|
||||
browserToolbarView.update(it)
|
||||
}
|
||||
}
|
||||
|
@ -144,13 +115,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
override fun onStart() {
|
||||
super.onStart()
|
||||
subscribeToTabCollections()
|
||||
quickActionSheetSessionObserver = QuickActionSheetSessionObserver(
|
||||
lifecycleScope,
|
||||
requireComponents,
|
||||
dispatch = { action -> browserStore.dispatch(action) }
|
||||
).also { observer ->
|
||||
getSessionById()?.register(observer, this, autoPause = true)
|
||||
}
|
||||
getSessionById()?.register(toolbarSessionObserver, this, autoPause = true)
|
||||
}
|
||||
|
||||
|
@ -173,7 +137,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
* This fixes issue #5254.
|
||||
*/
|
||||
(activity as HomeActivity).updateThemeForSession(it)
|
||||
quickActionSheetSessionObserver?.updateBookmarkState(it)
|
||||
}
|
||||
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
|
||||
}
|
||||
|
@ -182,34 +145,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
return readerViewFeature.onBackPressed() || super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun createBrowserToolbarViewInteractor(
|
||||
browserToolbarController: BrowserToolbarController,
|
||||
session: Session?
|
||||
): BrowserToolbarViewInteractor {
|
||||
val context = requireContext()
|
||||
|
||||
val interactor = BrowserInteractor(
|
||||
context = context,
|
||||
store = browserStore,
|
||||
browserToolbarController = browserToolbarController,
|
||||
quickActionSheetController = DefaultQuickActionSheetController(
|
||||
context = context,
|
||||
navController = findNavController(),
|
||||
sessionManager = context.components.core.sessionManager,
|
||||
appLinksUseCases = context.components.useCases.appLinksUseCases,
|
||||
bookmarkTapped = {
|
||||
lifecycleScope.launch { bookmarkTapped(it) }
|
||||
}
|
||||
),
|
||||
readerModeController = DefaultReaderModeController(readerViewFeature),
|
||||
currentSession = session
|
||||
)
|
||||
|
||||
quickActionSheetView = QuickActionSheetView(view!!.nestedScrollQuickAction, interactor)
|
||||
|
||||
return interactor
|
||||
}
|
||||
|
||||
override fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?) {
|
||||
val directions =
|
||||
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
|
||||
|
@ -241,8 +176,8 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
}
|
||||
|
||||
override fun getEngineMargins(): Pair<Int, Int> {
|
||||
val toolbarAndQASSize = resources.getDimensionPixelSize(R.dimen.toolbar_and_qab_height)
|
||||
return 0 to toolbarAndQASSize
|
||||
val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
||||
return 0 to toolbarSize
|
||||
}
|
||||
|
||||
override fun getAppropriateLayoutGravity() = Gravity.BOTTOM
|
||||
|
@ -277,51 +212,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun bookmarkTapped(session: Session) = withContext(IO) {
|
||||
val bookmarksStorage = requireComponents.core.bookmarksStorage
|
||||
val existing =
|
||||
bookmarksStorage.getBookmarksWithUrl(session.url).firstOrNull { it.url == session.url }
|
||||
if (existing != null) {
|
||||
// Bookmark exists, go to edit fragment
|
||||
withContext(Main) {
|
||||
nav(
|
||||
R.id.browserFragment,
|
||||
BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment(existing.guid)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Save bookmark, then go to edit fragment
|
||||
val guid = bookmarksStorage.addItem(
|
||||
BookmarkRoot.Mobile.id,
|
||||
url = session.url,
|
||||
title = session.title,
|
||||
position = null
|
||||
)
|
||||
|
||||
withContext(Main) {
|
||||
browserStore.dispatch(
|
||||
QuickActionSheetAction.BookmarkedStateChange(bookmarked = true)
|
||||
)
|
||||
requireComponents.analytics.metrics.track(Event.AddBookmark)
|
||||
|
||||
view?.let { view ->
|
||||
FenixSnackbar.make(view, Snackbar.LENGTH_LONG)
|
||||
.setAnchorView(browserToolbarView.view)
|
||||
.setAction(getString(R.string.edit_bookmark_snackbar_action)) {
|
||||
nav(
|
||||
R.id.browserFragment,
|
||||
BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment(
|
||||
guid
|
||||
)
|
||||
)
|
||||
}
|
||||
.setText(getString(R.string.bookmark_saved_snackbar))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToTabCollections() {
|
||||
requireComponents.core.tabCollectionStorage.getCollections().observe(this, Observer {
|
||||
requireComponents.core.tabCollectionStorage.cachedTabCollections = it
|
||||
|
@ -333,11 +223,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onSessionSelected(session: Session) {
|
||||
super.onSessionSelected(session)
|
||||
quickActionSheetSessionObserver?.updateBookmarkState(session)
|
||||
}
|
||||
|
||||
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
|
||||
override fun onCollectionCreated(title: String, sessions: List<Session>) {
|
||||
showTabSavedToCollectionSnackbar()
|
||||
|
@ -415,7 +300,7 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
view,
|
||||
FenixSnackbarDelegate(
|
||||
view,
|
||||
nestedScrollQuickAction
|
||||
bottom_bar
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.mozilla.fenix.GleanMetrics.Pings
|
|||
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode
|
||||
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut
|
||||
import org.mozilla.fenix.GleanMetrics.QrScanner
|
||||
import org.mozilla.fenix.GleanMetrics.QuickActionSheet
|
||||
import org.mozilla.fenix.GleanMetrics.ReaderMode
|
||||
import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine
|
||||
import org.mozilla.fenix.GleanMetrics.SearchShortcuts
|
||||
|
@ -138,24 +137,6 @@ private val Event.wrapper: EventWrapper<*>?
|
|||
{ Events.browserMenuAction.record(it) },
|
||||
{ Events.browserMenuActionKeys.valueOf(it) }
|
||||
)
|
||||
is Event.QuickActionSheetOpened -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.opened.record(it) }
|
||||
)
|
||||
is Event.QuickActionSheetClosed -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.closed.record(it) }
|
||||
)
|
||||
is Event.QuickActionSheetShareTapped -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.shareTapped.record(it) }
|
||||
)
|
||||
is Event.QuickActionSheetBookmarkTapped -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.bookmarkTapped.record(it) }
|
||||
)
|
||||
is Event.QuickActionSheetDownloadTapped -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.downloadTapped.record(it) }
|
||||
)
|
||||
is Event.QuickActionSheetOpenInAppTapped -> EventWrapper<NoExtraKeys>(
|
||||
{ QuickActionSheet.openAppTapped.record(it) }
|
||||
)
|
||||
is Event.OpenedBookmarkInNewTab -> EventWrapper<NoExtraKeys>(
|
||||
{ BookmarksManagement.openInNewTab.record(it) }
|
||||
)
|
||||
|
|
|
@ -50,12 +50,6 @@ sealed class Event {
|
|||
object AddBookmarkFolder : Event()
|
||||
object RemoveBookmarkFolder : Event()
|
||||
object RemoveBookmarks : Event()
|
||||
object QuickActionSheetOpened : Event()
|
||||
object QuickActionSheetClosed : Event()
|
||||
object QuickActionSheetShareTapped : Event()
|
||||
object QuickActionSheetBookmarkTapped : Event()
|
||||
object QuickActionSheetDownloadTapped : Event()
|
||||
object QuickActionSheetOpenInAppTapped : Event()
|
||||
object CustomTabsClosed : Event()
|
||||
object CustomTabsActionTapped : Event()
|
||||
object CustomTabsMenuOpened : Event()
|
||||
|
@ -300,7 +294,8 @@ sealed class Event {
|
|||
enum class Item {
|
||||
SETTINGS, LIBRARY, HELP, DESKTOP_VIEW_ON, DESKTOP_VIEW_OFF, FIND_IN_PAGE, NEW_TAB,
|
||||
NEW_PRIVATE_TAB, SHARE, REPORT_SITE_ISSUE, BACK, FORWARD, RELOAD, STOP, OPEN_IN_FENIX,
|
||||
SAVE_TO_COLLECTION, ADD_TO_HOMESCREEN, QUIT
|
||||
SAVE_TO_COLLECTION, ADD_TO_HOMESCREEN, QUIT, READER_MODE_ON, READER_MODE_OFF, OPEN_IN_APP,
|
||||
BOOKMARK, READER_MODE_APPEARANCE
|
||||
}
|
||||
|
||||
override val extras: Map<Events.browserMenuActionKeys, String>?
|
||||
|
|
|
@ -2,51 +2,27 @@
|
|||
* 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/. */
|
||||
|
||||
@file:Suppress("unused", "UNUSED_PARAMETER")
|
||||
|
||||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import mozilla.components.lib.state.Action
|
||||
import mozilla.components.lib.state.State
|
||||
import mozilla.components.lib.state.Store
|
||||
|
||||
// The state that used to live in this class was moved into another component in #4281. Keeping
|
||||
// the shell of this file because we will need to expand it as we add additional features to
|
||||
// the browser.
|
||||
class BrowserFragmentStore(initialState: BrowserFragmentState) :
|
||||
Store<BrowserFragmentState, BrowserFragmentAction>(initialState, ::browserStateReducer)
|
||||
|
||||
/**
|
||||
* The state for the Browser Screen
|
||||
* @property quickActionSheetState: state of the quick action sheet
|
||||
*/
|
||||
data class BrowserFragmentState(
|
||||
val quickActionSheetState: QuickActionSheetState
|
||||
) : State
|
||||
|
||||
/**
|
||||
* The state for the QuickActionSheet
|
||||
* @property readable Whether or not the current session can display a reader view
|
||||
* @property bookmarked Whether or not the current session is already bookmarked
|
||||
* @property readerActive Whether or not the current session is in reader mode
|
||||
* @property bounceNeeded Whether or not the quick action sheet should bounce
|
||||
*/
|
||||
data class QuickActionSheetState(
|
||||
val readable: Boolean,
|
||||
val bookmarked: Boolean,
|
||||
val readerActive: Boolean,
|
||||
val bounceNeeded: Boolean,
|
||||
val isAppLink: Boolean
|
||||
) : State
|
||||
class BrowserFragmentState : State
|
||||
|
||||
sealed class BrowserFragmentAction : Action
|
||||
|
||||
/**
|
||||
* Actions to dispatch through the [QuickActionSheetStore] to modify [QuickActionSheetState] through the reducer.
|
||||
*/
|
||||
sealed class QuickActionSheetAction : BrowserFragmentAction() {
|
||||
data class BookmarkedStateChange(val bookmarked: Boolean) : QuickActionSheetAction()
|
||||
data class ReadableStateChange(val readable: Boolean) : QuickActionSheetAction()
|
||||
data class ReaderActiveStateChange(val active: Boolean) : QuickActionSheetAction()
|
||||
data class AppLinkStateChange(val isAppLink: Boolean) : QuickActionSheetAction()
|
||||
object BounceNeededChange : QuickActionSheetAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reducers for [BrowserFragmentStore].
|
||||
*
|
||||
|
@ -57,30 +33,7 @@ private fun browserStateReducer(
|
|||
state: BrowserFragmentState,
|
||||
action: BrowserFragmentAction
|
||||
): BrowserFragmentState {
|
||||
return when (action) {
|
||||
is QuickActionSheetAction -> {
|
||||
QuickActionSheetStateReducer.reduce(state, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces [QuickActionSheetAction]s to update [BrowserFragmentState].
|
||||
*/
|
||||
internal object QuickActionSheetStateReducer {
|
||||
fun reduce(state: BrowserFragmentState, action: QuickActionSheetAction): BrowserFragmentState {
|
||||
return when (action) {
|
||||
is QuickActionSheetAction.BookmarkedStateChange ->
|
||||
state.copy(quickActionSheetState = state.quickActionSheetState.copy(bookmarked = action.bookmarked))
|
||||
is QuickActionSheetAction.ReadableStateChange ->
|
||||
state.copy(quickActionSheetState = state.quickActionSheetState.copy(readable = action.readable))
|
||||
is QuickActionSheetAction.ReaderActiveStateChange ->
|
||||
state.copy(quickActionSheetState = state.quickActionSheetState.copy(readerActive = action.active))
|
||||
is QuickActionSheetAction.BounceNeededChange ->
|
||||
state.copy(quickActionSheetState = state.quickActionSheetState.copy(bounceNeeded = true))
|
||||
is QuickActionSheetAction.AppLinkStateChange -> {
|
||||
state.copy(quickActionSheetState = state.quickActionSheetState.copy(isAppLink = action.isAppLink))
|
||||
}
|
||||
}
|
||||
return when {
|
||||
else -> BrowserFragmentState()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import mozilla.components.browser.session.Session
|
||||
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.metrics
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetController
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetViewInteractor
|
||||
|
||||
open class BrowserToolbarInteractor(
|
||||
open class BrowserInteractor(
|
||||
private val browserToolbarController: BrowserToolbarController
|
||||
) : BrowserToolbarViewInteractor {
|
||||
|
||||
|
@ -37,56 +28,3 @@ open class BrowserToolbarInteractor(
|
|||
browserToolbarController.handleToolbarItemInteraction(item)
|
||||
}
|
||||
}
|
||||
|
||||
class BrowserInteractor(
|
||||
private val context: Context,
|
||||
private val store: BrowserFragmentStore,
|
||||
browserToolbarController: BrowserToolbarController,
|
||||
private val quickActionSheetController: QuickActionSheetController,
|
||||
private val readerModeController: ReaderModeController,
|
||||
private val currentSession: Session?
|
||||
) : BrowserToolbarInteractor(browserToolbarController), QuickActionSheetViewInteractor {
|
||||
|
||||
override fun onQuickActionSheetOpened() {
|
||||
context.metrics.track(Event.QuickActionSheetOpened)
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetClosed() {
|
||||
context.metrics.track(Event.QuickActionSheetClosed)
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetSharePressed() {
|
||||
quickActionSheetController.handleShare()
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetDownloadPressed() {
|
||||
quickActionSheetController.handleDownload()
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetBookmarkPressed() {
|
||||
quickActionSheetController.handleBookmark()
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetReadPressed() {
|
||||
val enabled =
|
||||
currentSession?.readerMode ?: context.components.core.sessionManager.selectedSession?.readerMode ?: false
|
||||
|
||||
if (enabled) {
|
||||
context.metrics.track(Event.QuickActionSheetClosed)
|
||||
readerModeController.hideReaderView()
|
||||
} else {
|
||||
context.metrics.track(Event.QuickActionSheetOpened)
|
||||
readerModeController.showReaderView()
|
||||
}
|
||||
store.dispatch(QuickActionSheetAction.ReaderActiveStateChange(!enabled))
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetOpenLinkPressed() {
|
||||
quickActionSheetController.handleOpenLink()
|
||||
}
|
||||
|
||||
override fun onQuickActionSheetAppearancePressed() {
|
||||
context.metrics.track(Event.ReaderModeAppearanceOpened)
|
||||
readerModeController.showControls()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,18 +11,17 @@ import android.graphics.drawable.ColorDrawable
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.fragment.FragmentNavigator
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.concept.engine.EngineView
|
||||
import mozilla.components.support.ktx.kotlin.isUrl
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
|
@ -31,14 +30,14 @@ import org.mozilla.fenix.browser.BrowserFragment
|
|||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.lib.Do
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
|
||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||
|
||||
/**
|
||||
|
@ -54,10 +53,13 @@ interface BrowserToolbarController {
|
|||
|
||||
@Suppress("LargeClass")
|
||||
class DefaultBrowserToolbarController(
|
||||
private val store: BrowserFragmentStore,
|
||||
private val activity: Activity,
|
||||
private val snackbar: FenixSnackbar?,
|
||||
private val navController: NavController,
|
||||
private val readerModeController: ReaderModeController,
|
||||
private val browsingModeManager: BrowsingModeManager,
|
||||
private val sessionManager: SessionManager,
|
||||
private val findInPageLauncher: () -> Unit,
|
||||
private val browserLayout: ViewGroup,
|
||||
private val engineView: EngineView,
|
||||
|
@ -66,7 +68,7 @@ class DefaultBrowserToolbarController(
|
|||
private val customTabSession: Session?,
|
||||
private val getSupportUrl: () -> String,
|
||||
private val openInFenixIntent: Intent,
|
||||
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView>,
|
||||
private val bookmarkTapped: (Session) -> Unit,
|
||||
private val scope: LifecycleCoroutineScope,
|
||||
private val tabCollectionStorage: TabCollectionStorage
|
||||
) : BrowserToolbarController {
|
||||
|
@ -163,7 +165,6 @@ class DefaultBrowserToolbarController(
|
|||
browsingModeManager.mode = BrowsingMode.Private
|
||||
}
|
||||
ToolbarMenu.Item.FindInPage -> {
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
findInPageLauncher()
|
||||
activity.components.analytics.metrics.track(Event.FindInPageOpened)
|
||||
}
|
||||
|
@ -210,6 +211,35 @@ class DefaultBrowserToolbarController(
|
|||
activity.finish()
|
||||
}
|
||||
ToolbarMenu.Item.Quit -> deleteAndQuit(activity, scope, snackbar)
|
||||
is ToolbarMenu.Item.ReaderMode -> {
|
||||
val enabled = currentSession?.readerMode
|
||||
?: activity.components.core.sessionManager.selectedSession?.readerMode
|
||||
?: false
|
||||
|
||||
if (enabled) {
|
||||
readerModeController.hideReaderView()
|
||||
} else {
|
||||
readerModeController.showReaderView()
|
||||
}
|
||||
}
|
||||
ToolbarMenu.Item.ReaderModeAppearance -> {
|
||||
readerModeController.showControls()
|
||||
}
|
||||
ToolbarMenu.Item.OpenInApp -> {
|
||||
val appLinksUseCases =
|
||||
activity.components.useCases.appLinksUseCases
|
||||
val getRedirect = appLinksUseCases.appLinkRedirect
|
||||
sessionManager.selectedSession?.let {
|
||||
val redirect = getRedirect.invoke(it.url)
|
||||
redirect.appIntent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
appLinksUseCases.openAppLink.invoke(redirect)
|
||||
}
|
||||
}
|
||||
ToolbarMenu.Item.Bookmark -> {
|
||||
sessionManager.selectedSession?.let {
|
||||
bookmarkTapped(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,6 +290,16 @@ class DefaultBrowserToolbarController(
|
|||
ToolbarMenu.Item.SaveToCollection -> Event.BrowserMenuItemTapped.Item.SAVE_TO_COLLECTION
|
||||
ToolbarMenu.Item.AddToHomeScreen -> Event.BrowserMenuItemTapped.Item.ADD_TO_HOMESCREEN
|
||||
ToolbarMenu.Item.Quit -> Event.BrowserMenuItemTapped.Item.QUIT
|
||||
is ToolbarMenu.Item.ReaderMode ->
|
||||
if (item.isChecked) {
|
||||
Event.BrowserMenuItemTapped.Item.READER_MODE_ON
|
||||
} else {
|
||||
Event.BrowserMenuItemTapped.Item.READER_MODE_OFF
|
||||
}
|
||||
ToolbarMenu.Item.ReaderModeAppearance ->
|
||||
Event.BrowserMenuItemTapped.Item.READER_MODE_APPEARANCE
|
||||
ToolbarMenu.Item.OpenInApp -> Event.BrowserMenuItemTapped.Item.OPEN_IN_APP
|
||||
ToolbarMenu.Item.Bookmark -> Event.BrowserMenuItemTapped.Item.BOOKMARK
|
||||
}
|
||||
|
||||
activity.components.analytics.metrics.track(Event.BrowserMenuItemTapped(eventItem))
|
||||
|
|
|
@ -10,11 +10,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.*
|
||||
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.copy
|
||||
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.paste
|
||||
import kotlinx.android.synthetic.main.browser_toolbar_popup_window.view.paste_and_go
|
||||
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||
|
@ -24,6 +27,7 @@ import org.jetbrains.anko.dimen
|
|||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
|
||||
import org.mozilla.fenix.ext.bookmarkStorage
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
|
||||
|
@ -168,12 +172,18 @@ class BrowserToolbarView(
|
|||
)
|
||||
} else {
|
||||
DefaultToolbarMenu(
|
||||
this,
|
||||
context = this,
|
||||
hasAccountProblem = components.backgroundServices.accountManager.accountNeedsReauth(),
|
||||
requestDesktopStateProvider = {
|
||||
sessionManager.selectedSession?.desktopMode ?: false
|
||||
},
|
||||
onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) }
|
||||
readerModeStateProvider = {
|
||||
sessionManager.selectedSession?.readerMode ?: false
|
||||
},
|
||||
onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) },
|
||||
lifecycleOwner = container.context as AppCompatActivity,
|
||||
sessionManager = sessionManager,
|
||||
bookmarksStorage = bookmarkStorage
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,19 @@
|
|||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.BrowserMenuDivider
|
||||
import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
|
||||
import mozilla.components.browser.menu.item.BrowserMenuImageText
|
||||
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
|
||||
import mozilla.components.browser.menu.item.BrowserMenuImageSwitch
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.concept.storage.BookmarksStorage
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -19,35 +26,24 @@ import org.mozilla.fenix.ext.components
|
|||
import org.mozilla.fenix.theme.ThemeManager
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@Suppress("LargeClass") // While large, most of the class is very simple
|
||||
class DefaultToolbarMenu(
|
||||
private val context: Context,
|
||||
private val hasAccountProblem: Boolean = false,
|
||||
private val requestDesktopStateProvider: () -> Boolean = { false },
|
||||
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {}
|
||||
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val bookmarksStorage: BookmarksStorage,
|
||||
readerModeStateProvider: () -> Boolean = { false },
|
||||
sessionManager: SessionManager
|
||||
) : ToolbarMenu {
|
||||
|
||||
private var currentUrlIsBookmarked = false
|
||||
private var isBookmarkedJob: Job? = null
|
||||
|
||||
override val menuBuilder by lazy { BrowserMenuBuilder(menuItems, endOfMenuAlwaysVisible = true) }
|
||||
|
||||
override val menuToolbar by lazy {
|
||||
val back = BrowserMenuItemToolbar.TwoStateButton(
|
||||
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_back,
|
||||
primaryContentDescription = context.getString(R.string.browser_menu_back),
|
||||
primaryImageTintResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
),
|
||||
isInPrimaryState = {
|
||||
context.components.core.sessionManager.selectedSession?.canGoBack ?: true
|
||||
},
|
||||
secondaryImageTintResource = ThemeManager.resolveAttribute(
|
||||
R.attr.disabled,
|
||||
context
|
||||
),
|
||||
disableInSecondaryState = true
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Back)
|
||||
}
|
||||
|
||||
val forward = BrowserMenuItemToolbar.TwoStateButton(
|
||||
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
|
||||
primaryContentDescription = context.getString(R.string.browser_menu_forward),
|
||||
|
@ -93,143 +89,227 @@ class DefaultToolbarMenu(
|
|||
}
|
||||
}
|
||||
|
||||
BrowserMenuItemToolbar(listOf(back, forward, refresh))
|
||||
}
|
||||
|
||||
private val menuItems by lazy {
|
||||
val items = mutableListOf(
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_help),
|
||||
R.drawable.ic_help,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Help)
|
||||
},
|
||||
|
||||
BrowserMenuHighlightableItem(
|
||||
label = context.getString(R.string.browser_menu_settings),
|
||||
imageResource = R.drawable.ic_settings,
|
||||
iconTintColorResource = if (hasAccountProblem)
|
||||
R.color.sync_error_text_color else
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context),
|
||||
textColorResource = if (hasAccountProblem)
|
||||
R.color.sync_error_text_color else
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context),
|
||||
highlight = if (hasAccountProblem) {
|
||||
BrowserMenuHighlightableItem.Highlight(
|
||||
endImageResource = R.drawable.ic_alert,
|
||||
backgroundResource = R.drawable.sync_error_background_with_ripple,
|
||||
colorResource = R.color.sync_error_background_color
|
||||
)
|
||||
} else null
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Settings)
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_your_library),
|
||||
R.drawable.ic_library,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Library)
|
||||
},
|
||||
|
||||
BrowserMenuImageSwitch(
|
||||
R.drawable.ic_desktop,
|
||||
context.getString(R.string.browser_menu_desktop_site),
|
||||
requestDesktopStateProvider
|
||||
) { checked ->
|
||||
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_add_to_homescreen),
|
||||
R.drawable.ic_add_to_homescreen,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.AddToHomeScreen)
|
||||
}.apply {
|
||||
visible = ::shouldShowAddToHomescreen
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_find_in_page),
|
||||
R.drawable.mozac_ic_search,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_private_tab),
|
||||
R.drawable.ic_private_browsing,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.NewPrivateTab)
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_new_tab),
|
||||
R.drawable.ic_new,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.NewTab)
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_share),
|
||||
R.drawable.mozac_ic_share,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
val share = BrowserMenuItemToolbar.Button(
|
||||
imageResource = R.drawable.mozac_ic_share,
|
||||
contentDescription = context.getString(R.string.browser_menu_share),
|
||||
iconTintColorResource = primaryTextColor(),
|
||||
listener = {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Share)
|
||||
},
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_report_issue),
|
||||
R.drawable.ic_report_issues,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.ReportIssue)
|
||||
}
|
||||
)
|
||||
|
||||
if ((context.asActivity() as? HomeActivity)?.browsingModeManager?.mode == BrowsingMode.Normal) {
|
||||
items.add(
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_save_to_collection),
|
||||
R.drawable.ic_tab_collection,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
|
||||
}
|
||||
)
|
||||
registerForIsBookmarkedUpdates(sessionManager)
|
||||
val bookmark = BrowserMenuItemToolbar.TwoStateButton(
|
||||
primaryImageResource = R.drawable.ic_bookmark_filled,
|
||||
primaryContentDescription = context.getString(R.string.browser_menu_edit_bookmark),
|
||||
primaryImageTintResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
),
|
||||
// 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 = { currentUrlIsBookmarked },
|
||||
secondaryImageResource = R.drawable.ic_bookmark_outline,
|
||||
secondaryContentDescription = context.getString(R.string.browser_menu_bookmark),
|
||||
secondaryImageTintResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
),
|
||||
disableInSecondaryState = false
|
||||
) {
|
||||
if (!currentUrlIsBookmarked) currentUrlIsBookmarked = true
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Bookmark)
|
||||
}
|
||||
|
||||
if (Settings.getInstance(context).shouldDeleteBrowsingDataOnQuit) {
|
||||
items.add(
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.delete_browsing_data_on_quit_action),
|
||||
R.drawable.ic_exit,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Quit)
|
||||
}
|
||||
)
|
||||
BrowserMenuItemToolbar(listOf(forward, bookmark, share, refresh))
|
||||
}
|
||||
|
||||
private val menuItems by lazy {
|
||||
// Predicates that are called once, during screen init
|
||||
val shouldShowSaveToCollection = (context.asActivity() as? HomeActivity)
|
||||
?.browsingModeManager?.mode == BrowsingMode.Normal
|
||||
val shouldDeleteDataOnQuit = Settings.getInstance(context)
|
||||
.shouldDeleteBrowsingDataOnQuit
|
||||
|
||||
// Predicates that need to be repeatedly called as the session changes
|
||||
fun shouldShowAddToHomescreen(): Boolean {
|
||||
return context.components.useCases.webAppUseCases.isPinningSupported() &&
|
||||
context.components.core.sessionManager.selectedSession != null
|
||||
}
|
||||
fun shouldShowReaderMode(): Boolean = sessionManager.selectedSession?.readerable ?: false
|
||||
fun shouldShowOpenInApp(): Boolean = sessionManager.selectedSession?.let { session ->
|
||||
val appLink =
|
||||
context.components.useCases.appLinksUseCases.appLinkRedirect
|
||||
appLink(session.url).hasExternalApp()
|
||||
} ?: false
|
||||
fun shouldShowReaderAppearance(): Boolean =
|
||||
sessionManager.selectedSession?.readerMode ?: false
|
||||
|
||||
items.add(
|
||||
BrowserMenuDivider()
|
||||
)
|
||||
|
||||
items.add(
|
||||
listOfNotNull(
|
||||
help,
|
||||
settings,
|
||||
library,
|
||||
desktopMode,
|
||||
addToHomescreen.apply { visible = ::shouldShowAddToHomescreen },
|
||||
findInPage,
|
||||
privateTab,
|
||||
newTab,
|
||||
reportIssue,
|
||||
if (shouldShowSaveToCollection) saveToCollection else null,
|
||||
if (shouldDeleteDataOnQuit) deleteDataOnQuit else null,
|
||||
readerMode.apply { visible = ::shouldShowReaderMode },
|
||||
readerAppearance.apply { visible = ::shouldShowReaderAppearance },
|
||||
openInApp.apply { visible = ::shouldShowOpenInApp },
|
||||
BrowserMenuDivider(),
|
||||
menuToolbar
|
||||
)
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
private fun shouldShowAddToHomescreen(): Boolean {
|
||||
return context.components.useCases.webAppUseCases.isPinningSupported() &&
|
||||
context.components.core.sessionManager.selectedSession != null
|
||||
private val help = BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_help),
|
||||
R.drawable.ic_help,
|
||||
primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Help)
|
||||
}
|
||||
|
||||
private val settings = BrowserMenuHighlightableItem(
|
||||
label = context.getString(R.string.browser_menu_settings),
|
||||
imageResource = R.drawable.ic_settings,
|
||||
iconTintColorResource = if (hasAccountProblem)
|
||||
R.color.sync_error_text_color else
|
||||
primaryTextColor(),
|
||||
textColorResource = if (hasAccountProblem)
|
||||
R.color.sync_error_text_color else
|
||||
primaryTextColor(),
|
||||
highlight = if (hasAccountProblem) {
|
||||
BrowserMenuHighlightableItem.Highlight(
|
||||
endImageResource = R.drawable.ic_alert,
|
||||
backgroundResource = R.drawable.sync_error_background_with_ripple,
|
||||
colorResource = R.color.sync_error_background_color
|
||||
)
|
||||
} else null
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Settings)
|
||||
}
|
||||
|
||||
private val library = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_your_library),
|
||||
imageResource = R.drawable.ic_library,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Library)
|
||||
}
|
||||
|
||||
private val desktopMode = BrowserMenuImageSwitch(
|
||||
imageResource = R.drawable.ic_desktop,
|
||||
label = context.getString(R.string.browser_menu_desktop_site),
|
||||
initialState = requestDesktopStateProvider
|
||||
) { checked ->
|
||||
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
|
||||
}
|
||||
|
||||
private val addToHomescreen = 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)
|
||||
}
|
||||
|
||||
private val findInPage = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_find_in_page),
|
||||
imageResource = R.drawable.mozac_ic_search,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
|
||||
}
|
||||
|
||||
private val privateTab = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_private_tab),
|
||||
imageResource = R.drawable.ic_private_browsing,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.NewPrivateTab)
|
||||
}
|
||||
|
||||
private val newTab = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_new_tab),
|
||||
imageResource = R.drawable.ic_new,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.NewTab)
|
||||
}
|
||||
|
||||
private val reportIssue = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_report_issue),
|
||||
imageResource = R.drawable.ic_report_issues,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.ReportIssue)
|
||||
}
|
||||
|
||||
private val saveToCollection = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_save_to_collection),
|
||||
imageResource = R.drawable.ic_tab_collection,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
|
||||
}
|
||||
|
||||
private val deleteDataOnQuit = BrowserMenuImageText(
|
||||
label = context.getString(R.string.delete_browsing_data_on_quit_action),
|
||||
imageResource = R.drawable.ic_exit,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Quit)
|
||||
}
|
||||
|
||||
private val readerMode = BrowserMenuImageSwitch(
|
||||
label = context.getString(R.string.browser_menu_read),
|
||||
imageResource = R.drawable.ic_readermode,
|
||||
initialState = readerModeStateProvider
|
||||
) { 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,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.ReaderModeAppearance)
|
||||
}
|
||||
|
||||
private val openInApp = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_open_app_link),
|
||||
imageResource = R.drawable.ic_app_links,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.OpenInApp)
|
||||
}
|
||||
|
||||
private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
|
||||
private fun registerForIsBookmarkedUpdates(sessionManager: SessionManager) {
|
||||
val observer = object : Session.Observer {
|
||||
override fun onUrlChanged(session: Session, url: String) {
|
||||
currentUrlIsBookmarked = false
|
||||
updateCurrentUrlIsBookmarked(url)
|
||||
}
|
||||
}
|
||||
|
||||
sessionManager.selectedSession?.url?.let { updateCurrentUrlIsBookmarked(it) }
|
||||
sessionManager.selectedSession?.register(observer, lifecycleOwner)
|
||||
}
|
||||
|
||||
private fun updateCurrentUrlIsBookmarked(newUrl: String) {
|
||||
isBookmarkedJob?.cancel()
|
||||
isBookmarkedJob = lifecycleOwner.lifecycleScope.launch {
|
||||
currentUrlIsBookmarked = bookmarksStorage
|
||||
.getBookmarksWithUrl(newUrl)
|
||||
.any { it.url == newUrl }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ interface ToolbarMenu {
|
|||
object SaveToCollection : Item()
|
||||
object AddToHomeScreen : Item()
|
||||
object Quit : Item()
|
||||
data class ReaderMode(val isChecked: Boolean) : Item()
|
||||
object OpenInApp : Item()
|
||||
object Bookmark : Item()
|
||||
object ReaderModeAppearance : Item()
|
||||
}
|
||||
|
||||
val menuBuilder: BrowserMenuBuilder
|
||||
|
|
|
@ -99,60 +99,58 @@ class CustomTabToolbarMenu(
|
|||
private val menuItems by lazy {
|
||||
listOf(
|
||||
menuToolbar,
|
||||
|
||||
BrowserMenuDivider(),
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_share),
|
||||
R.drawable.mozac_ic_share,
|
||||
textColorResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
),
|
||||
iconTintColorResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Share)
|
||||
},
|
||||
|
||||
BrowserMenuSwitch(context.getString(R.string.browser_menu_desktop_site),
|
||||
{ session?.desktopMode ?: false }, { checked ->
|
||||
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
|
||||
}),
|
||||
|
||||
BrowserMenuImageText(
|
||||
context.getString(R.string.browser_menu_find_in_page),
|
||||
R.drawable.mozac_ic_search,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
{
|
||||
val appName = context.getString(R.string.app_name)
|
||||
context.getString(R.string.browser_menu_open_in_fenix, appName)
|
||||
}(),
|
||||
textColorResource = ThemeManager.resolveAttribute(
|
||||
R.attr.primaryText,
|
||||
context
|
||||
)
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.OpenInFenix)
|
||||
},
|
||||
|
||||
share,
|
||||
desktopMode,
|
||||
findInPage,
|
||||
openInFenix,
|
||||
BrowserMenuDivider(),
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
{
|
||||
val appName = context.getString(R.string.app_name)
|
||||
context.getString(R.string.browser_menu_powered_by2, appName).toUpperCase()
|
||||
}(),
|
||||
ToolbarMenu.CAPTION_TEXT_SIZE,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
)
|
||||
poweredBy
|
||||
)
|
||||
}
|
||||
|
||||
private val share = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_share),
|
||||
imageResource = R.drawable.mozac_ic_share,
|
||||
textColorResource = primaryTextColor(),
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Share)
|
||||
}
|
||||
|
||||
private val desktopMode = BrowserMenuSwitch(
|
||||
label = context.getString(R.string.browser_menu_desktop_site),
|
||||
initialState = { session?.desktopMode ?: false }, listener = { checked ->
|
||||
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
|
||||
})
|
||||
|
||||
private val findInPage = BrowserMenuImageText(
|
||||
label = context.getString(R.string.browser_menu_find_in_page),
|
||||
imageResource = R.drawable.mozac_ic_search,
|
||||
iconTintColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
|
||||
}
|
||||
|
||||
private val openInFenix = SimpleBrowserMenuItem(
|
||||
label = {
|
||||
val appName = context.getString(R.string.app_name)
|
||||
context.getString(R.string.browser_menu_open_in_fenix, appName)
|
||||
}(),
|
||||
textSize = ToolbarMenu.CAPTION_TEXT_SIZE,
|
||||
textColorResource = primaryTextColor()
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.OpenInFenix)
|
||||
}
|
||||
|
||||
private val poweredBy = SimpleBrowserMenuItem(
|
||||
label = {
|
||||
val appName = context.getString(R.string.app_name)
|
||||
context.getString(R.string.browser_menu_powered_by, appName).toUpperCase()
|
||||
}(),
|
||||
textSize = ToolbarMenu.CAPTION_TEXT_SIZE,
|
||||
textColorResource = primaryTextColor()
|
||||
)
|
||||
|
||||
private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.view.Gravity
|
|||
import android.view.View
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.airbnb.lottie.LottieCompositionFactory
|
||||
import com.airbnb.lottie.LottieDrawable
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
|
@ -28,7 +27,6 @@ class CustomTabsIntegration(
|
|||
toolbar: BrowserToolbar,
|
||||
sessionId: String,
|
||||
activity: Activity,
|
||||
quickActionbar: NestedScrollView,
|
||||
engineLayout: View,
|
||||
onItemTapped: (ToolbarMenu.Item) -> Unit = {}
|
||||
) : LifecycleAwareFeature, BackHandler {
|
||||
|
@ -52,9 +50,6 @@ class CustomTabsIntegration(
|
|||
}
|
||||
}
|
||||
|
||||
// Hide the Quick Action Bar.
|
||||
quickActionbar.visibility = View.GONE
|
||||
|
||||
val task = LottieCompositionFactory
|
||||
.fromRawRes(
|
||||
activity,
|
||||
|
|
|
@ -32,8 +32,6 @@ import org.mozilla.fenix.R
|
|||
import org.mozilla.fenix.browser.BaseBrowserFragment
|
||||
import org.mozilla.fenix.browser.CustomTabContextMenuCandidate
|
||||
import org.mozilla.fenix.browser.FenixSnackbarDelegate
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
|
||||
import org.mozilla.fenix.components.toolbar.BrowserToolbarInteractor
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
@ -63,12 +61,11 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
customTabSessionId?.let { customTabSessionId ->
|
||||
customTabsIntegration.set(
|
||||
feature = CustomTabsIntegration(
|
||||
requireComponents.core.sessionManager,
|
||||
toolbar,
|
||||
customTabSessionId,
|
||||
activity,
|
||||
view.nestedScrollQuickAction,
|
||||
view.swipeRefresh,
|
||||
sessionManager = requireComponents.core.sessionManager,
|
||||
toolbar = toolbar,
|
||||
sessionId = customTabSessionId,
|
||||
activity = activity,
|
||||
engineLayout = view.swipeRefresh,
|
||||
onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }
|
||||
),
|
||||
owner = this,
|
||||
|
@ -123,7 +120,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
}
|
||||
}
|
||||
|
||||
consumeFrom(browserStore) {
|
||||
consumeFrom(browserFragmentStore) {
|
||||
browserToolbarView.update(it)
|
||||
}
|
||||
|
||||
|
@ -146,11 +143,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
|
|||
return customTabsIntegration.onBackPressed() || super.removeSessionIfNeeded()
|
||||
}
|
||||
|
||||
override fun createBrowserToolbarViewInteractor(
|
||||
browserToolbarController: BrowserToolbarController,
|
||||
session: Session?
|
||||
) = BrowserToolbarInteractor(browserToolbarController)
|
||||
|
||||
override fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?) {
|
||||
val directions = ExternalAppBrowserFragmentDirections
|
||||
.actionExternalAppBrowserFragmentToQuickSettingsSheetDialogFragment(
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import kotlinx.android.synthetic.main.layout_quick_action_sheet.view.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
||||
const val POSITION_SNAP_BUFFER = 1f
|
||||
|
||||
class QuickActionSheet @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyle, defStyleRes) {
|
||||
|
||||
private val scope = MainScope()
|
||||
|
||||
private lateinit var quickActionSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.layout_quick_action_sheet, this)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
quickActionSheetBehavior =
|
||||
QuickActionSheetBehavior.from(quick_action_sheet.parent as NestedScrollView)
|
||||
quickActionSheetBehavior.isHideable = false
|
||||
setupHandle()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
private fun setupHandle() {
|
||||
quick_action_sheet_handle.setOnClickListener {
|
||||
quickActionSheetBehavior.state = when (quickActionSheetBehavior.state) {
|
||||
BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_COLLAPSED
|
||||
else -> BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
||||
|
||||
quick_action_sheet_handle.setAccessibilityDelegate(HandleAccessibilityDelegate(quickActionSheetBehavior))
|
||||
}
|
||||
|
||||
fun bounceSheet() {
|
||||
context.settings().incrementAutomaticBounceQuickActionSheetCount()
|
||||
scope.launch(Dispatchers.Main) {
|
||||
delay(BOUNCE_ANIMATION_DELAY_LENGTH)
|
||||
quickActionSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
delay(BOUNCE_ANIMATION_PAUSE_LENGTH)
|
||||
quickActionSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
}
|
||||
|
||||
class HandleAccessibilityDelegate(
|
||||
private val quickActionSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
|
||||
) : View.AccessibilityDelegate() {
|
||||
private var finalState = BottomSheetBehavior.STATE_COLLAPSED
|
||||
get() = when (quickActionSheetBehavior.state) {
|
||||
BottomSheetBehavior.STATE_EXPANDED,
|
||||
BottomSheetBehavior.STATE_HIDDEN,
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||
quickActionSheetBehavior.state
|
||||
}
|
||||
else -> field
|
||||
}
|
||||
set(value) {
|
||||
field = value
|
||||
quickActionSheetBehavior.state = value
|
||||
}
|
||||
|
||||
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
|
||||
finalState = when (action) {
|
||||
AccessibilityNodeInfo.ACTION_CLICK ->
|
||||
when (quickActionSheetBehavior.state) {
|
||||
BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_COLLAPSED
|
||||
else -> BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
AccessibilityNodeInfo.ACTION_COLLAPSE ->
|
||||
BottomSheetBehavior.STATE_COLLAPSED
|
||||
AccessibilityNodeInfo.ACTION_EXPAND ->
|
||||
BottomSheetBehavior.STATE_EXPANDED
|
||||
else -> return super.performAccessibilityAction(host, action, args)
|
||||
}
|
||||
|
||||
host?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info)
|
||||
info?.addAction(
|
||||
when (finalState) {
|
||||
BottomSheetBehavior.STATE_COLLAPSED,
|
||||
BottomSheetBehavior.STATE_HIDDEN -> AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND
|
||||
else -> AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val BOUNCE_ANIMATION_DELAY_LENGTH = 1000L
|
||||
const val BOUNCE_ANIMATION_PAUSE_LENGTH = 2000L
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,68 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.app.links.AppLinksUseCases
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.metrics
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
||||
|
||||
/**
|
||||
* An interface that handles the view manipulation of the QuickActionSheet, triggered by the Interactor
|
||||
*/
|
||||
interface QuickActionSheetController {
|
||||
fun handleShare()
|
||||
fun handleDownload()
|
||||
fun handleBookmark()
|
||||
fun handleOpenLink()
|
||||
}
|
||||
|
||||
class DefaultQuickActionSheetController(
|
||||
private val context: Context,
|
||||
private val navController: NavController,
|
||||
private val sessionManager: SessionManager,
|
||||
private val appLinksUseCases: AppLinksUseCases,
|
||||
private val bookmarkTapped: (Session) -> Unit
|
||||
) : QuickActionSheetController {
|
||||
|
||||
override fun handleShare() {
|
||||
context.metrics.track(Event.QuickActionSheetShareTapped)
|
||||
sessionManager.selectedSession?.url.let {
|
||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToShareFragment(it)
|
||||
navController.nav(R.id.browserFragment, directions)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleDownload() {
|
||||
context.metrics.track(Event.QuickActionSheetDownloadTapped)
|
||||
ItsNotBrokenSnack(context).showSnackbar(issueNumber = "348")
|
||||
}
|
||||
|
||||
override fun handleBookmark() {
|
||||
context.metrics.track(Event.QuickActionSheetBookmarkTapped)
|
||||
sessionManager.selectedSession?.let {
|
||||
bookmarkTapped(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleOpenLink() {
|
||||
context.metrics.track(Event.QuickActionSheetOpenInAppTapped)
|
||||
|
||||
val getRedirect = appLinksUseCases.appLinkRedirect
|
||||
sessionManager.selectedSession?.let {
|
||||
val redirect = getRedirect.invoke(it.url)
|
||||
redirect.appIntent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
appLinksUseCases.openAppLink.invoke(redirect)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.session.Session
|
||||
import org.mozilla.fenix.components.Components
|
||||
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
class QuickActionSheetSessionObserver(
|
||||
private val parentScope: CoroutineScope,
|
||||
private val components: Components,
|
||||
private val dispatch: (QuickActionSheetAction) -> Unit
|
||||
) : Session.Observer {
|
||||
|
||||
private var findBookmarkJob: Job? = null
|
||||
|
||||
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
|
||||
if (!loading) {
|
||||
updateBookmarkState(session)
|
||||
dispatch(QuickActionSheetAction.BounceNeededChange)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUrlChanged(session: Session, url: String) {
|
||||
updateBookmarkState(session)
|
||||
updateAppLinksState(session)
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches job to update the bookmark button on the quick action sheet.
|
||||
*/
|
||||
fun updateBookmarkState(session: Session) {
|
||||
findBookmarkJob?.cancel()
|
||||
findBookmarkJob = parentScope.launch(Main) {
|
||||
val found = findBookmarkedURL(session)
|
||||
dispatch(QuickActionSheetAction.BookmarkedStateChange(found))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the app link button on the quick action sheet.
|
||||
*/
|
||||
private fun updateAppLinksState(session: Session) {
|
||||
val url = session.url
|
||||
val appLinks = components.useCases.appLinksUseCases.appLinkRedirect
|
||||
dispatch(QuickActionSheetAction.AppLinkStateChange(appLinks(url).hasExternalApp()))
|
||||
}
|
||||
|
||||
private suspend fun findBookmarkedURL(session: Session): Boolean = withContext(IO) {
|
||||
try {
|
||||
val url = URL(session.url).toString()
|
||||
val list = components.core.bookmarksStorage.getBookmarksWithUrl(url)
|
||||
list.isNotEmpty() && list[0].url == url
|
||||
} catch (e: MalformedURLException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.layout_quick_action_sheet.*
|
||||
import kotlinx.android.synthetic.main.layout_quick_action_sheet.view.*
|
||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.components.toolbar.BrowserFragmentState
|
||||
|
||||
interface QuickActionSheetViewInteractor {
|
||||
fun onQuickActionSheetOpened()
|
||||
fun onQuickActionSheetClosed()
|
||||
fun onQuickActionSheetSharePressed()
|
||||
fun onQuickActionSheetDownloadPressed()
|
||||
fun onQuickActionSheetBookmarkPressed()
|
||||
fun onQuickActionSheetReadPressed()
|
||||
fun onQuickActionSheetAppearancePressed()
|
||||
fun onQuickActionSheetOpenLinkPressed()
|
||||
}
|
||||
|
||||
/**
|
||||
* View for the quick action sheet that slides out from the toolbar.
|
||||
*/
|
||||
class QuickActionSheetView(
|
||||
override val containerView: ViewGroup,
|
||||
private val interactor: QuickActionSheetViewInteractor
|
||||
) : LayoutContainer, View.OnClickListener {
|
||||
|
||||
val view: NestedScrollView = LayoutInflater.from(containerView.context)
|
||||
.inflate(R.layout.component_quick_action_sheet, containerView, true)
|
||||
.findViewById(R.id.nestedScrollQuickAction)
|
||||
|
||||
private val quickActionSheet = view.quick_action_sheet as QuickActionSheet
|
||||
private val quickActionSheetBehavior = QuickActionSheetBehavior.from(nestedScrollQuickAction)
|
||||
|
||||
init {
|
||||
quickActionSheetBehavior.setQuickActionSheetCallback(object :
|
||||
QuickActionSheetBehavior.QuickActionSheetCallback {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
updateImportantForAccessibility(newState)
|
||||
|
||||
if (newState == QuickActionSheetBehavior.STATE_EXPANDED) {
|
||||
interactor.onQuickActionSheetOpened()
|
||||
} else if (newState == QuickActionSheetBehavior.STATE_COLLAPSED) {
|
||||
interactor.onQuickActionSheetClosed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
animateOverlay(slideOffset)
|
||||
}
|
||||
})
|
||||
|
||||
updateImportantForAccessibility(quickActionSheetBehavior.state)
|
||||
|
||||
quick_action_share.setOnClickListener(this)
|
||||
quick_action_downloads.setOnClickListener(this)
|
||||
quick_action_bookmark.setOnClickListener(this)
|
||||
quick_action_read.setOnClickListener(this)
|
||||
quick_action_appearance.setOnClickListener(this)
|
||||
quick_action_open_app_link.setOnClickListener(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks from quick action buttons
|
||||
*/
|
||||
override fun onClick(button: View) {
|
||||
when (button.id) {
|
||||
R.id.quick_action_share -> interactor.onQuickActionSheetSharePressed()
|
||||
R.id.quick_action_downloads -> interactor.onQuickActionSheetDownloadPressed()
|
||||
R.id.quick_action_bookmark -> interactor.onQuickActionSheetBookmarkPressed()
|
||||
R.id.quick_action_read -> interactor.onQuickActionSheetReadPressed()
|
||||
R.id.quick_action_appearance -> interactor.onQuickActionSheetAppearancePressed()
|
||||
R.id.quick_action_open_app_link -> interactor.onQuickActionSheetOpenLinkPressed()
|
||||
else -> return
|
||||
}
|
||||
quickActionSheetBehavior.state = QuickActionSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes alpha of overlay based on new offset of this sheet within [-1,1] range.
|
||||
*/
|
||||
private fun animateOverlay(offset: Float) {
|
||||
quick_action_overlay.alpha = (1 - offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the important for accessibility flag on the buttons container,
|
||||
* depending on if the sheet is opened or closed.
|
||||
*/
|
||||
private fun updateImportantForAccessibility(state: Int) {
|
||||
view.quick_action_buttons_layout.importantForAccessibility = when (state) {
|
||||
QuickActionSheetBehavior.STATE_COLLAPSED, QuickActionSheetBehavior.STATE_HIDDEN ->
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
else ->
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
|
||||
}
|
||||
}
|
||||
|
||||
fun update(state: BrowserFragmentState) {
|
||||
val quickActionSheetState = state.quickActionSheetState
|
||||
view.quick_action_read.isVisible = quickActionSheetState.readable
|
||||
view.quick_action_read.isSelected = quickActionSheetState.readerActive
|
||||
view.quick_action_read.text = view.context.getString(
|
||||
if (quickActionSheetState.readerActive) R.string.quick_action_read_close else R.string.quick_action_read
|
||||
)
|
||||
notifyReaderModeButton(quickActionSheetState.readable)
|
||||
|
||||
view.quick_action_appearance.isVisible = quickActionSheetState.readerActive
|
||||
|
||||
view.quick_action_bookmark.isSelected = quickActionSheetState.bookmarked
|
||||
view.quick_action_bookmark.text = view.context.getString(
|
||||
if (quickActionSheetState.bookmarked) {
|
||||
R.string.quick_action_bookmark_edit
|
||||
} else {
|
||||
R.string.quick_action_bookmark
|
||||
}
|
||||
)
|
||||
|
||||
if (quickActionSheetState.bounceNeeded && view.context.settings().shouldAutoBounceQuickActionSheet) {
|
||||
quickActionSheet.bounceSheet()
|
||||
}
|
||||
|
||||
view.quick_action_open_app_link.apply {
|
||||
visibility = if (quickActionSheetState.isAppLink) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifyReaderModeButton(readable: Boolean) {
|
||||
val settings = view.context.settings().preferences
|
||||
val shouldNotifyKey = view.context.getString(R.string.pref_key_reader_mode_notification)
|
||||
|
||||
@DrawableRes
|
||||
val readerTwoStateDrawableRes = if (readable && settings.getBoolean(shouldNotifyKey, true)) {
|
||||
quickActionSheet.bounceSheet()
|
||||
settings.edit { putBoolean(shouldNotifyKey, false) }
|
||||
R.drawable.reader_two_state_with_notification
|
||||
} else {
|
||||
R.drawable.reader_two_state
|
||||
}
|
||||
|
||||
view.quick_action_read.putCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
top = view.context.getDrawable(readerTwoStateDrawableRes)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -132,9 +132,6 @@ class Settings private constructor(
|
|||
get() = trackingProtectionOnboardingCount < trackingProtectionOnboardingMaximumCount &&
|
||||
!trackingProtectionOnboardingShownThisSession
|
||||
|
||||
val shouldAutoBounceQuickActionSheet: Boolean
|
||||
get() = autoBounceQuickActionSheetCount < autoBounceMaximumCount
|
||||
|
||||
val shouldShowSecurityPinWarningSync: Boolean
|
||||
get() = loginsSecureWarningSyncCount < showLoginsSecureWarningSyncMaxCount
|
||||
|
||||
|
@ -225,12 +222,6 @@ class Settings private constructor(
|
|||
else -> appContext.getString(R.string.preference_light_theme)
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = PRIVATE)
|
||||
internal val autoBounceQuickActionSheetCount by intPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_bounce_quick_action),
|
||||
default = 0
|
||||
)
|
||||
|
||||
@VisibleForTesting(otherwise = PRIVATE)
|
||||
internal val loginsSecureWarningSyncCount by intPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_logins_secure_warning_sync),
|
||||
|
@ -257,13 +248,6 @@ class Settings private constructor(
|
|||
).apply()
|
||||
}
|
||||
|
||||
fun incrementAutomaticBounceQuickActionSheetCount() {
|
||||
preferences.edit().putInt(
|
||||
appContext.getPreferenceKey(R.string.pref_key_bounce_quick_action),
|
||||
autoBounceQuickActionSheetCount + 1
|
||||
).apply()
|
||||
}
|
||||
|
||||
val shouldShowSearchSuggestions by booleanPreference(
|
||||
appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions),
|
||||
default = true
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,9 +0,0 @@
|
|||
<?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/. -->
|
||||
<org.mozilla.fenix.quickactionsheet.QuickActionSheet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/quick_action_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom" />
|
|
@ -14,7 +14,7 @@
|
|||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/toolbar_and_qab_height">
|
||||
android:layout_marginBottom="@dimen/browser_toolbar_height">
|
||||
|
||||
<mozilla.components.concept.engine.EngineView
|
||||
android:id="@+id/engineView"
|
||||
|
@ -22,14 +22,6 @@
|
|||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nestedScrollQuickAction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:mozac_behavior_hideable="true"
|
||||
app:mozac_behavior_peekHeight="12dp"
|
||||
app:layout_behavior="org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/stubFindInPage"
|
||||
android:inflatedId="@+id/findInPageView"
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
<?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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/quick_action_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?foundation">
|
||||
|
||||
<View
|
||||
android:id="@+id/quick_action_sheet_faded_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/neutralFaded"
|
||||
android:focusable="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/quick_action_sheet_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="12dp"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/quick_action_sheet_handle"
|
||||
android:paddingTop="7dp"
|
||||
android:src="@drawable/ic_drawer_pull_tab"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/quick_action_sheet_faded_handle" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/quick_action_buttons_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@null"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/quick_action_sheet_handle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_share"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/quick_action_icon_share"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_share"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_downloads"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/library_icon_downloads_circle_background"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_download"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_bookmark"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/bookmark_two_state"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_bookmark"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_appearance"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/quick_action_icon_appearance"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_read_appearance"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_open_app_link"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/library_icon_app_links_circle_background"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_open_app_link"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/quick_action_read"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:drawableTop="@drawable/reader_two_state"
|
||||
android:drawablePadding="5dp"
|
||||
android:text="@string/quick_action_read"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/quick_action_overlay"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.0"
|
||||
android:background="?foundation"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/quick_action_buttons_layout" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -67,55 +67,4 @@
|
|||
<attr name="onboardingKey" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="QuickActionSheetBehavior_Layout">
|
||||
<!-- The height of the bottom sheet when it is collapsed. -->
|
||||
<attr format="dimension" name="mozac_behavior_peekHeight">
|
||||
<!-- Peek at the 16:9 ratio keyline of its parent -->
|
||||
<enum name="auto" value="-1"/>
|
||||
</attr>
|
||||
<!-- Whether this bottom sheet can be hidden by dragging it further downwards -->
|
||||
<attr format="boolean" name="mozac_behavior_hideable"/>
|
||||
<!-- Skip the collapsed state once expanded; no effect unless it is hideable -->
|
||||
<attr format="boolean" name="mozac_behavior_skipCollapsed"/>
|
||||
<!-- Whether height of expanded sheet wraps content or not -->
|
||||
<attr format="boolean" name="mozac_behavior_fitToContents"/>
|
||||
<!-- The ratio to be used to set the height of half-expanded state in proportion to parent, when
|
||||
fitToContents is false. Defaults to true half, 0.5, if not explicitly set. Ratio must be a
|
||||
float value between 0 and 1 and produce a half-expanded state height larger than the
|
||||
peek height for the half-expanded state to be operational -->
|
||||
<attr format="reference|float" name="mozac_behavior_halfExpandedRatio"/>
|
||||
<!-- The top offset of the BottomSheet in the expanded-state when fitsToContent is false.
|
||||
The default value is 0, which results in the sheet matching the parent's top. -->
|
||||
<attr format="reference|integer" name="mozac_behavior_expandedOffset"/>
|
||||
<!-- Shape appearance style reference for BottomSheet. Attribute declaration is in the shape
|
||||
package. -->
|
||||
<attr name="shapeAppearance"/>
|
||||
<!-- Shape appearance overlay style reference for BottomSheet. To be used to augment attributes
|
||||
declared in the shapeAppearance. Attribute declaration is in the shape package. -->
|
||||
<attr name="shapeAppearanceOverlay"/>
|
||||
<!-- Background color used by the BottomSheetBehavior background drawable when shape theming is
|
||||
enabled. Accepts a ColorStateList or ColorInt. If shape theming is not enabled,
|
||||
android:background should instead be utilized to set the background resource. -->
|
||||
<attr name="backgroundTint"/>
|
||||
<!-- Behavior properties will be saved and restored by evaluating each flag.
|
||||
usage: app:behavior_saveFlags=”hideable|skipCollapsed” -->
|
||||
<attr name="mozac_behavior_saveFlags">
|
||||
<!-- This flag will preserve the peekHeight on configuration change. -->
|
||||
<flag name="peekHeight" value="0x1"/>
|
||||
<!-- This flag will preserve the fitToContents boolean value on configuration change. -->
|
||||
<flag name="fitToContents" value="0x2"/>
|
||||
<!-- This flag will preserve the hideable boolean value on configuration change. -->
|
||||
<flag name="hideable" value="0x4"/>
|
||||
<!-- This flag will preserve the skipCollapsed boolean value on configuration change. -->
|
||||
<flag name="skipCollapsed" value="0x8"/>
|
||||
<!-- This flag will preserve the all the aforementioned values on configuration change. -->
|
||||
<flag name="all" value="-1"/>
|
||||
<!-- This flag will not preserve the aforementioned values on configuration change. The only
|
||||
value preserved will be the positional state, e.g. collapsed, hidden, expanded, etc.
|
||||
This is the default behavior. -->
|
||||
<flag name="none" value="0"/>
|
||||
</attr>
|
||||
<attr name="android:elevation"/>
|
||||
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
|
||||
<!-- Browser Toolbar -->
|
||||
<dimen name="browser_toolbar_height">56dp</dimen>
|
||||
<dimen name="toolbar_and_qab_height">67dp</dimen>
|
||||
|
||||
<!-- SignIn Component -->
|
||||
<dimen name="sign_in_button_padding">10dp</dimen>
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
<string name="browser_menu_refresh">Refresh</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Stop loading current website -->
|
||||
<string name="browser_menu_stop">Stop</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Bookmark the current page -->
|
||||
<string name="browser_menu_bookmark">Bookmark</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Un-bookmark the current page -->
|
||||
<string name="browser_menu_edit_bookmark">Edit bookmark</string>
|
||||
<!-- Browser menu button that sends a user to help articles -->
|
||||
<string name="browser_menu_help">Help</string>
|
||||
<!-- Browser menu button that sends a to a the what's new article -->
|
||||
|
@ -94,6 +98,12 @@
|
|||
<!-- 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 -->
|
||||
<string name="browser_menu_read">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 -->
|
||||
<string name="browser_menu_read_appearance">Appearance</string>
|
||||
|
||||
<!-- Search Fragment -->
|
||||
<!-- Button in the search view that lets a user search by scanning a QR code -->
|
||||
|
@ -302,27 +312,6 @@
|
|||
<!-- Preference for using following device theme -->
|
||||
<string name="preference_follow_device_theme">Follow device theme</string>
|
||||
|
||||
<!-- Quick Action Sheet -->
|
||||
<!-- Button in the browser chrome to share the current page -->
|
||||
<string name="quick_action_share">Share</string>
|
||||
<!-- Button in the browser chrome to download the current page -->
|
||||
<string name="quick_action_download">Download</string>
|
||||
<!-- Button in the browser chrome in the browser to bookmark the current page -->
|
||||
<string name="quick_action_bookmark">Bookmark</string>
|
||||
<!-- Button in the browser chrome in the browser to edit the bookmark of the current page -->
|
||||
<string name="quick_action_bookmark_edit">Edit Bookmark</string>
|
||||
<!-- Button in the browser chrome in the browser to put the the current page in reader mode -->
|
||||
<string name="quick_action_read">Read</string>
|
||||
<!-- Button in the browser chrome to exit reader mode -->
|
||||
<string name="quick_action_read_close">Close</string>
|
||||
<!-- Button in the browser chrome to open the current page in an external app -->
|
||||
<string name="quick_action_open_app_link">Open in App</string>
|
||||
<!-- Button in the browser chrome to configure reader mode appearance e.g. the used font type and size -->
|
||||
<string name="quick_action_read_appearance">Appearance</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Quick action sheet handle to provide users
|
||||
with shortcuts to commonly used actions such as Share, Bookmark etc.. -->
|
||||
<string name="quick_action_sheet_handle">Quick actions</string>
|
||||
|
||||
<!-- Library -->
|
||||
<!-- Option in Library to open Sessions page -->
|
||||
<string name="library_sessions">Sessions</string>
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Test
|
||||
|
||||
class BrowserFragmentStoreTest {
|
||||
|
||||
@Test
|
||||
fun bookmarkStateChange() = runBlocking {
|
||||
val initialState = defaultBrowserState()
|
||||
val store = BrowserFragmentStore(initialState)
|
||||
|
||||
store.dispatch(QuickActionSheetAction.BookmarkedStateChange(true)).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(store.state.quickActionSheetState.bookmarked, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readableStateChange() = runBlocking {
|
||||
val initialState = defaultBrowserState()
|
||||
val store = BrowserFragmentStore(initialState)
|
||||
|
||||
store.dispatch(QuickActionSheetAction.ReadableStateChange(true)).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(store.state.quickActionSheetState.readable, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readerActiveStateChange() = runBlocking {
|
||||
val initialState = defaultBrowserState()
|
||||
val store = BrowserFragmentStore(initialState)
|
||||
|
||||
store.dispatch(QuickActionSheetAction.ReaderActiveStateChange(true)).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(store.state.quickActionSheetState.readerActive, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bounceNeededChange() = runBlocking {
|
||||
val initialState = defaultBrowserState()
|
||||
val store = BrowserFragmentStore(initialState)
|
||||
|
||||
store.dispatch(QuickActionSheetAction.BounceNeededChange).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(store.state.quickActionSheetState.bounceNeeded, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appLinkStateChange() = runBlocking {
|
||||
val initialState = defaultBrowserState()
|
||||
val store = BrowserFragmentStore(initialState)
|
||||
|
||||
store.dispatch(QuickActionSheetAction.AppLinkStateChange(true)).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(store.state.quickActionSheetState.isAppLink, true)
|
||||
}
|
||||
|
||||
private fun defaultBrowserState(): BrowserFragmentState = BrowserFragmentState(
|
||||
quickActionSheetState = defaultQuickActionSheetState()
|
||||
)
|
||||
|
||||
private fun defaultQuickActionSheetState(): QuickActionSheetState = QuickActionSheetState(
|
||||
readable = false,
|
||||
bookmarked = false,
|
||||
readerActive = false,
|
||||
bounceNeeded = false,
|
||||
isAppLink = false
|
||||
)
|
||||
}
|
|
@ -1,52 +1,21 @@
|
|||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.session.Session
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.metrics
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetController
|
||||
|
||||
class BrowserInteractorTest {
|
||||
|
||||
lateinit var metrics: MetricController
|
||||
lateinit var context: Context
|
||||
lateinit var browserStore: BrowserFragmentStore
|
||||
lateinit var browserToolbarController: BrowserToolbarController
|
||||
lateinit var quickActionSheetController: QuickActionSheetController
|
||||
lateinit var readerModeController: ReaderModeController
|
||||
lateinit var session: Session
|
||||
lateinit var interactor: BrowserInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
metrics = mockk()
|
||||
context = mockk()
|
||||
browserStore = mockk(relaxed = true)
|
||||
browserToolbarController = mockk(relaxed = true)
|
||||
quickActionSheetController = mockk(relaxed = true)
|
||||
readerModeController = mockk(relaxed = true)
|
||||
session = mockk()
|
||||
interactor = BrowserInteractor(
|
||||
context,
|
||||
browserStore,
|
||||
browserToolbarController,
|
||||
quickActionSheetController,
|
||||
readerModeController,
|
||||
session
|
||||
browserToolbarController
|
||||
)
|
||||
every { context.metrics } returns metrics
|
||||
every { context.components.core.sessionManager.selectedSession } returns session
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -85,86 +54,4 @@ class BrowserInteractorTest {
|
|||
|
||||
verify { browserToolbarController.handleToolbarItemInteraction(item) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetOpened() {
|
||||
every { metrics.track(Event.QuickActionSheetOpened) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetOpened()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetOpened) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetClosed() {
|
||||
every { metrics.track(Event.QuickActionSheetClosed) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetClosed()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetClosed) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetSharePressed() {
|
||||
interactor.onQuickActionSheetSharePressed()
|
||||
|
||||
verify { quickActionSheetController.handleShare() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetDownloadPressed() {
|
||||
every { metrics.track(Event.QuickActionSheetDownloadTapped) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetDownloadPressed()
|
||||
|
||||
verify { quickActionSheetController.handleDownload() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetBookmarkPressed() {
|
||||
interactor.onQuickActionSheetBookmarkPressed()
|
||||
|
||||
verify { quickActionSheetController.handleBookmark() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetReadPressed() {
|
||||
every { session.readerMode } returns false
|
||||
every { metrics.track(Event.QuickActionSheetOpened) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetReadPressed()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetOpened) }
|
||||
verify { readerModeController.showReaderView() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetReadPressedWithActiveReaderMode() {
|
||||
every { session.readerMode } returns true
|
||||
every { metrics.track(Event.QuickActionSheetClosed) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetReadPressed()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetClosed) }
|
||||
verify { readerModeController.hideReaderView() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetOpenLinkPressed() {
|
||||
interactor.onQuickActionSheetOpenLinkPressed()
|
||||
|
||||
verify { quickActionSheetController.handleOpenLink() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onQuickActionSheetAppearancePressed() {
|
||||
every { metrics.track(Event.ReaderModeAppearanceOpened) } just Runs
|
||||
|
||||
interactor.onQuickActionSheetAppearancePressed()
|
||||
|
||||
verify {
|
||||
metrics.track(Event.ReaderModeAppearanceOpened)
|
||||
readerModeController.showControls()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.mozilla.fenix.components.toolbar
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
|
@ -52,7 +51,6 @@ import org.mozilla.fenix.ext.nav
|
|||
import org.mozilla.fenix.ext.toTab
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
|
||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
|
@ -72,8 +70,6 @@ class DefaultBrowserToolbarControllerTest {
|
|||
private val getSupportUrl: () -> String = { "https://supportUrl.org" }
|
||||
private val openInFenixIntent: Intent = mockk(relaxed = true)
|
||||
private val currentSessionAsTab: Tab = mockk(relaxed = true)
|
||||
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView> =
|
||||
mockk(relaxed = true)
|
||||
private val metrics: MetricController = mockk(relaxed = true)
|
||||
private val searchUseCases: SearchUseCases = mockk(relaxed = true)
|
||||
private val sessionUseCases: SessionUseCases = mockk(relaxed = true)
|
||||
|
@ -99,11 +95,14 @@ class DefaultBrowserToolbarControllerTest {
|
|||
customTabSession = null,
|
||||
getSupportUrl = getSupportUrl,
|
||||
openInFenixIntent = openInFenixIntent,
|
||||
bottomSheetBehavior = bottomSheetBehavior,
|
||||
scope = scope,
|
||||
browserLayout = browserLayout,
|
||||
swipeRefresh = swipeRefreshLayout,
|
||||
tabCollectionStorage = tabCollectionStorage
|
||||
tabCollectionStorage = tabCollectionStorage,
|
||||
bookmarkTapped = mockk(),
|
||||
readerModeController = mockk(),
|
||||
sessionManager = mockk(),
|
||||
store = mockk()
|
||||
)
|
||||
|
||||
mockkStatic(
|
||||
|
@ -338,7 +337,6 @@ class DefaultBrowserToolbarControllerTest {
|
|||
|
||||
controller.handleToolbarItemInteraction(item)
|
||||
|
||||
verify { bottomSheetBehavior.state = QuickActionSheetBehavior.STATE_COLLAPSED }
|
||||
verify { findInPageLauncher() }
|
||||
verify { metrics.track(Event.FindInPageOpened) }
|
||||
}
|
||||
|
@ -466,11 +464,14 @@ class DefaultBrowserToolbarControllerTest {
|
|||
customTabSession = currentSession,
|
||||
getSupportUrl = getSupportUrl,
|
||||
openInFenixIntent = openInFenixIntent,
|
||||
bottomSheetBehavior = bottomSheetBehavior,
|
||||
scope = scope,
|
||||
browserLayout = browserLayout,
|
||||
swipeRefresh = swipeRefreshLayout,
|
||||
tabCollectionStorage = tabCollectionStorage
|
||||
tabCollectionStorage = tabCollectionStorage,
|
||||
bookmarkTapped = mockk(),
|
||||
readerModeController = mockk(),
|
||||
sessionManager = mockk(),
|
||||
store = mockk()
|
||||
)
|
||||
|
||||
val sessionManager: SessionManager = mockk(relaxed = true)
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.app.links.AppLinksUseCases
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.ext.metrics
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
||||
|
||||
class DefaultQuickActionSheetControllerTest {
|
||||
private val context: HomeActivity = mockk(relaxed = true)
|
||||
private val navController: NavController = mockk(relaxed = true)
|
||||
private val sessionManager: SessionManager = mockk(relaxed = true)
|
||||
private val currentSession: Session = mockk(relaxed = true)
|
||||
private val appLinksUseCases: AppLinksUseCases = mockk(relaxed = true)
|
||||
private val bookmarkTapped: (Session) -> Unit = mockk(relaxed = true)
|
||||
private val metrics: MetricController = mockk(relaxed = true)
|
||||
|
||||
private lateinit var controller: DefaultQuickActionSheetController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
controller = DefaultQuickActionSheetController(
|
||||
context,
|
||||
navController,
|
||||
sessionManager,
|
||||
appLinksUseCases,
|
||||
bookmarkTapped
|
||||
)
|
||||
|
||||
every { sessionManager.selectedSession } returns currentSession
|
||||
|
||||
every { context.metrics } returns metrics
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleShare() {
|
||||
controller.handleShare()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetShareTapped) }
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.browserFragment,
|
||||
BrowserFragmentDirections.actionBrowserFragmentToShareFragment(currentSession.url)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDownload() {
|
||||
controller.handleDownload()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetDownloadTapped) }
|
||||
verify { ItsNotBrokenSnack(context).showSnackbar(issueNumber = "348") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleBookmark() {
|
||||
controller.handleBookmark()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetBookmarkTapped) }
|
||||
verify { bookmarkTapped(currentSession) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleOpenLink() {
|
||||
controller.handleOpenLink()
|
||||
|
||||
verify { metrics.track(Event.QuickActionSheetOpenInAppTapped) }
|
||||
verify { appLinksUseCases.appLinkRedirect.invoke(currentSession.url) }
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.app.links.AppLinkRedirect
|
||||
import mozilla.components.feature.app.links.AppLinksUseCases
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.components.Components
|
||||
import org.mozilla.fenix.components.toolbar.BrowserFragmentStore
|
||||
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class QuickActionSheetSessionObserverTest {
|
||||
|
||||
private lateinit var components: Components
|
||||
private lateinit var appLinkRedirect: AppLinksUseCases.GetAppLinkRedirect
|
||||
private lateinit var store: BrowserFragmentStore
|
||||
private lateinit var dispatch: (QuickActionSheetAction) -> Unit
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
components = mockk(relaxed = true)
|
||||
appLinkRedirect = mockk(relaxed = true)
|
||||
store = mockk(relaxed = true)
|
||||
dispatch = { store.dispatch(it) }
|
||||
|
||||
every { components.useCases.appLinksUseCases.appLinkRedirect } returns appLinkRedirect
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onLoadingStateChanged dispatches BounceNeededChange and updates bookmark button`() {
|
||||
val session: Session = mockk()
|
||||
val observer = spyk(QuickActionSheetSessionObserver(mockk(), components, dispatch))
|
||||
every { observer.updateBookmarkState(session) } just Runs
|
||||
|
||||
observer.onLoadingStateChanged(session, true)
|
||||
verify(exactly = 0) { store.dispatch(QuickActionSheetAction.BounceNeededChange) }
|
||||
|
||||
observer.onLoadingStateChanged(session, false)
|
||||
verify { observer.updateBookmarkState(session) }
|
||||
verify { store.dispatch(QuickActionSheetAction.BounceNeededChange) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onUrlChanged updates bookmark and app link buttons`() {
|
||||
val url = "https://example.com"
|
||||
val session: Session = mockk()
|
||||
every { session.url } returns url
|
||||
|
||||
val observer = spyk(QuickActionSheetSessionObserver(mockk(), components, dispatch))
|
||||
every { observer.updateBookmarkState(session) } just Runs
|
||||
every { appLinkRedirect.invoke(url) } returns AppLinkRedirect(mockk(), "", false)
|
||||
|
||||
observer.onUrlChanged(session, "")
|
||||
verify { observer.updateBookmarkState(session) }
|
||||
verify { appLinkRedirect.invoke("https://example.com") }
|
||||
verify { store.dispatch(QuickActionSheetAction.AppLinkStateChange(true)) }
|
||||
}
|
||||
}
|
|
@ -140,39 +140,6 @@ class SettingsTest {
|
|||
assertTrue(settings.isTelemetryEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun autoBounceQuickActionSheetCount() {
|
||||
// When just created
|
||||
// Then
|
||||
assertEquals(0, settings.autoBounceQuickActionSheetCount)
|
||||
|
||||
// When
|
||||
settings.incrementAutomaticBounceQuickActionSheetCount()
|
||||
settings.incrementAutomaticBounceQuickActionSheetCount()
|
||||
|
||||
// Then
|
||||
assertEquals(2, settings.autoBounceQuickActionSheetCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldAutoBounceQuickActionSheet() {
|
||||
// When just created
|
||||
// Then
|
||||
assertTrue(settings.shouldAutoBounceQuickActionSheet)
|
||||
|
||||
// When
|
||||
settings.incrementAutomaticBounceQuickActionSheetCount()
|
||||
|
||||
// Then
|
||||
assertTrue(settings.shouldAutoBounceQuickActionSheet)
|
||||
|
||||
// When
|
||||
settings.incrementAutomaticBounceQuickActionSheetCount()
|
||||
|
||||
// Then
|
||||
assertFalse(settings.shouldAutoBounceQuickActionSheet)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showLoginsDialogWarningSync() {
|
||||
// When just created
|
||||
|
|
|
@ -111,12 +111,6 @@ The following metrics are added to the ping:
|
|||
| qr_scanner.navigation_denied |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "deny" on the prompt, putting the user back to the scanning view |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-03-01 |
|
||||
| qr_scanner.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the QR scanner |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-03-01 |
|
||||
| qr_scanner.prompt_displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user scanned a QR code, causing a confirmation prompt to display asking if they want to navigate to the page |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-03-01 |
|
||||
| quick_action_sheet.bookmark_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the bookmark button |[1](https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466)||2020-03-01 |
|
||||
| quick_action_sheet.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the quick action sheet UI |[1](https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466)||2020-03-01 |
|
||||
| quick_action_sheet.download_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the download button |[1](https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466)||2020-03-01 |
|
||||
| quick_action_sheet.open_app_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the open in app button |[1](https://github.com/mozilla-mobile/fenix/pull/4629)||2020-03-01 |
|
||||
| quick_action_sheet.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the quick action sheet UI |[1](https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466)||2020-03-01 |
|
||||
| quick_action_sheet.share_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the share button |[1](https://github.com/mozilla-mobile/fenix/pull/1362#issuecomment-479668466)||2020-03-01 |
|
||||
| reader_mode.appearance |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the appearance button |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-03-01 |
|
||||
| reader_mode.available |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Reader mode is available for the current page |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-03-01 |
|
||||
| reader_mode.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed reader mode |[1](https://github.com/mozilla-mobile/fenix/pull/4328)||2020-03-01 |
|
||||
|
|
Loading…
Reference in New Issue