For #167: Improves home to browser animation
parent
a3d40b7045
commit
e96732604b
|
@ -321,7 +321,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
|
||||||
BrowserDirection.FromGlobal ->
|
BrowserDirection.FromGlobal ->
|
||||||
NavGraphDirections.actionGlobalBrowser(customTabSessionId)
|
NavGraphDirections.actionGlobalBrowser(customTabSessionId)
|
||||||
BrowserDirection.FromHome ->
|
BrowserDirection.FromHome ->
|
||||||
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(customTabSessionId)
|
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(customTabSessionId, true)
|
||||||
BrowserDirection.FromSearch ->
|
BrowserDirection.FromSearch ->
|
||||||
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(customTabSessionId)
|
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(customTabSessionId)
|
||||||
BrowserDirection.FromSettings ->
|
BrowserDirection.FromSettings ->
|
||||||
|
|
|
@ -6,8 +6,6 @@ package org.mozilla.fenix.browser
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -15,11 +13,9 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavDirections
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
@ -89,6 +85,7 @@ import org.mozilla.fenix.ext.sessionsOfType
|
||||||
import org.mozilla.fenix.ext.settings
|
import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
import org.mozilla.fenix.theme.ThemeManager
|
import org.mozilla.fenix.theme.ThemeManager
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base fragment extended by [BrowserFragment].
|
* Base fragment extended by [BrowserFragment].
|
||||||
|
@ -98,6 +95,7 @@ import org.mozilla.fenix.theme.ThemeManager
|
||||||
@Suppress("TooManyFunctions", "LargeClass")
|
@Suppress("TooManyFunctions", "LargeClass")
|
||||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer {
|
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer {
|
||||||
protected lateinit var browserFragmentStore: BrowserFragmentStore
|
protected lateinit var browserFragmentStore: BrowserFragmentStore
|
||||||
|
private lateinit var browserAnimator: BrowserAnimator
|
||||||
|
|
||||||
private var _browserInteractor: BrowserToolbarViewInteractor? = null
|
private var _browserInteractor: BrowserToolbarViewInteractor? = null
|
||||||
protected val browserInteractor: BrowserToolbarViewInteractor
|
protected val browserInteractor: BrowserToolbarViewInteractor
|
||||||
|
@ -164,6 +162,15 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
|
|
||||||
initializeEngineView(toolbarHeight)
|
initializeEngineView(toolbarHeight)
|
||||||
|
|
||||||
|
browserAnimator = BrowserAnimator(
|
||||||
|
fragment = WeakReference(this),
|
||||||
|
engineView = WeakReference(engineView),
|
||||||
|
swipeRefresh = WeakReference(swipeRefresh),
|
||||||
|
arguments = arguments!!
|
||||||
|
).apply {
|
||||||
|
beginAnimationIfNecessary()
|
||||||
|
}
|
||||||
|
|
||||||
return getSessionById()?.also { session ->
|
return getSessionById()?.also { session ->
|
||||||
val browserToolbarController = DefaultBrowserToolbarController(
|
val browserToolbarController = DefaultBrowserToolbarController(
|
||||||
store = browserFragmentStore,
|
store = browserFragmentStore,
|
||||||
|
@ -177,10 +184,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
browsingModeManager = (activity as HomeActivity).browsingModeManager,
|
browsingModeManager = (activity as HomeActivity).browsingModeManager,
|
||||||
sessionManager = requireComponents.core.sessionManager,
|
sessionManager = requireComponents.core.sessionManager,
|
||||||
findInPageLauncher = { findInPageIntegration.withFeature { it.launch() } },
|
findInPageLauncher = { findInPageIntegration.withFeature { it.launch() } },
|
||||||
browserLayout = view.browserLayout,
|
|
||||||
engineView = engineView,
|
engineView = engineView,
|
||||||
swipeRefresh = swipeRefresh,
|
swipeRefresh = swipeRefresh,
|
||||||
adjustBackgroundAndNavigate = ::adjustBackgroundAndNavigate,
|
browserAnimator = browserAnimator,
|
||||||
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
|
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
|
||||||
getSupportUrl = {
|
getSupportUrl = {
|
||||||
SupportUtils.getSumoURLForTopic(
|
SupportUtils.getSumoURLForTopic(
|
||||||
|
@ -499,26 +505,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
view: View
|
view: View
|
||||||
): List<ContextMenuCandidate>
|
): List<ContextMenuCandidate>
|
||||||
|
|
||||||
private fun adjustBackgroundAndNavigate(directions: NavDirections) {
|
|
||||||
context?.let {
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
// isAdded check is necessary because of a bug in viewLifecycleOwner. See AC#3828
|
|
||||||
if (!this@BaseBrowserFragment.isAdded) return@launch
|
|
||||||
|
|
||||||
engineView.captureThumbnail { bitmap ->
|
|
||||||
if (!this@BaseBrowserFragment.isAdded) return@captureThumbnail
|
|
||||||
|
|
||||||
// If the bitmap is null, the best we can do to reduce the flash is set transparent
|
|
||||||
swipeRefresh.background = bitmap?.toDrawable(it.resources)
|
|
||||||
?: ColorDrawable(Color.TRANSPARENT)
|
|
||||||
|
|
||||||
engineView.asView().visibility = View.GONE
|
|
||||||
findNavController().nav(R.id.browserFragment, directions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
override fun onSessionSelected(session: Session) {
|
override fun onSessionSelected(session: Session) {
|
||||||
(activity as HomeActivity).updateThemeForSession(session)
|
(activity as HomeActivity).updateThemeForSession(session)
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* 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.browser
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import androidx.core.animation.doOnEnd
|
||||||
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.concept.engine.EngineView
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles properly animating the browser engine based on `SHOULD_ANIMATE_FLAG` passed in through
|
||||||
|
* nav arguments.
|
||||||
|
*/
|
||||||
|
class BrowserAnimator(
|
||||||
|
private val fragment: WeakReference<Fragment>,
|
||||||
|
private val engineView: WeakReference<EngineView>,
|
||||||
|
private val swipeRefresh: WeakReference<View>,
|
||||||
|
private val arguments: Bundle
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val viewLifeCycleScope: LifecycleCoroutineScope?
|
||||||
|
get() = fragment.get()?.viewLifecycleOwner?.lifecycleScope
|
||||||
|
|
||||||
|
private val unwrappedEngineView: EngineView?
|
||||||
|
get() = engineView.get()
|
||||||
|
|
||||||
|
private val unwrappedSwipeRefresh: View?
|
||||||
|
get() = swipeRefresh.get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the browser animation to run if necessary (based on the SHOULD_ANIMATE_FLAG). Also
|
||||||
|
* removes the flag from the bundle so it is not played on future entries into the fragment.
|
||||||
|
*/
|
||||||
|
fun beginAnimationIfNecessary() {
|
||||||
|
val shouldAnimate = arguments.getBoolean(SHOULD_ANIMATE_FLAG, false)
|
||||||
|
if (shouldAnimate) {
|
||||||
|
viewLifeCycleScope?.launch(Dispatchers.Main) {
|
||||||
|
delay(ANIMATION_DELAY)
|
||||||
|
captureEngineViewAndDrawStatically {
|
||||||
|
animateBrowserEngine(unwrappedSwipeRefresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unwrappedSwipeRefresh?.alpha = 1f
|
||||||
|
unwrappedEngineView?.asView()?.visibility = View.VISIBLE
|
||||||
|
unwrappedSwipeRefresh?.background = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details exactly how the browserEngine animation should look and plays it.
|
||||||
|
*/
|
||||||
|
private fun animateBrowserEngine(browserEngine: View?) {
|
||||||
|
val valueAnimator = ValueAnimator.ofFloat(0f, END_ANIMATOR_VALUE)
|
||||||
|
|
||||||
|
valueAnimator.addUpdateListener {
|
||||||
|
browserEngine?.scaleX = STARTING_XY_SCALE + XY_SCALE_MULTIPLIER * it.animatedFraction
|
||||||
|
browserEngine?.scaleY = STARTING_XY_SCALE + XY_SCALE_MULTIPLIER * it.animatedFraction
|
||||||
|
browserEngine?.alpha = it.animatedFraction
|
||||||
|
}
|
||||||
|
|
||||||
|
valueAnimator.doOnEnd {
|
||||||
|
unwrappedEngineView?.asView()?.visibility = View.VISIBLE
|
||||||
|
unwrappedSwipeRefresh?.background = null
|
||||||
|
arguments.putBoolean(SHOULD_ANIMATE_FLAG, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueAnimator.interpolator = DecelerateInterpolator()
|
||||||
|
valueAnimator.duration = ANIMATION_DURATION
|
||||||
|
valueAnimator.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the swipeRefresh background a screenshot of the engineView in its current state.
|
||||||
|
* This allows us to "animate" the engineView.
|
||||||
|
*/
|
||||||
|
fun captureEngineViewAndDrawStatically(onComplete: () -> Unit) {
|
||||||
|
unwrappedEngineView?.asView()?.context.let {
|
||||||
|
viewLifeCycleScope?.launch {
|
||||||
|
// isAdded check is necessary because of a bug in viewLifecycleOwner. See AC#3828
|
||||||
|
if (!fragment.isAdded()) { return@launch }
|
||||||
|
unwrappedEngineView?.captureThumbnail { bitmap ->
|
||||||
|
if (!fragment.isAdded()) { return@captureThumbnail }
|
||||||
|
|
||||||
|
unwrappedSwipeRefresh?.apply {
|
||||||
|
alpha = 0f
|
||||||
|
// If the bitmap is null, the best we can do to reduce the flash is set transparent
|
||||||
|
background = bitmap?.toDrawable(context.resources)
|
||||||
|
?: ColorDrawable(Color.TRANSPARENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
onComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun WeakReference<Fragment>.isAdded(): Boolean {
|
||||||
|
val unwrapped = get()
|
||||||
|
return unwrapped != null && unwrapped.isAdded
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SHOULD_ANIMATE_FLAG = "shouldAnimate"
|
||||||
|
private const val ANIMATION_DELAY = 50L
|
||||||
|
private const val ANIMATION_DURATION = 115L
|
||||||
|
private const val END_ANIMATOR_VALUE = 500f
|
||||||
|
private const val XY_SCALE_MULTIPLIER = .05f
|
||||||
|
private const val STARTING_XY_SCALE = .95f
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||||
view.browserLayout.transitionName = "$TAB_ITEM_TRANSITION_NAME${getSessionById()?.id}"
|
|
||||||
startPostponedEnterTransition()
|
startPostponedEnterTransition()
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,8 @@ package org.mozilla.fenix.components.toolbar
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavDirections
|
|
||||||
import androidx.navigation.NavOptions
|
|
||||||
import androidx.navigation.fragment.FragmentNavigator
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -30,6 +22,7 @@ import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.support.ktx.kotlin.isUrl
|
import mozilla.components.support.ktx.kotlin.isUrl
|
||||||
import org.mozilla.fenix.NavGraphDirections
|
import org.mozilla.fenix.NavGraphDirections
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.browser.BrowserAnimator
|
||||||
import org.mozilla.fenix.browser.BrowserFragment
|
import org.mozilla.fenix.browser.BrowserFragment
|
||||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||||
|
@ -57,6 +50,8 @@ interface BrowserToolbarController {
|
||||||
fun handleTabCounterClick()
|
fun handleTabCounterClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias onComplete = () -> Unit
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
class DefaultBrowserToolbarController(
|
class DefaultBrowserToolbarController(
|
||||||
private val store: BrowserFragmentStore,
|
private val store: BrowserFragmentStore,
|
||||||
|
@ -66,9 +61,8 @@ class DefaultBrowserToolbarController(
|
||||||
private val browsingModeManager: BrowsingModeManager,
|
private val browsingModeManager: BrowsingModeManager,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
private val findInPageLauncher: () -> Unit,
|
private val findInPageLauncher: () -> Unit,
|
||||||
private val browserLayout: ViewGroup,
|
|
||||||
private val engineView: EngineView,
|
private val engineView: EngineView,
|
||||||
private val adjustBackgroundAndNavigate: (NavDirections) -> Unit,
|
private val browserAnimator: BrowserAnimator,
|
||||||
private val swipeRefresh: SwipeRefreshLayout,
|
private val swipeRefresh: SwipeRefreshLayout,
|
||||||
private val customTabSession: Session?,
|
private val customTabSession: Session?,
|
||||||
private val getSupportUrl: () -> String,
|
private val getSupportUrl: () -> String,
|
||||||
|
@ -89,12 +83,14 @@ class DefaultBrowserToolbarController(
|
||||||
internal var ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
internal var ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
override fun handleToolbarPaste(text: String) {
|
override fun handleToolbarPaste(text: String) {
|
||||||
adjustBackgroundAndNavigate.invoke(
|
browserAnimator.captureEngineViewAndDrawStatically {
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||||
sessionId = currentSession?.id,
|
sessionId = currentSession?.id,
|
||||||
pastedText = text
|
pastedText = text
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
navController.nav(R.id.browserFragment, directions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleToolbarPasteAndGo(text: String) {
|
override fun handleToolbarPasteAndGo(text: String) {
|
||||||
|
@ -112,9 +108,14 @@ class DefaultBrowserToolbarController(
|
||||||
activity.components.analytics.metrics.track(
|
activity.components.analytics.metrics.track(
|
||||||
Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)
|
Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)
|
||||||
)
|
)
|
||||||
adjustBackgroundAndNavigate.invoke(
|
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(currentSession?.id)
|
browserAnimator.captureEngineViewAndDrawStatically {
|
||||||
)
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||||
|
currentSession?.id
|
||||||
|
)
|
||||||
|
|
||||||
|
navController.nav(R.id.browserFragment, directions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleTabCounterClick() {
|
override fun handleTabCounterClick() {
|
||||||
|
@ -132,12 +133,14 @@ class DefaultBrowserToolbarController(
|
||||||
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession)
|
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession)
|
||||||
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
|
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
|
||||||
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
|
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
|
||||||
ToolbarMenu.Item.Settings -> adjustBackgroundAndNavigate.invoke(
|
ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically {
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
|
||||||
)
|
navController.nav(R.id.browserFragment, directions)
|
||||||
ToolbarMenu.Item.Library -> adjustBackgroundAndNavigate.invoke(
|
}
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment()
|
ToolbarMenu.Item.Library -> browserAnimator.captureEngineViewAndDrawStatically {
|
||||||
)
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment()
|
||||||
|
navController.nav(R.id.browserFragment, directions)
|
||||||
|
}
|
||||||
is ToolbarMenu.Item.RequestDesktop -> sessionUseCases.requestDesktopSite.invoke(
|
is ToolbarMenu.Item.RequestDesktop -> sessionUseCases.requestDesktopSite.invoke(
|
||||||
item.isChecked,
|
item.isChecked,
|
||||||
currentSession
|
currentSession
|
||||||
|
@ -297,28 +300,14 @@ class DefaultBrowserToolbarController(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateTabAndNavigateHome() {
|
private fun animateTabAndNavigateHome() {
|
||||||
// We need to dynamically add the options here because if you do it in XML it overwrites
|
browserAnimator.captureEngineViewAndDrawStatically {
|
||||||
val options = NavOptions.Builder().setPopUpTo(R.id.nav_graph, false)
|
if (!navController.popBackStack(R.id.homeFragment, false)) {
|
||||||
.setEnterAnim(R.anim.fade_in).build()
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToHomeFragment()
|
||||||
val extras = FragmentNavigator.Extras.Builder().addSharedElement(
|
navController.nav(
|
||||||
browserLayout,
|
R.id.browserFragment,
|
||||||
"${TAB_ITEM_TRANSITION_NAME}${currentSession?.id}"
|
directions,
|
||||||
).build()
|
null
|
||||||
engineView.captureThumbnail { bitmap ->
|
)
|
||||||
scope.launch {
|
|
||||||
// If the bitmap is null, the best we can do to reduce the flash is set transparent
|
|
||||||
swipeRefresh.background = bitmap?.toDrawable(activity.resources)
|
|
||||||
?: ColorDrawable(Color.TRANSPARENT)
|
|
||||||
engineView.asView().visibility = View.GONE
|
|
||||||
if (!navController.popBackStack(R.id.homeFragment, false)) {
|
|
||||||
navController.nav(
|
|
||||||
R.id.browserFragment,
|
|
||||||
R.id.action_browserFragment_to_homeFragment,
|
|
||||||
null,
|
|
||||||
options,
|
|
||||||
extras
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
||||||
import androidx.transition.TransitionInflater
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.fragment_home.*
|
import kotlinx.android.synthetic.main.fragment_home.*
|
||||||
|
@ -60,8 +59,8 @@ import mozilla.components.feature.media.ext.getSession
|
||||||
import mozilla.components.feature.media.state.MediaState
|
import mozilla.components.feature.media.state.MediaState
|
||||||
import mozilla.components.feature.media.state.MediaStateMachine
|
import mozilla.components.feature.media.state.MediaStateMachine
|
||||||
import mozilla.components.feature.tab.collections.TabCollection
|
import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.support.ktx.android.util.dpToPx
|
|
||||||
import mozilla.components.feature.top.sites.TopSite
|
import mozilla.components.feature.top.sites.TopSite
|
||||||
|
import mozilla.components.support.ktx.android.util.dpToPx
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
@ -145,9 +144,6 @@ class HomeFragment : Fragment() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
postponeEnterTransition()
|
postponeEnterTransition()
|
||||||
sharedElementEnterTransition =
|
|
||||||
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
|
|
||||||
.setDuration(SHARED_TRANSITION_MS)
|
|
||||||
|
|
||||||
val sessionObserver = BrowserSessionsObserver(sessionManager, singleSessionObserver) {
|
val sessionObserver = BrowserSessionsObserver(sessionManager, singleSessionObserver) {
|
||||||
emitSessionChanges()
|
emitSessionChanges()
|
||||||
|
@ -270,6 +266,10 @@ class HomeFragment : Fragment() {
|
||||||
sessionControlView!!.view.layoutManager?.onRestoreInstanceState(parcelable)
|
sessionControlView!!.view.layoutManager?.onRestoreInstanceState(parcelable)
|
||||||
}
|
}
|
||||||
homeViewModel.layoutManagerState = null
|
homeViewModel.layoutManagerState = null
|
||||||
|
|
||||||
|
// We have to delay so that the keyboard collapses and the view is resized before the
|
||||||
|
// animation from SearchFragment happens
|
||||||
|
delay(ANIMATION_DELAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
||||||
|
@ -309,14 +309,7 @@ class HomeFragment : Fragment() {
|
||||||
view.toolbar_wrapper.setOnClickListener {
|
view.toolbar_wrapper.setOnClickListener {
|
||||||
invokePendingDeleteJobs()
|
invokePendingDeleteJobs()
|
||||||
hideOnboardingIfNeeded()
|
hideOnboardingIfNeeded()
|
||||||
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(
|
navigateToSearch()
|
||||||
sessionId = null
|
|
||||||
)
|
|
||||||
val extras =
|
|
||||||
FragmentNavigator.Extras.Builder()
|
|
||||||
.addSharedElement(toolbar_wrapper, "toolbar_wrapper_transition")
|
|
||||||
.build()
|
|
||||||
nav(R.id.homeFragment, directions, extras)
|
|
||||||
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
|
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +543,12 @@ class HomeFragment : Fragment() {
|
||||||
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(
|
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(
|
||||||
sessionId = null
|
sessionId = null
|
||||||
)
|
)
|
||||||
nav(R.id.homeFragment, directions)
|
|
||||||
|
val extras = FragmentNavigator.Extras.Builder()
|
||||||
|
.addSharedElement(toolbar_wrapper, "toolbar_wrapper_transition")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
nav(R.id.homeFragment, directions, extras)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSettingsScreen() {
|
private fun openSettingsScreen() {
|
||||||
|
@ -897,12 +895,13 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val ANIMATION_DELAY = 100L
|
||||||
|
|
||||||
private const val NON_TAB_ITEM_NUM = 3
|
private const val NON_TAB_ITEM_NUM = 3
|
||||||
private const val ANIM_SCROLL_DELAY = 100L
|
private const val ANIM_SCROLL_DELAY = 100L
|
||||||
private const val ANIM_ON_SCREEN_DELAY = 200L
|
private const val ANIM_ON_SCREEN_DELAY = 200L
|
||||||
private const val FADE_ANIM_DURATION = 150L
|
private const val FADE_ANIM_DURATION = 150L
|
||||||
private const val ANIM_SNACKBAR_DELAY = 100L
|
private const val ANIM_SNACKBAR_DELAY = 100L
|
||||||
private const val SHARED_TRANSITION_MS = 200L
|
|
||||||
private const val CFR_WIDTH_DIVIDER = 1.7
|
private const val CFR_WIDTH_DIVIDER = 1.7
|
||||||
private const val CFR_Y_OFFSET = -20
|
private const val CFR_Y_OFFSET = -20
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.mozilla.fenix.home.sessioncontrol
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.FragmentNavigator
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -341,27 +340,19 @@ class DefaultSessionControlController(
|
||||||
invokePendingDeleteJobs()
|
invokePendingDeleteJobs()
|
||||||
val session = sessionManager.findSessionById(sessionId)
|
val session = sessionManager.findSessionById(sessionId)
|
||||||
sessionManager.select(session!!)
|
sessionManager.select(session!!)
|
||||||
val directions = HomeFragmentDirections.actionHomeFragmentToBrowserFragment(null)
|
activity.openToBrowser(BrowserDirection.FromHome)
|
||||||
val extras =
|
|
||||||
FragmentNavigator.Extras.Builder()
|
|
||||||
.addSharedElement(
|
|
||||||
tabView,
|
|
||||||
"$TAB_ITEM_TRANSITION_NAME$sessionId"
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
navController.nav(R.id.homeFragment, directions, extras)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleSelectTopSite(url: String) {
|
override fun handleSelectTopSite(url: String) {
|
||||||
|
invokePendingDeleteJobs()
|
||||||
metrics.track(Event.TopSiteOpenInNewTab)
|
metrics.track(Event.TopSiteOpenInNewTab)
|
||||||
if (url == SupportUtils.POCKET_TRENDING_URL) {
|
if (url == SupportUtils.POCKET_TRENDING_URL) { metrics.track(Event.PocketTopSiteClicked) }
|
||||||
metrics.track(Event.PocketTopSiteClicked)
|
activity.components.useCases.tabsUseCases.addTab.invoke(
|
||||||
}
|
url = url,
|
||||||
activity.components.useCases.tabsUseCases.addTab.invoke(url, true, true)
|
selectTab = true,
|
||||||
navController.nav(
|
startLoading = true
|
||||||
R.id.homeFragment,
|
|
||||||
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(null)
|
|
||||||
)
|
)
|
||||||
|
activity.openToBrowser(BrowserDirection.FromHome)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleShareTabs() {
|
override fun handleShareTabs() {
|
||||||
|
|
|
@ -59,11 +59,11 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
postponeEnterTransition()
|
postponeEnterTransition()
|
||||||
|
|
||||||
sharedElementEnterTransition =
|
sharedElementEnterTransition =
|
||||||
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
|
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
|
||||||
.setDuration(
|
.setDuration(SHARED_TRANSITION_MS)
|
||||||
SHARED_TRANSITION_MS
|
|
||||||
)
|
|
||||||
requireComponents.analytics.metrics.track(Event.InteractWithSearchURLArea)
|
requireComponents.analytics.metrics.track(Event.InteractWithSearchURLArea)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +346,7 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SHARED_TRANSITION_MS = 200L
|
private const val SHARED_TRANSITION_MS = 250L
|
||||||
private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1
|
private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class FragmentPreDrawManager(
|
||||||
fragment.postponeEnterTransition()
|
fragment.postponeEnterTransition()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun execute(code: () -> Unit) {
|
fun execute(code: suspend () -> Unit) {
|
||||||
fragment.view?.doOnPreDraw {
|
fragment.view?.doOnPreDraw {
|
||||||
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
||||||
code()
|
code()
|
||||||
|
|
|
@ -5,4 +5,4 @@
|
||||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:interpolator="@android:interpolator/decelerate_quad"
|
android:interpolator="@android:interpolator/decelerate_quad"
|
||||||
android:fromAlpha="0.0" android:toAlpha="1.0"
|
android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||||
android:duration="250" />
|
android:duration="150" />
|
|
@ -5,4 +5,4 @@
|
||||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:interpolator="@android:interpolator/accelerate_quad"
|
android:interpolator="@android:interpolator/accelerate_quad"
|
||||||
android:fromAlpha="1.0" android:toAlpha="0"
|
android:fromAlpha="1.0" android:toAlpha="0"
|
||||||
android:duration="250" />
|
android:duration="150" />
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<alpha
|
||||||
|
android:interpolator="@android:interpolator/linear"
|
||||||
|
android:fromAlpha="1.0" android:toAlpha="0"
|
||||||
|
android:duration="125" />
|
||||||
|
<scale
|
||||||
|
android:interpolator="@android:interpolator/linear"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%"
|
||||||
|
android:fromXScale="100%"
|
||||||
|
android:toXScale="113%"
|
||||||
|
android:fromYScale="100%"
|
||||||
|
android:toYScale="113%"
|
||||||
|
android:duration="125" />
|
||||||
|
</set>
|
|
@ -15,6 +15,7 @@
|
||||||
android:background="@drawable/toolbar_background_top"
|
android:background="@drawable/toolbar_background_top"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:transitionName="toolbar_wrapper_transition"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
|
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
|
||||||
app:browserToolbarClearColor="?primaryText"
|
app:browserToolbarClearColor="?primaryText"
|
||||||
|
|
|
@ -14,9 +14,11 @@
|
||||||
android:id="@+id/swipeRefresh"
|
android:id="@+id/swipeRefresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
<mozilla.components.concept.engine.EngineView
|
<mozilla.components.concept.engine.EngineView
|
||||||
android:id="@+id/engineView"
|
android:id="@+id/engineView"
|
||||||
|
android:visibility="gone"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeFragment_to_browserFragment"
|
android:id="@+id/action_homeFragment_to_browserFragment"
|
||||||
app:destination="@id/browserFragment"
|
app:destination="@id/browserFragment"
|
||||||
app:exitAnim="@anim/fade_out"
|
app:exitAnim="@anim/zoom_in_fade"
|
||||||
app:popEnterAnim="@anim/fade_in" />
|
app:popEnterAnim="@anim/fade_in" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeFragment_to_libraryFragment"
|
android:id="@+id/action_homeFragment_to_libraryFragment"
|
||||||
|
@ -187,6 +187,10 @@
|
||||||
android:name="activeSessionId"
|
android:name="activeSessionId"
|
||||||
app:argType="string"
|
app:argType="string"
|
||||||
app:nullable="true" />
|
app:nullable="true" />
|
||||||
|
<argument
|
||||||
|
android:name="shouldAnimate"
|
||||||
|
app:argType="boolean"
|
||||||
|
android:defaultValue="false" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_browserFragment_to_settingsFragment"
|
android:id="@+id/action_browserFragment_to_settingsFragment"
|
||||||
app:destination="@id/settingsFragment" />
|
app:destination="@id/settingsFragment" />
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.mozilla.fenix.components.toolbar
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
|
@ -16,12 +15,14 @@ import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.mockk.verifyOrder
|
import io.mockk.verifyOrder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||||
import kotlinx.coroutines.newSingleThreadContext
|
import kotlinx.coroutines.newSingleThreadContext
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.resetMain
|
import kotlinx.coroutines.test.resetMain
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
|
@ -37,6 +38,7 @@ import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.browser.BrowserAnimator
|
||||||
import org.mozilla.fenix.browser.BrowserFragment
|
import org.mozilla.fenix.browser.BrowserFragment
|
||||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||||
|
@ -59,7 +61,6 @@ import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||||
class DefaultBrowserToolbarControllerTest {
|
class DefaultBrowserToolbarControllerTest {
|
||||||
|
|
||||||
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
||||||
private var browserLayout: ViewGroup = mockk(relaxed = true)
|
|
||||||
private var swipeRefreshLayout: SwipeRefreshLayout = mockk(relaxed = true)
|
private var swipeRefreshLayout: SwipeRefreshLayout = mockk(relaxed = true)
|
||||||
private var activity: HomeActivity = mockk(relaxed = true)
|
private var activity: HomeActivity = mockk(relaxed = true)
|
||||||
private var analytics: Analytics = mockk(relaxed = true)
|
private var analytics: Analytics = mockk(relaxed = true)
|
||||||
|
@ -75,7 +76,7 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
private val searchUseCases: SearchUseCases = mockk(relaxed = true)
|
private val searchUseCases: SearchUseCases = mockk(relaxed = true)
|
||||||
private val sessionUseCases: SessionUseCases = mockk(relaxed = true)
|
private val sessionUseCases: SessionUseCases = mockk(relaxed = true)
|
||||||
private val scope: LifecycleCoroutineScope = mockk(relaxed = true)
|
private val scope: LifecycleCoroutineScope = mockk(relaxed = true)
|
||||||
private val adjustBackgroundAndNavigate: (NavDirections) -> Unit = mockk(relaxed = true)
|
private val browserAnimator: BrowserAnimator = mockk(relaxed = true)
|
||||||
private val snackbar = mockk<FenixSnackbar>(relaxed = true)
|
private val snackbar = mockk<FenixSnackbar>(relaxed = true)
|
||||||
private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true)
|
private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true)
|
||||||
private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true)
|
private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true)
|
||||||
|
@ -92,12 +93,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
browsingModeManager = browsingModeManager,
|
browsingModeManager = browsingModeManager,
|
||||||
findInPageLauncher = findInPageLauncher,
|
findInPageLauncher = findInPageLauncher,
|
||||||
engineView = engineView,
|
engineView = engineView,
|
||||||
adjustBackgroundAndNavigate = adjustBackgroundAndNavigate,
|
browserAnimator = browserAnimator,
|
||||||
customTabSession = null,
|
customTabSession = null,
|
||||||
getSupportUrl = getSupportUrl,
|
getSupportUrl = getSupportUrl,
|
||||||
openInFenixIntent = openInFenixIntent,
|
openInFenixIntent = openInFenixIntent,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
browserLayout = browserLayout,
|
|
||||||
swipeRefresh = swipeRefreshLayout,
|
swipeRefresh = swipeRefreshLayout,
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
topSiteStorage = topSiteStorage,
|
topSiteStorage = topSiteStorage,
|
||||||
|
@ -122,7 +122,9 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
every { activity.components.useCases.sessionUseCases } returns sessionUseCases
|
every { activity.components.useCases.sessionUseCases } returns sessionUseCases
|
||||||
every { activity.components.useCases.searchUseCases } returns searchUseCases
|
every { activity.components.useCases.searchUseCases } returns searchUseCases
|
||||||
every { activity.components.core.sessionManager.selectedSession } returns currentSession
|
every { activity.components.core.sessionManager.selectedSession } returns currentSession
|
||||||
every { adjustBackgroundAndNavigate.invoke(any()) } just Runs
|
|
||||||
|
val onComplete = slot<() -> Unit>()
|
||||||
|
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -133,12 +135,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
controller.handleToolbarPaste(pastedText)
|
controller.handleToolbarPaste(pastedText)
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
adjustBackgroundAndNavigate.invoke(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
sessionId = "1",
|
||||||
sessionId = "1",
|
pastedText = pastedText
|
||||||
pastedText = pastedText
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
navController.nav(R.id.browserFragment, directions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,11 +179,10 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
|
|
||||||
verify { metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)) }
|
verify { metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)) }
|
||||||
verify {
|
verify {
|
||||||
adjustBackgroundAndNavigate.invoke(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
|
||||||
sessionId = "1"
|
sessionId = "1"
|
||||||
)
|
)
|
||||||
)
|
navController.nav(R.id.browserFragment, directions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,16 +229,15 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleToolbarSettingsPress() {
|
fun handleToolbarSettingsPress() = runBlocking {
|
||||||
val item = ToolbarMenu.Item.Settings
|
val item = ToolbarMenu.Item.Settings
|
||||||
|
|
||||||
controller.handleToolbarItemInteraction(item)
|
controller.handleToolbarItemInteraction(item)
|
||||||
|
|
||||||
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SETTINGS)) }
|
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SETTINGS)) }
|
||||||
verify {
|
verify {
|
||||||
adjustBackgroundAndNavigate.invoke(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
|
navController.nav(R.id.browserFragment, directions)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,9 +249,8 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
|
|
||||||
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.LIBRARY)) }
|
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.LIBRARY)) }
|
||||||
verify {
|
verify {
|
||||||
adjustBackgroundAndNavigate.invoke(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment()
|
||||||
BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment()
|
navController.nav(R.id.browserFragment, directions)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,12 +302,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
browsingModeManager = browsingModeManager,
|
browsingModeManager = browsingModeManager,
|
||||||
findInPageLauncher = findInPageLauncher,
|
findInPageLauncher = findInPageLauncher,
|
||||||
engineView = engineView,
|
engineView = engineView,
|
||||||
adjustBackgroundAndNavigate = adjustBackgroundAndNavigate,
|
browserAnimator = browserAnimator,
|
||||||
customTabSession = null,
|
customTabSession = null,
|
||||||
getSupportUrl = getSupportUrl,
|
getSupportUrl = getSupportUrl,
|
||||||
openInFenixIntent = openInFenixIntent,
|
openInFenixIntent = openInFenixIntent,
|
||||||
scope = this,
|
scope = this,
|
||||||
browserLayout = browserLayout,
|
|
||||||
swipeRefresh = swipeRefreshLayout,
|
swipeRefresh = swipeRefreshLayout,
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
topSiteStorage = topSiteStorage,
|
topSiteStorage = topSiteStorage,
|
||||||
|
@ -500,12 +497,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
browsingModeManager = browsingModeManager,
|
browsingModeManager = browsingModeManager,
|
||||||
findInPageLauncher = findInPageLauncher,
|
findInPageLauncher = findInPageLauncher,
|
||||||
engineView = engineView,
|
engineView = engineView,
|
||||||
adjustBackgroundAndNavigate = adjustBackgroundAndNavigate,
|
browserAnimator = browserAnimator,
|
||||||
customTabSession = currentSession,
|
customTabSession = currentSession,
|
||||||
getSupportUrl = getSupportUrl,
|
getSupportUrl = getSupportUrl,
|
||||||
openInFenixIntent = openInFenixIntent,
|
openInFenixIntent = openInFenixIntent,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
browserLayout = browserLayout,
|
|
||||||
swipeRefresh = swipeRefreshLayout,
|
swipeRefresh = swipeRefreshLayout,
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
topSiteStorage = topSiteStorage,
|
topSiteStorage = topSiteStorage,
|
||||||
|
@ -542,12 +538,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
browsingModeManager = browsingModeManager,
|
browsingModeManager = browsingModeManager,
|
||||||
findInPageLauncher = findInPageLauncher,
|
findInPageLauncher = findInPageLauncher,
|
||||||
engineView = engineView,
|
engineView = engineView,
|
||||||
adjustBackgroundAndNavigate = adjustBackgroundAndNavigate,
|
browserAnimator = browserAnimator,
|
||||||
customTabSession = null,
|
customTabSession = null,
|
||||||
getSupportUrl = getSupportUrl,
|
getSupportUrl = getSupportUrl,
|
||||||
openInFenixIntent = openInFenixIntent,
|
openInFenixIntent = openInFenixIntent,
|
||||||
scope = testScope,
|
scope = testScope,
|
||||||
browserLayout = browserLayout,
|
|
||||||
swipeRefresh = swipeRefreshLayout,
|
swipeRefresh = swipeRefreshLayout,
|
||||||
tabCollectionStorage = tabCollectionStorage,
|
tabCollectionStorage = tabCollectionStorage,
|
||||||
topSiteStorage = topSiteStorage,
|
topSiteStorage = topSiteStorage,
|
||||||
|
|
|
@ -20,18 +20,17 @@ import kotlinx.coroutines.test.setMain
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
import mozilla.components.concept.engine.Engine
|
import mozilla.components.concept.engine.Engine
|
||||||
import mozilla.components.feature.tab.collections.TabCollection
|
import mozilla.components.feature.tab.collections.TabCollection
|
||||||
|
import mozilla.components.feature.tabs.TabsUseCases
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||||
import org.mozilla.fenix.components.TabCollectionStorage
|
import org.mozilla.fenix.components.TabCollectionStorage
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.nav
|
|
||||||
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
|
@ -61,6 +60,7 @@ class DefaultSessionControlControllerTest {
|
||||||
private val sessionManager: SessionManager = mockk(relaxed = true)
|
private val sessionManager: SessionManager = mockk(relaxed = true)
|
||||||
private val engine: Engine = mockk(relaxed = true)
|
private val engine: Engine = mockk(relaxed = true)
|
||||||
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
||||||
|
private val tabsUseCases: TabsUseCases = mockk(relaxed = true)
|
||||||
|
|
||||||
private lateinit var controller: DefaultSessionControlController
|
private lateinit var controller: DefaultSessionControlController
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ class DefaultSessionControlControllerTest {
|
||||||
every { activity.components.core.engine } returns engine
|
every { activity.components.core.engine } returns engine
|
||||||
every { activity.components.core.sessionManager } returns sessionManager
|
every { activity.components.core.sessionManager } returns sessionManager
|
||||||
every { activity.components.core.tabCollectionStorage } returns tabCollectionStorage
|
every { activity.components.core.tabCollectionStorage } returns tabCollectionStorage
|
||||||
|
every { activity.components.useCases.tabsUseCases } returns tabsUseCases
|
||||||
|
|
||||||
every { store.state } returns state
|
every { store.state } returns state
|
||||||
every { state.collections } returns emptyList()
|
every { state.collections } returns emptyList()
|
||||||
|
@ -194,10 +195,24 @@ class DefaultSessionControlControllerTest {
|
||||||
fun handleSelectTab() {
|
fun handleSelectTab() {
|
||||||
val tabView: View = mockk(relaxed = true)
|
val tabView: View = mockk(relaxed = true)
|
||||||
val sessionId = "hello"
|
val sessionId = "hello"
|
||||||
val directions = HomeFragmentDirections.actionHomeFragmentToBrowserFragment(null)
|
|
||||||
controller.handleSelectTab(tabView, sessionId)
|
controller.handleSelectTab(tabView, sessionId)
|
||||||
verify { invokePendingDeleteJobs() }
|
verify { invokePendingDeleteJobs() }
|
||||||
verify { navController.nav(R.id.homeFragment, directions) }
|
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handleSelectTopSite() {
|
||||||
|
val topSiteUrl = "mozilla.org"
|
||||||
|
|
||||||
|
controller.handleSelectTopSite(topSiteUrl)
|
||||||
|
verify { invokePendingDeleteJobs() }
|
||||||
|
verify { metrics.track(Event.TopSiteOpenInNewTab) }
|
||||||
|
verify { tabsUseCases.addTab.invoke(
|
||||||
|
topSiteUrl,
|
||||||
|
selectTab = true,
|
||||||
|
startLoading = true
|
||||||
|
) }
|
||||||
|
verify { activity.openToBrowser(BrowserDirection.FromHome) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue