1
0
Fork 0

For #6413: Adds more snackbar positioning logic (#7444)

* For #6413: Adds more snackbar positioning logic

* Refactor
master
Sawyer Blatz 2020-01-02 14:31:52 -08:00 committed by GitHub
parent f6e286fd9c
commit c7c4ad051a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 159 additions and 174 deletions

View File

@ -64,7 +64,6 @@ import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.BrowserSnackbarPresenter
import org.mozilla.fenix.components.FindInPageIntegration import org.mozilla.fenix.components.FindInPageIntegration
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
@ -160,7 +159,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
// browsing data on quit" is activated). After the deletion is over, the snackbar // browsing data on quit" is activated). After the deletion is over, the snackbar
// is dismissed. // is dismissed.
val snackbar: FenixSnackbar? = requireActivity().getRootView()?.let { v -> val snackbar: FenixSnackbar? = requireActivity().getRootView()?.let { v ->
FenixSnackbar.make(v, Snackbar.LENGTH_INDEFINITE) FenixSnackbar.makeWithToolbarPadding(v)
.setText(v.context.getString(R.string.deleting_browsing_data_in_progress)) .setText(v.context.getString(R.string.deleting_browsing_data_in_progress))
} }
@ -283,12 +282,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
download = download, download = download,
tryAgain = downloadFeature::tryAgain, tryAgain = downloadFeature::tryAgain,
onCannotOpenFile = { onCannotOpenFile = {
BrowserSnackbarPresenter(view).present( FenixSnackbar.makeWithToolbarPadding(view, Snackbar.LENGTH_SHORT)
text = context.getString(R.string.mozac_feature_downloads_could_not_open_file), .setText(context.getString(R.string.mozac_feature_downloads_could_not_open_file))
length = Snackbar.LENGTH_SHORT .show()
)
} }
) )
dialog.show() dialog.show()
} }
@ -520,9 +517,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
@CallSuper @CallSuper
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
return findInPageIntegration.onBackPressed() || return findInPageIntegration.onBackPressed() ||
fullScreenFeature.onBackPressed() || fullScreenFeature.onBackPressed() ||
sessionFeature.onBackPressed() || sessionFeature.onBackPressed() ||
removeSessionIfNeeded() removeSessionIfNeeded()
} }
/** /**
@ -703,17 +700,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
requireComponents.analytics.metrics.track(Event.AddBookmark) requireComponents.analytics.metrics.track(Event.AddBookmark)
view?.let { view -> view?.let { view ->
BrowserSnackbarPresenter(view).present( FenixSnackbar.makeWithToolbarPadding(view)
text = getString(R.string.bookmark_saved_snackbar), .setText(getString(R.string.bookmark_saved_snackbar))
action = { nav( .setAction(getString(R.string.edit_bookmark_snackbar_action)) {
R.id.browserFragment, nav(
BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment( R.id.browserFragment,
guid BrowserFragmentDirections.actionBrowserFragmentToBookmarkEditFragment(
)) guid
}, ))
actionName = getString(R.string.edit_bookmark_snackbar_action), }
length = Snackbar.LENGTH_LONG .show()
)
} }
} }
} }

View File

@ -30,7 +30,7 @@ import mozilla.components.support.base.feature.UserInteractionHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
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.ext.components import org.mozilla.fenix.ext.components
@ -230,10 +230,9 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
private fun showTabSavedToCollectionSnackbar() { private fun showTabSavedToCollectionSnackbar() {
view?.let { view -> view?.let { view ->
BrowserSnackbarPresenter(view).present( FenixSnackbar.makeWithToolbarPadding(view, Snackbar.LENGTH_SHORT)
text = view.context.getString(R.string.create_collection_tab_saved), .setText(view.context.getString(R.string.create_collection_tab_saved))
length = Snackbar.LENGTH_SHORT .show()
)
} }
} }
} }

View File

@ -6,9 +6,8 @@ package org.mozilla.fenix.browser
import android.view.View import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar
import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuCandidate
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
class FenixSnackbarDelegate(val view: View) : class FenixSnackbarDelegate(val view: View) :
ContextMenuCandidate.SnackbarDelegate { ContextMenuCandidate.SnackbarDelegate {
@ -20,17 +19,14 @@ class FenixSnackbarDelegate(val view: View) :
listener: ((v: View) -> Unit)? listener: ((v: View) -> Unit)?
) { ) {
if (listener != null && action != 0) { if (listener != null && action != 0) {
BrowserSnackbarPresenter(view).present( FenixSnackbar.makeWithToolbarPadding(view)
text = view.context.getString(text), .setText(view.context.getString(text))
length = Snackbar.LENGTH_LONG, .setAction(view.context.getString(action)) { listener.invoke(view) }
actionName = view.context.getString(action), .show()
action = { listener.invoke(view) }
)
} else { } else {
BrowserSnackbarPresenter(view).present( FenixSnackbar.makeWithToolbarPadding(view)
text = view.context.getString(text), .setText(view.context.getString(text))
length = Snackbar.LENGTH_LONG .show()
)
} }
} }
} }

View File

@ -32,11 +32,7 @@ class FenixSnackbar private constructor(
init { init {
view.background = null view.background = null
view.snackbar_layout.background = if (isError) { setAppropriateBackground(isError)
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_error_background)
} else {
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_background)
}
content.snackbar_btn.increaseTapArea(actionButtonIncreaseDps) content.snackbar_btn.increaseTapArea(actionButtonIncreaseDps)
@ -49,10 +45,22 @@ class FenixSnackbar private constructor(
) )
} }
fun setAppropriateBackground(isError: Boolean) {
view.snackbar_layout.background = if (isError) {
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_error_background)
} else {
AppCompatResources.getDrawable(context, R.drawable.fenix_snackbar_background)
}
}
fun setText(text: String) = this.apply { fun setText(text: String) = this.apply {
view.snackbar_text.text = text view.snackbar_text.text = text
} }
fun setLength(duration: Int) = this.apply {
this.duration = duration
}
fun setAction(text: String, action: () -> Unit) = this.apply { fun setAction(text: String, action: () -> Unit) = this.apply {
view.snackbar_btn.apply { view.snackbar_btn.apply {
setText(text) setText(text)
@ -90,6 +98,28 @@ class FenixSnackbar private constructor(
} }
} }
/**
* Considers BrowserToolbar for padding when making snackbar
*/
fun makeWithToolbarPadding(
view: View,
duration: Int = LENGTH_LONG,
isError: Boolean = false
): FenixSnackbar {
val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar
val toolbarHeight = view.context.resources
.getDimensionPixelSize(R.dimen.browser_toolbar_height)
return make(view, duration, isError).apply {
this.view.setPadding(
0,
0,
0,
if (shouldUseBottomToolbar) toolbarHeight else 0
)
}
}
// Use the same implementation of `Snackbar` // Use the same implementation of `Snackbar`
private fun findSuitableParent(_view: View?): ViewGroup? { private fun findSuitableParent(_view: View?): ViewGroup? {
var view = _view var view = _view
@ -147,35 +177,3 @@ private class FenixSnackbarCallback(
private const val animateOutDuration = 150L private const val animateOutDuration = 150L
} }
} }
/**
* This snackbar presenter should be used when displaying a snackbar that will appear in
* the BrowserFragment as it takes into account the position of the BrowserToolbar
*/
class BrowserSnackbarPresenter(
private val view: View
) {
fun present(
text: String,
length: Int = FenixSnackbar.LENGTH_LONG,
action: (() -> Unit)? = null,
actionName: String? = null,
isError: Boolean = false
) {
val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar
val toolbarHeight = view.context.resources
.getDimensionPixelSize(R.dimen.browser_toolbar_height)
FenixSnackbar.make(view, length, isError).apply {
if (action != null && actionName != null) setAction(actionName, action)
setText(text)
view.setPadding(
0,
0,
0,
if (shouldUseBottomToolbar) toolbarHeight else 0
)
show()
}
}
}

View File

@ -20,10 +20,10 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -41,12 +41,12 @@ import androidx.transition.TransitionInflater
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.*
import kotlinx.android.synthetic.main.fragment_home.view.* import kotlinx.android.synthetic.main.fragment_home.view.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.appservices.places.BookmarkRoot import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.menu.BrowserMenu import mozilla.components.browser.menu.BrowserMenu
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
@ -546,11 +546,7 @@ class HomeFragment : Fragment() {
deleteAndQuit( deleteAndQuit(
activity, activity,
lifecycleScope, lifecycleScope,
view?.let { view -> view?.let { view -> FenixSnackbar.makeWithToolbarPadding(view) }
FenixSnackbar.make(view, Snackbar.LENGTH_INDEFINITE)
.setText(view.context.getString(R.string.deleting_browsing_data_in_progress))
.setAnchorView(bottom_bar)
}
) )
} }
} }

View File

@ -17,7 +17,7 @@ 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
import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.Services import org.mozilla.fenix.components.Services
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
@ -46,7 +46,7 @@ interface BookmarkController {
class DefaultBookmarkController( class DefaultBookmarkController(
private val context: Context, private val context: Context,
private val navController: NavController, private val navController: NavController,
private val snackbarPresenter: BrowserSnackbarPresenter, private val snackbar: FenixSnackbar,
private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit, private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit,
private val invokePendingDeletion: () -> Unit private val invokePendingDeletion: () -> Unit
) : BookmarkController { ) : BookmarkController {
@ -72,13 +72,15 @@ class DefaultBookmarkController(
} }
override fun handleBookmarkSelected(node: BookmarkNode) { override fun handleBookmarkSelected(node: BookmarkNode) {
snackbarPresenter.present(resources.getString(R.string.bookmark_cannot_edit_root)) snackbar.setText(resources.getString(R.string.bookmark_cannot_edit_root))
snackbar.show()
} }
override fun handleCopyUrl(item: BookmarkNode) { override fun handleCopyUrl(item: BookmarkNode) {
val urlClipData = ClipData.newPlainText(item.url, item.url) val urlClipData = ClipData.newPlainText(item.url, item.url)
context.getSystemService<ClipboardManager>()?.primaryClip = urlClipData context.getSystemService<ClipboardManager>()?.primaryClip = urlClipData
snackbarPresenter.present(resources.getString(R.string.url_copied)) snackbar.setText(resources.getString(R.string.url_copied))
snackbar.show()
} }
override fun handleBookmarkSharing(item: BookmarkNode) { override fun handleBookmarkSharing(item: BookmarkNode) {

View File

@ -38,7 +38,7 @@ import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.UserInteractionHandler
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.bookmarkStorage import org.mozilla.fenix.ext.bookmarkStorage
@ -87,7 +87,7 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
bookmarksController = DefaultBookmarkController( bookmarksController = DefaultBookmarkController(
context = context!!, context = context!!,
navController = findNavController(), navController = findNavController(),
snackbarPresenter = BrowserSnackbarPresenter(view), snackbar = FenixSnackbar.make(view, FenixSnackbar.LENGTH_LONG),
deleteBookmarkNodes = ::deleteMulti, deleteBookmarkNodes = ::deleteMulti,
invokePendingDeletion = ::invokePendingDeletion invokePendingDeletion = ::invokePendingDeletion
), ),

View File

@ -37,7 +37,7 @@ import mozilla.components.concept.storage.BookmarkNodeType
import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getRootView import org.mozilla.fenix.ext.getRootView
@ -187,13 +187,14 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
.popBackStack() .popBackStack()
bookmarkNode?.let { bookmark -> bookmarkNode?.let { bookmark ->
BrowserSnackbarPresenter(activity.getRootView()!!).present( FenixSnackbar.makeWithToolbarPadding(activity.getRootView()!!)
getString( .setText(
R.string.bookmark_deletion_snackbar_message, getString(R.string.bookmark_deletion_snackbar_message,
bookmark.url?.toShortUrl(context.components.publicSuffixList) bookmark.url?.toShortUrl(context.components.publicSuffixList)
?: bookmark.title ?: bookmark.title
)
) )
) .show()
} }
} }
} }

View File

@ -18,7 +18,6 @@ import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.OAuthAccount
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.BrowserSnackbarPresenter
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.ext.showToolbar
@ -26,6 +25,9 @@ import org.mozilla.fenix.ext.showToolbar
@SuppressWarnings("TooManyFunctions") @SuppressWarnings("TooManyFunctions")
class TurnOnSyncFragment : Fragment(), AccountObserver { class TurnOnSyncFragment : Fragment(), AccountObserver {
private val safeArguments get() = requireNotNull(arguments)
private val args get() = TurnOnSyncFragmentArgs.fromBundle(safeArguments)
private val signInClickListener = View.OnClickListener { private val signInClickListener = View.OnClickListener {
requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext()) requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext())
// TODO The sign-in web content populates session history, // TODO The sign-in web content populates session history,
@ -74,9 +76,19 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
} }
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
BrowserSnackbarPresenter(requireView()).present( val snackbarText = requireContext().getString(R.string.sync_syncing_in_progress)
text = requireContext().getString(R.string.sync_syncing_in_progress), val snackbarLength = FenixSnackbar.LENGTH_SHORT
length = FenixSnackbar.LENGTH_SHORT
) // Since the snackbar can be presented in BrowserFragment or in SettingsFragment we must
// base our display method on the padSnackbar argument
if (args.padSnackbar) {
FenixSnackbar.makeWithToolbarPadding(requireView(), snackbarLength)
.setText(snackbarText)
.show()
} else {
FenixSnackbar.make(requireView(), snackbarLength)
.setText(snackbarText)
.show()
}
} }
} }

View File

@ -5,11 +5,13 @@
package org.mozilla.fenix.settings.deletebrowsingdata package org.mozilla.fenix.settings.deletebrowsingdata
import android.app.Activity import android.app.Activity
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.joinAll import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
@ -21,7 +23,11 @@ fun deleteAndQuit(activity: Activity, coroutineScope: CoroutineScope, snackbar:
val settings = activity.settings() val settings = activity.settings()
val controller = DefaultDeleteBrowsingDataController(activity, coroutineContext) val controller = DefaultDeleteBrowsingDataController(activity, coroutineContext)
snackbar?.show() snackbar?.apply {
setText(activity.getString(R.string.deleting_browsing_data_in_progress))
duration = Snackbar.LENGTH_INDEFINITE
show()
}
DeleteBrowsingDataOnQuitType.values().map { type -> DeleteBrowsingDataOnQuitType.values().map { type ->
launch { launch {

View File

@ -146,7 +146,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da
updateItemCounts() updateItemCounts()
FenixSnackbar.make(view!!, FenixSnackbar.LENGTH_SHORT) FenixSnackbar.makeWithToolbarPadding(requireView(), FenixSnackbar.LENGTH_SHORT)
.setText(resources.getString(R.string.preferences_delete_browsing_data_snackbar)) .setText(resources.getString(R.string.preferences_delete_browsing_data_snackbar))
.show() .show()

View File

@ -22,7 +22,7 @@ import mozilla.components.concept.sync.Device
import mozilla.components.concept.sync.TabData import mozilla.components.concept.sync.TabData
import mozilla.components.feature.accounts.push.SendTabUseCases import mozilla.components.feature.accounts.push.SendTabUseCases
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.nav
@ -53,7 +53,7 @@ interface ShareController {
* @param context [Context] used for various Android interactions. * @param context [Context] used for various Android interactions.
* @param shareData the list of [ShareData]s that can be shared. * @param shareData the list of [ShareData]s that can be shared.
* @param sendTabUseCases instance of [SendTabUseCases] which allows sending tabs to account devices. * @param sendTabUseCases instance of [SendTabUseCases] which allows sending tabs to account devices.
* @param snackbarPresenter - instance of [BrowserSnackbarPresenter] for displaying styled snackbars * @param snackbar - instance of [FenixSnackbar] for displaying styled snackbars
* @param navController - [NavController] used for navigation. * @param navController - [NavController] used for navigation.
* @param dismiss - callback signalling sharing can be closed. * @param dismiss - callback signalling sharing can be closed.
*/ */
@ -62,7 +62,7 @@ class DefaultShareController(
private val context: Context, private val context: Context,
private val shareData: List<ShareData>, private val shareData: List<ShareData>,
private val sendTabUseCases: SendTabUseCases, private val sendTabUseCases: SendTabUseCases,
private val snackbarPresenter: BrowserSnackbarPresenter, private val snackbar: FenixSnackbar,
private val navController: NavController, private val navController: NavController,
private val dismiss: (ShareController.Result) -> Unit private val dismiss: (ShareController.Result) -> Unit
) : ShareController { ) : ShareController {
@ -89,7 +89,8 @@ class DefaultShareController(
context.startActivity(intent) context.startActivity(intent)
ShareController.Result.SUCCESS ShareController.Result.SUCCESS
} catch (e: SecurityException) { } catch (e: SecurityException) {
snackbarPresenter.present(context.getString(R.string.share_error_snackbar)) snackbar.setText(context.getString(R.string.share_error_snackbar))
snackbar.show()
ShareController.Result.SHARE_ERROR ShareController.Result.SHARE_ERROR
} }
dismiss(result) dismiss(result)
@ -111,7 +112,8 @@ class DefaultShareController(
override fun handleSignIn() { override fun handleSignIn() {
context.metrics.track(Event.SignInToSendTab) context.metrics.track(Event.SignInToSendTab)
val directions = ShareFragmentDirections.actionShareFragmentToTurnOnSyncFragment() val directions =
ShareFragmentDirections.actionShareFragmentToTurnOnSyncFragment(padSnackbar = true)
navController.nav(R.id.shareFragment, directions) navController.nav(R.id.shareFragment, directions)
dismiss(ShareController.Result.DISMISSED) dismiss(ShareController.Result.DISMISSED)
} }
@ -132,21 +134,20 @@ class DefaultShareController(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun showSuccess() { fun showSuccess() {
snackbarPresenter.present( snackbar.apply {
getSuccessMessage(), setText(getSuccessMessage())
Snackbar.LENGTH_SHORT setLength(Snackbar.LENGTH_SHORT)
) show()
}
} }
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun showFailureWithRetryOption(operation: () -> Unit) { fun showFailureWithRetryOption(operation: () -> Unit) {
snackbarPresenter.present( snackbar.setText(context.getString(R.string.sync_sent_tab_error_snackbar))
text = context.getString(R.string.sync_sent_tab_error_snackbar), snackbar.setLength(Snackbar.LENGTH_LONG)
length = Snackbar.LENGTH_LONG, snackbar.setAction(context.getString(R.string.sync_sent_tab_error_snackbar_action), operation)
action = operation, snackbar.setAppropriateBackground(true)
actionName = context.getString(R.string.sync_sent_tab_error_snackbar_action), snackbar.show()
isError = true
)
} }
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)

View File

@ -21,7 +21,7 @@ import mozilla.components.browser.state.selector.findTabOrCustomTab
import mozilla.components.concept.engine.prompt.PromptRequest import mozilla.components.concept.engine.prompt.PromptRequest
import mozilla.components.feature.accounts.push.SendTabUseCases import mozilla.components.feature.accounts.push.SendTabUseCases
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.ext.getRootView import org.mozilla.fenix.ext.getRootView
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
@ -65,7 +65,7 @@ class ShareFragment : AppCompatDialogFragment() {
DefaultShareController( DefaultShareController(
context = requireContext(), context = requireContext(),
shareData = shareData, shareData = shareData,
snackbarPresenter = BrowserSnackbarPresenter(requireActivity().getRootView()!!), snackbar = FenixSnackbar.makeWithToolbarPadding(requireActivity().getRootView()!!),
navController = findNavController(), navController = findNavController(),
sendTabUseCases = SendTabUseCases(accountManager) sendTabUseCases = SendTabUseCases(accountManager)
) { result -> ) { result ->

View File

@ -444,6 +444,10 @@
android:id="@+id/turnOnSyncFragment" android:id="@+id/turnOnSyncFragment"
android:name="org.mozilla.fenix.settings.account.TurnOnSyncFragment" android:name="org.mozilla.fenix.settings.account.TurnOnSyncFragment"
android:label="@string/preferences_sync"> android:label="@string/preferences_sync">
<argument
android:name="padSnackbar"
app:argType="boolean"
android:defaultValue="false" />
<action <action
android:id="@+id/action_turnOnSyncFragment_to_pairFragment" android:id="@+id/action_turnOnSyncFragment_to_pairFragment"
app:destination="@id/pairFragment" /> app:destination="@id/pairFragment" />
@ -555,7 +559,8 @@
android:id="@+id/action_shareFragment_to_turnOnSyncFragment" android:id="@+id/action_shareFragment_to_turnOnSyncFragment"
app:destination="@+id/turnOnSyncFragment" app:destination="@+id/turnOnSyncFragment"
app:popUpTo="@id/shareFragment" app:popUpTo="@id/shareFragment"
app:popUpToInclusive="true" /> app:popUpToInclusive="true" >
</action>
<action <action
android:id="@+id/action_shareFragment_to_accountProblemFragment" android:id="@+id/action_shareFragment_to_accountProblemFragment"
app:destination="@id/accountProblemFragment" /> app:destination="@id/accountProblemFragment" />

View File

@ -29,7 +29,7 @@ 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
import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.Services import org.mozilla.fenix.components.Services
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
@ -41,7 +41,7 @@ class BookmarkControllerTest {
private val context: Context = mockk(relaxed = true) private val context: Context = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true)
private val snackbarPresenter: BrowserSnackbarPresenter = mockk(relaxed = true) private val snackbar: FenixSnackbar = mockk(relaxed = true)
private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit = mockk(relaxed = true) private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit = mockk(relaxed = true)
private val invokePendingDeletion: () -> Unit = mockk(relaxed = true) private val invokePendingDeletion: () -> Unit = mockk(relaxed = true)
@ -90,7 +90,7 @@ class BookmarkControllerTest {
controller = DefaultBookmarkController( controller = DefaultBookmarkController(
context = homeActivity, context = homeActivity,
navController = navController, navController = navController,
snackbarPresenter = snackbarPresenter, snackbar = snackbar,
deleteBookmarkNodes = deleteBookmarkNodes, deleteBookmarkNodes = deleteBookmarkNodes,
invokePendingDeletion = invokePendingDeletion invokePendingDeletion = invokePendingDeletion
) )
@ -165,7 +165,8 @@ class BookmarkControllerTest {
controller.handleBookmarkSelected(root) controller.handleBookmarkSelected(root)
verify { verify {
snackbarPresenter.present(errorMessage, any(), any(), any()) snackbar.setText(errorMessage)
snackbar.show()
} }
} }
@ -180,7 +181,8 @@ class BookmarkControllerTest {
verifyOrder { verifyOrder {
ClipData.newPlainText(item.url, item.url) ClipData.newPlainText(item.url, item.url)
snackbarPresenter.present(urlCopiedMessage, any(), any(), any()) snackbar.setText(urlCopiedMessage)
snackbar.show()
} }
} }

View File

@ -88,7 +88,6 @@ class DeleteAndQuitTest {
verifyOrder { verifyOrder {
snackbar.show() snackbar.show()
removeAllTabsUseCases.invoke() removeAllTabsUseCases.invoke()
snackbar.dismiss()
activity.finish() activity.finish()
} }
@ -141,8 +140,6 @@ class DeleteAndQuitTest {
historyStorage historyStorage
snackbar.dismiss()
activity.finish() activity.finish()
} }
} }

View File

@ -12,8 +12,6 @@ import assertk.assertAll
import assertk.assertThat import assertk.assertThat
import assertk.assertions.isEqualTo import assertk.assertions.isEqualTo
import assertk.assertions.isNotEqualTo import assertk.assertions.isNotEqualTo
import assertk.assertions.isSameAs
import assertk.assertions.isSuccess
import assertk.assertions.isTrue import assertk.assertions.isTrue
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import io.mockk.Runs import io.mockk.Runs
@ -35,7 +33,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.TestApplication import org.mozilla.fenix.TestApplication
import org.mozilla.fenix.components.BrowserSnackbarPresenter import org.mozilla.fenix.components.FenixSnackbar
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.metrics import org.mozilla.fenix.ext.metrics
@ -61,11 +59,11 @@ class ShareControllerTest {
) )
private val textToShare = "${shareData[0].url}\n${shareData[1].url}" private val textToShare = "${shareData[0].url}\n${shareData[1].url}"
private val sendTabUseCases = mockk<SendTabUseCases>(relaxed = true) private val sendTabUseCases = mockk<SendTabUseCases>(relaxed = true)
private val snackbarPresenter = mockk<BrowserSnackbarPresenter>(relaxed = true) private val snackbar = mockk<FenixSnackbar>(relaxed = true)
private val navController = mockk<NavController>(relaxed = true) private val navController = mockk<NavController>(relaxed = true)
private val dismiss = mockk<(ShareController.Result) -> Unit>(relaxed = true) private val dismiss = mockk<(ShareController.Result) -> Unit>(relaxed = true)
private val controller = DefaultShareController( private val controller = DefaultShareController(
context, shareData, sendTabUseCases, snackbarPresenter, navController, dismiss context, shareData, sendTabUseCases, snackbar, navController, dismiss
) )
@Before @Before
@ -121,7 +119,7 @@ class ShareControllerTest {
// needed for capturing the actual Intent used the `slot` one doesn't have this flag so we // needed for capturing the actual Intent used the `slot` one doesn't have this flag so we
// need to use an Activity Context. // need to use an Activity Context.
val activityContext: Context = mockk<Activity>() val activityContext: Context = mockk<Activity>()
val testController = DefaultShareController(activityContext, shareData, mockk(), snackbarPresenter, mockk(), dismiss) val testController = DefaultShareController(activityContext, shareData, mockk(), snackbar, mockk(), dismiss)
every { activityContext.startActivity(capture(shareIntent)) } throws SecurityException() every { activityContext.startActivity(capture(shareIntent)) } throws SecurityException()
every { activityContext.getString(R.string.share_error_snackbar) } returns "Cannot share to this app" every { activityContext.getString(R.string.share_error_snackbar) } returns "Cannot share to this app"
@ -129,7 +127,8 @@ class ShareControllerTest {
verifyOrder { verifyOrder {
activityContext.startActivity(shareIntent.captured) activityContext.startActivity(shareIntent.captured)
snackbarPresenter.present("Cannot share to this app") snackbar.setText("Cannot share to this app")
snackbar.show()
dismiss(ShareController.Result.SHARE_ERROR) dismiss(ShareController.Result.SHARE_ERROR)
} }
} }
@ -211,18 +210,12 @@ class ShareControllerTest {
fun `showSuccess should show a snackbar with a success message`() { fun `showSuccess should show a snackbar with a success message`() {
val expectedMessage = controller.getSuccessMessage() val expectedMessage = controller.getSuccessMessage()
val expectedTimeout = Snackbar.LENGTH_SHORT val expectedTimeout = Snackbar.LENGTH_SHORT
val messageSlot = slot<String>()
val timeoutSlot = slot<Int>()
controller.showSuccess() controller.showSuccess()
verify { snackbarPresenter.present(capture(messageSlot), capture(timeoutSlot)) } verify {
assertAll { snackbar.setText(expectedMessage)
assertThat(messageSlot.isCaptured).isTrue() snackbar.setLength(expectedTimeout)
assertThat(timeoutSlot.isCaptured).isTrue()
assertThat(messageSlot.captured).isEqualTo(expectedMessage)
assertThat(timeoutSlot.captured).isEqualTo(expectedTimeout)
} }
} }
@ -233,35 +226,16 @@ class ShareControllerTest {
val operation: () -> Unit = { println("Hello World") } val operation: () -> Unit = { println("Hello World") }
val expectedRetryMessage = val expectedRetryMessage =
context.getString(R.string.sync_sent_tab_error_snackbar_action) context.getString(R.string.sync_sent_tab_error_snackbar_action)
val messageSlot = slot<String>()
val timeoutSlot = slot<Int>()
val operationSlot = slot<() -> Unit>()
val retryMesageSlot = slot<String>()
val isFailureSlot = slot<Boolean>()
controller.showFailureWithRetryOption(operation) controller.showFailureWithRetryOption(operation)
verify { verify {
snackbarPresenter.present( snackbar.apply {
capture(messageSlot), setText(expectedMessage)
capture(timeoutSlot), setLength(expectedTimeout)
capture(operationSlot), setAction(expectedRetryMessage, operation)
capture(retryMesageSlot), setAppropriateBackground(true)
capture(isFailureSlot) }
)
}
assertAll {
assertThat(messageSlot.isCaptured).isTrue()
assertThat(timeoutSlot.isCaptured).isTrue()
assertThat(operationSlot.isCaptured).isTrue()
assertThat(retryMesageSlot.isCaptured).isTrue()
assertThat(isFailureSlot.isCaptured).isTrue()
assertThat(messageSlot.captured).isEqualTo(expectedMessage)
assertThat(timeoutSlot.captured).isEqualTo(expectedTimeout)
assertThat { operationSlot.captured }.isSuccess().isSameAs(operation)
assertThat(retryMesageSlot.captured).isEqualTo(expectedRetryMessage)
assertThat(isFailureSlot.captured).isEqualTo(true)
} }
} }