For #1048 - Add ability to view tab history by long-pressing the back or forward button.
parent
2a0a11f740
commit
921b16233b
|
@ -6,11 +6,14 @@ package org.mozilla.fenix
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.text.format.DateUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.IdRes
|
||||
|
@ -30,6 +33,8 @@ import kotlinx.android.synthetic.main.activity_home.*
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
|
@ -139,6 +144,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
)
|
||||
}
|
||||
|
||||
// See onKeyDown for why this is necessary
|
||||
private var backLongPressJob: Job? = null
|
||||
|
||||
private lateinit var navigationToolbar: Toolbar
|
||||
|
||||
final override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -349,6 +357,50 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
super.onBackPressed()
|
||||
}
|
||||
|
||||
private fun isAndroidN(): Boolean =
|
||||
Build.VERSION.SDK_INT == Build.VERSION_CODES.N || Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1
|
||||
|
||||
private fun handleBackLongPress(): Boolean {
|
||||
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
||||
if (it is OnBackLongPressedListener && it.onBackLongPressed()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
final override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
// Inspired by https://searchfox.org/mozilla-esr68/source/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java#584-613
|
||||
// Android N has broken passing onKeyLongPress events for the back button, so we
|
||||
// instead implement the long press behavior ourselves
|
||||
// - For short presses, we cancel the callback in onKeyUp
|
||||
// - For long presses, the normal keypress is marked as cancelled, hence won't be handled elsewhere
|
||||
// (but Android still provides the haptic feedback), and the long press action is run
|
||||
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
backLongPressJob = lifecycleScope.launch {
|
||||
delay(ViewConfiguration.getLongPressTimeout().toLong())
|
||||
handleBackLongPress()
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
backLongPressJob?.cancel()
|
||||
}
|
||||
return super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
// onKeyLongPress is broken in Android N so we don't handle back button long presses here
|
||||
// for N. The version check ensures we don't handle back button long presses twice.
|
||||
if (!isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return handleBackLongPress()
|
||||
}
|
||||
return super.onKeyLongPress(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onUserLeaveHint() {
|
||||
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
||||
if (it is UserInteractionHandler && it.onHomePressed()) {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* 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
|
||||
|
||||
/**
|
||||
* Interface for features and fragments that want to handle long presses of the system back button
|
||||
*/
|
||||
interface OnBackLongPressedListener {
|
||||
|
||||
/**
|
||||
* Called when the system back button is long pressed.
|
||||
*
|
||||
* Note: This cannot be called when gesture navigation is enabled on Android 10+ due to system
|
||||
* limitations.
|
||||
*
|
||||
* @return true if the event was handled
|
||||
*/
|
||||
fun onBackLongPressed(): Boolean
|
||||
}
|
|
@ -77,6 +77,7 @@ import org.mozilla.fenix.FeatureFlags
|
|||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.OnBackLongPressedListener
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
|
||||
|
@ -116,7 +117,8 @@ import java.lang.ref.WeakReference
|
|||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer {
|
||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer,
|
||||
OnBackLongPressedListener {
|
||||
private lateinit var browserFragmentStore: BrowserFragmentStore
|
||||
private lateinit var browserAnimator: BrowserAnimator
|
||||
|
||||
|
@ -757,6 +759,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
removeSessionIfNeeded()
|
||||
}
|
||||
|
||||
override fun onBackLongPressed(): Boolean {
|
||||
findNavController().navigate(R.id.action_global_tabHistoryDialogFragment)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the external app session ID to be restored later in [onViewStateRestored].
|
||||
*/
|
||||
|
|
|
@ -170,7 +170,13 @@ class DefaultBrowserToolbarController(
|
|||
|
||||
Do exhaustive when (item) {
|
||||
ToolbarMenu.Item.Back -> sessionUseCases.goBack.invoke(currentSession)
|
||||
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession)
|
||||
is ToolbarMenu.Item.Forward -> {
|
||||
if (item.viewHistory) {
|
||||
navController.navigate(R.id.action_global_tabHistoryDialogFragment)
|
||||
} else {
|
||||
sessionUseCases.goForward.invoke(currentSession)
|
||||
}
|
||||
}
|
||||
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
|
||||
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
|
||||
ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically {
|
||||
|
@ -333,7 +339,7 @@ class DefaultBrowserToolbarController(
|
|||
private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) {
|
||||
val eventItem = when (item) {
|
||||
ToolbarMenu.Item.Back -> Event.BrowserMenuItemTapped.Item.BACK
|
||||
ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
|
||||
is ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
|
||||
ToolbarMenu.Item.Reload -> Event.BrowserMenuItemTapped.Item.RELOAD
|
||||
ToolbarMenu.Item.Stop -> Event.BrowserMenuItemTapped.Item.STOP
|
||||
ToolbarMenu.Item.Settings -> Event.BrowserMenuItemTapped.Item.SETTINGS
|
||||
|
|
|
@ -82,9 +82,10 @@ class DefaultToolbarMenu(
|
|||
session?.canGoForward ?: true
|
||||
},
|
||||
secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context),
|
||||
disableInSecondaryState = true
|
||||
disableInSecondaryState = true,
|
||||
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward)
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
|
||||
}
|
||||
|
||||
val refresh = BrowserMenuItemToolbar.TwoStateButton(
|
||||
|
|
|
@ -14,7 +14,7 @@ interface ToolbarMenu {
|
|||
object FindInPage : Item()
|
||||
object Share : Item()
|
||||
object Back : Item()
|
||||
object Forward : Item()
|
||||
data class Forward(val viewHistory: Boolean) : Item()
|
||||
object Reload : Item()
|
||||
object Stop : Item()
|
||||
object OpenInFenix : Item()
|
||||
|
|
|
@ -75,9 +75,10 @@ class CustomTabToolbarMenu(
|
|||
R.attr.disabled,
|
||||
context
|
||||
),
|
||||
disableInSecondaryState = true
|
||||
disableInSecondaryState = true,
|
||||
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward)
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
|
||||
}
|
||||
|
||||
val refresh = BrowserMenuItemToolbar.TwoStateButton(
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
data class TabHistoryItem(
|
||||
val title: String,
|
||||
val url: String,
|
||||
val index: Int,
|
||||
val isSelected: Boolean
|
||||
)
|
||||
|
||||
class TabHistoryAdapter(
|
||||
private val interactor: TabHistoryViewInteractor
|
||||
) : RecyclerView.Adapter<TabHistoryViewHolder>() {
|
||||
|
||||
var historyList: List<TabHistoryItem> = emptyList()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabHistoryViewHolder {
|
||||
val view =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.history_list_item, parent, false)
|
||||
return TabHistoryViewHolder(view, interactor)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TabHistoryViewHolder, position: Int) {
|
||||
holder.bind(historyList[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = historyList.size
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
interface TabHistoryController {
|
||||
fun handleGoToHistoryItem(item: TabHistoryItem)
|
||||
}
|
||||
|
||||
class DefaultTabHistoryController(
|
||||
private val navController: NavController,
|
||||
private val goToHistoryIndexUseCase: SessionUseCases.GoToHistoryIndexUseCase
|
||||
) : TabHistoryController {
|
||||
|
||||
override fun handleGoToHistoryItem(item: TabHistoryItem) {
|
||||
navController.popBackStack(R.id.browserFragment, false)
|
||||
goToHistoryIndexUseCase.invoke(item.index)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.android.synthetic.main.fragment_tab_history_dialog.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
||||
class TabHistoryDialogFragment : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, R.style.BottomSheet)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? = inflater.inflate(R.layout.fragment_tab_history_dialog, container, false)
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val controller = DefaultTabHistoryController(
|
||||
navController = findNavController(),
|
||||
goToHistoryIndexUseCase = requireComponents.useCases.sessionUseCases.goToHistoryIndex
|
||||
)
|
||||
val tabHistoryView = TabHistoryView(
|
||||
container = tabHistoryLayout,
|
||||
expandDialog = ::expand,
|
||||
interactor = TabHistoryInteractor(controller)
|
||||
)
|
||||
|
||||
consumeFrom(requireComponents.core.store) {
|
||||
tabHistoryView.updateState(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun expand() {
|
||||
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
class TabHistoryInteractor(
|
||||
private val controller: TabHistoryController
|
||||
) : TabHistoryViewInteractor {
|
||||
|
||||
override fun goToHistoryItem(item: TabHistoryItem) {
|
||||
controller.handleGoToHistoryItem(item)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_tabhistory.*
|
||||
import mozilla.components.browser.state.selector.selectedTab
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
interface TabHistoryViewInteractor {
|
||||
|
||||
/**
|
||||
* Jump to a specific index in the tab's history.
|
||||
*/
|
||||
fun goToHistoryItem(item: TabHistoryItem)
|
||||
}
|
||||
|
||||
class TabHistoryView(
|
||||
private val container: ViewGroup,
|
||||
private val expandDialog: () -> Unit,
|
||||
interactor: TabHistoryViewInteractor
|
||||
) : LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
get() = container
|
||||
|
||||
val view: View = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_tabhistory, container, true)
|
||||
|
||||
private val adapter = TabHistoryAdapter(interactor)
|
||||
private val layoutManager = object : LinearLayoutManager(view.context) {
|
||||
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
||||
super.onLayoutCompleted(state)
|
||||
currentIndex?.let { index ->
|
||||
// Force expansion of the dialog, otherwise scrolling to the current history item
|
||||
// won't work when its position is near the bottom of the recyclerview.
|
||||
expandDialog.invoke()
|
||||
// Also, attempt to center the current history item.
|
||||
val itemView = tabHistoryRecyclerView.findViewHolderForLayoutPosition(
|
||||
findFirstCompletelyVisibleItemPosition()
|
||||
)?.itemView
|
||||
val offset = tabHistoryRecyclerView.height / 2 - (itemView?.height ?: 0) / 2
|
||||
scrollToPositionWithOffset(index, offset)
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
reverseLayout = true
|
||||
}
|
||||
|
||||
private var currentIndex: Int? = null
|
||||
|
||||
init {
|
||||
tabHistoryRecyclerView.adapter = adapter
|
||||
tabHistoryRecyclerView.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
fun updateState(state: BrowserState) {
|
||||
state.selectedTab?.content?.history?.let { historyState ->
|
||||
currentIndex = historyState.currentIndex
|
||||
val items = historyState.items.mapIndexed { index, historyItem ->
|
||||
TabHistoryItem(
|
||||
title = historyItem.title,
|
||||
url = historyItem.uri,
|
||||
index = index,
|
||||
isSelected = index == historyState.currentIndex
|
||||
)
|
||||
}
|
||||
adapter.historyList = items
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.history_list_item.view.*
|
||||
|
||||
class TabHistoryViewHolder(
|
||||
private val view: View,
|
||||
private val interactor: TabHistoryViewInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(item: TabHistoryItem) {
|
||||
view.history_layout.overflowView.isVisible = false
|
||||
view.history_layout.urlView.text = item.url
|
||||
view.history_layout.loadFavicon(item.url)
|
||||
|
||||
view.history_layout.titleView.text = if (item.isSelected) {
|
||||
buildSpannedString {
|
||||
bold { append(item.title) }
|
||||
}
|
||||
} else {
|
||||
item.title
|
||||
}
|
||||
|
||||
view.setOnClickListener { interactor.goToHistoryItem(item) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tabHistoryWrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<View
|
||||
android:id="@+id/handle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/secondary_text_normal_theme"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.1" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tabHistoryRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="19dp"
|
||||
tools:listitem="@layout/history_list_item" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?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/. -->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tabHistoryLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
|
@ -102,6 +102,9 @@
|
|||
<action
|
||||
android:id="@+id/action_global_savedLoginsAuthFragment"
|
||||
app:destination="@id/savedLoginsAuthFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_tabHistoryDialogFragment"
|
||||
app:destination="@id/tabHistoryDialogFragment" />
|
||||
|
||||
<dialog
|
||||
android:id="@+id/tabTrayDialogFragment"
|
||||
|
@ -859,4 +862,7 @@
|
|||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/tabHistoryDialogFragment"
|
||||
android:name="org.mozilla.fenix.tabhistory.TabHistoryDialogFragment" />
|
||||
</navigation>
|
||||
|
|
|
@ -220,7 +220,7 @@ class DefaultBrowserToolbarControllerTest {
|
|||
|
||||
@Test
|
||||
fun handleToolbarForwardPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Forward
|
||||
val item = ToolbarMenu.Item.Forward(false)
|
||||
|
||||
val controller = createController(scope = this)
|
||||
controller.handleToolbarItemInteraction(item)
|
||||
|
@ -229,6 +229,17 @@ class DefaultBrowserToolbarControllerTest {
|
|||
verify { sessionUseCases.goForward(currentSession) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleToolbarForwardLongPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Forward(true)
|
||||
|
||||
val controller = createController(scope = this)
|
||||
controller.handleToolbarItemInteraction(item)
|
||||
|
||||
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.FORWARD)) }
|
||||
verify { navController.navigate(R.id.action_global_tabHistoryDialogFragment) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleToolbarReloadPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Reload
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import org.junit.Test
|
||||
|
||||
class TabHistoryControllerTest {
|
||||
|
||||
val sessionManager: SessionManager = mockk(relaxed = true)
|
||||
val navController: NavController = mockk(relaxed = true)
|
||||
val sessionUseCases = SessionUseCases(sessionManager)
|
||||
val goToHistoryIndexUseCase = sessionUseCases.goToHistoryIndex
|
||||
val controller = DefaultTabHistoryController(
|
||||
navController = navController,
|
||||
goToHistoryIndexUseCase = goToHistoryIndexUseCase
|
||||
)
|
||||
|
||||
val currentItem = TabHistoryItem(
|
||||
index = 0,
|
||||
title = "",
|
||||
url = "",
|
||||
isSelected = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun handleGoToHistoryIndex() {
|
||||
controller.handleGoToHistoryItem(currentItem)
|
||||
|
||||
verify { goToHistoryIndexUseCase.invoke(currentItem.index) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Test
|
||||
|
||||
class TabHistoryInteractorTest {
|
||||
|
||||
val controller: TabHistoryController = mockk(relaxed = true)
|
||||
val interactor = TabHistoryInteractor(controller)
|
||||
|
||||
@Test
|
||||
fun onGoToHistoryItem() {
|
||||
val item: TabHistoryItem = mockk()
|
||||
|
||||
interactor.goToHistoryItem(item)
|
||||
|
||||
verify { controller.handleGoToHistoryItem(item) }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue