Use ShareData with ShareFragment (#6698)
parent
d69db50968
commit
7e8f079269
|
@ -23,6 +23,7 @@ import kotlinx.coroutines.launch
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
import mozilla.components.concept.engine.EngineView
|
import mozilla.components.concept.engine.EngineView
|
||||||
|
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
|
||||||
|
@ -32,8 +33,8 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||||
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||||
import org.mozilla.fenix.components.TabCollectionStorage
|
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
|
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
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
|
@ -144,11 +145,11 @@ class DefaultBrowserToolbarController(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolbarMenu.Item.Share -> {
|
ToolbarMenu.Item.Share -> {
|
||||||
val currentUrl = currentSession?.url
|
val directions = NavGraphDirections.actionGlobalShareFragment(
|
||||||
currentUrl?.apply {
|
data = arrayOf(ShareData(url = currentSession?.url, title = currentSession?.title)),
|
||||||
val directions = NavGraphDirections.actionGlobalShareFragment(this)
|
showPage = true
|
||||||
navController.navigate(directions)
|
)
|
||||||
}
|
navController.navigate(directions)
|
||||||
}
|
}
|
||||||
ToolbarMenu.Item.NewTab -> {
|
ToolbarMenu.Item.NewTab -> {
|
||||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
|
||||||
|
|
|
@ -37,7 +37,6 @@ 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.Dispatchers
|
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -47,6 +46,7 @@ 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
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.concept.sync.AccountObserver
|
import mozilla.components.concept.sync.AccountObserver
|
||||||
import mozilla.components.concept.sync.AuthType
|
import mozilla.components.concept.sync.AuthType
|
||||||
import mozilla.components.concept.sync.OAuthAccount
|
import mozilla.components.concept.sync.OAuthAccount
|
||||||
|
@ -95,7 +95,6 @@ import org.mozilla.fenix.mvi.getManagedEmitter
|
||||||
import org.mozilla.fenix.onboarding.FenixOnboarding
|
import org.mozilla.fenix.onboarding.FenixOnboarding
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||||
import org.mozilla.fenix.share.ShareTab
|
|
||||||
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
||||||
import org.mozilla.fenix.utils.allowUndo
|
import org.mozilla.fenix.utils.allowUndo
|
||||||
import org.mozilla.fenix.whatsnew.WhatsNew
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||||
|
@ -230,14 +229,14 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
setupHomeMenu()
|
setupHomeMenu()
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
|
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
||||||
val iconSize = resources.getDimension(R.dimen.preference_icon_drawable_size).toInt()
|
val iconSize = resources.getDimensionPixelSize(R.dimen.preference_icon_drawable_size)
|
||||||
|
|
||||||
val searchEngine = requireComponents.search.provider.getDefaultEngine(requireContext())
|
val searchEngine = requireComponents.search.provider.getDefaultEngine(requireContext())
|
||||||
val searchIcon = BitmapDrawable(resources, searchEngine.icon)
|
val searchIcon = BitmapDrawable(resources, searchEngine.icon)
|
||||||
searchIcon.setBounds(0, 0, iconSize, iconSize)
|
searchIcon.setBounds(0, 0, iconSize, iconSize)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Main) {
|
||||||
search_engine_icon?.setImageDrawable(searchIcon)
|
search_engine_icon?.setImageDrawable(searchIcon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,7 +408,7 @@ class HomeFragment : Fragment() {
|
||||||
is TabAction.Share -> {
|
is TabAction.Share -> {
|
||||||
invokePendingDeleteJobs()
|
invokePendingDeleteJobs()
|
||||||
sessionManager.findSessionById(action.sessionId)?.let { session ->
|
sessionManager.findSessionById(action.sessionId)?.let { session ->
|
||||||
share(session.url)
|
share(listOf(ShareData(url = session.url)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TabAction.PauseMedia -> {
|
is TabAction.PauseMedia -> {
|
||||||
|
@ -449,11 +448,11 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
is TabAction.ShareTabs -> {
|
is TabAction.ShareTabs -> {
|
||||||
invokePendingDeleteJobs()
|
invokePendingDeleteJobs()
|
||||||
val shareTabs = sessionManager
|
val shareData = sessionManager
|
||||||
.sessionsOfType(private = browsingModeManager.mode.isPrivate)
|
.sessionsOfType(private = browsingModeManager.mode.isPrivate)
|
||||||
.map { ShareTab(it.url, it.title) }
|
.map { ShareData(url = it.url, title = it.title) }
|
||||||
.toList()
|
.toList()
|
||||||
share(tabs = shareTabs)
|
share(shareData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,8 +582,7 @@ class HomeFragment : Fragment() {
|
||||||
components.analytics.metrics.track(Event.CollectionAllTabsRestored)
|
components.analytics.metrics.track(Event.CollectionAllTabsRestored)
|
||||||
}
|
}
|
||||||
is CollectionAction.ShareTabs -> {
|
is CollectionAction.ShareTabs -> {
|
||||||
val shareTabs = action.collection.tabs.map { ShareTab(it.url, it.title) }
|
share(action.collection.tabs.map { ShareData(url = it.url, title = it.title) })
|
||||||
share(tabs = shareTabs)
|
|
||||||
requireComponents.analytics.metrics.track(Event.CollectionShared)
|
requireComponents.analytics.metrics.track(Event.CollectionShared)
|
||||||
}
|
}
|
||||||
is CollectionAction.RemoveTab -> {
|
is CollectionAction.RemoveTab -> {
|
||||||
|
@ -853,12 +851,10 @@ class HomeFragment : Fragment() {
|
||||||
showCollectionCreationFragment(step, selectedTabId?.let { arrayOf(it) })
|
showCollectionCreationFragment(step, selectedTabId?.let { arrayOf(it) })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun share(url: String? = null, tabs: List<ShareTab>? = null) {
|
private fun share(data: List<ShareData>) {
|
||||||
val directions =
|
val directions = HomeFragmentDirections.actionHomeFragmentToShareFragment(
|
||||||
HomeFragmentDirections.actionHomeFragmentToShareFragment(
|
data = data.toTypedArray()
|
||||||
url = url,
|
)
|
||||||
tabs = tabs?.toTypedArray()
|
|
||||||
)
|
|
||||||
nav(R.id.homeFragment, directions)
|
nav(R.id.homeFragment, directions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.content.res.Resources
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.concept.storage.BookmarkNode
|
import mozilla.components.concept.storage.BookmarkNode
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.HomeActivity
|
import org.mozilla.fenix.HomeActivity
|
||||||
|
@ -21,7 +22,6 @@ 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
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.share.ShareTab
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [BookmarkFragment] controller.
|
* [BookmarkFragment] controller.
|
||||||
|
@ -84,9 +84,7 @@ class DefaultBookmarkController(
|
||||||
override fun handleBookmarkSharing(item: BookmarkNode) {
|
override fun handleBookmarkSharing(item: BookmarkNode) {
|
||||||
navigate(
|
navigate(
|
||||||
BookmarkFragmentDirections.actionBookmarkFragmentToShareFragment(
|
BookmarkFragmentDirections.actionBookmarkFragmentToShareFragment(
|
||||||
url = item.url!!,
|
data = arrayOf(ShareData(url = item.url, title = item.title))
|
||||||
title = item.title,
|
|
||||||
tabs = arrayOf(ShareTab(item.url!!, item.title!!))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import mozilla.appservices.places.BookmarkRoot
|
import mozilla.appservices.places.BookmarkRoot
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.concept.storage.BookmarkNode
|
import mozilla.components.concept.storage.BookmarkNode
|
||||||
import mozilla.components.concept.storage.BookmarkNodeType
|
import mozilla.components.concept.storage.BookmarkNodeType
|
||||||
import mozilla.components.concept.sync.AccountObserver
|
import mozilla.components.concept.sync.AccountObserver
|
||||||
|
@ -46,7 +47,6 @@ import org.mozilla.fenix.ext.minus
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.ext.toShortUrl
|
import org.mozilla.fenix.ext.toShortUrl
|
||||||
import org.mozilla.fenix.library.LibraryPageFragment
|
import org.mozilla.fenix.library.LibraryPageFragment
|
||||||
import org.mozilla.fenix.share.ShareTab
|
|
||||||
import org.mozilla.fenix.utils.allowUndo
|
import org.mozilla.fenix.utils.allowUndo
|
||||||
|
|
||||||
@Suppress("TooManyFunctions", "LargeClass")
|
@Suppress("TooManyFunctions", "LargeClass")
|
||||||
|
@ -196,9 +196,7 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), BackHandler {
|
||||||
val bookmark = bookmarkStore.state.mode.selectedItems.first()
|
val bookmark = bookmarkStore.state.mode.selectedItems.first()
|
||||||
navigate(
|
navigate(
|
||||||
BookmarkFragmentDirections.actionBookmarkFragmentToShareFragment(
|
BookmarkFragmentDirections.actionBookmarkFragmentToShareFragment(
|
||||||
url = bookmark.url,
|
data = arrayOf(ShareData(url = bookmark.url, title = bookmark.title))
|
||||||
title = bookmark.title,
|
|
||||||
tabs = arrayOf(ShareTab(bookmark.url.orEmpty(), bookmark.title.orEmpty()))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
true
|
true
|
||||||
|
|
|
@ -21,6 +21,7 @@ import kotlinx.android.synthetic.main.fragment_history.view.*
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.lib.state.ext.consumeFrom
|
import mozilla.components.lib.state.ext.consumeFrom
|
||||||
import mozilla.components.support.base.feature.BackHandler
|
import mozilla.components.support.base.feature.BackHandler
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
|
@ -35,7 +36,6 @@ import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.nav
|
import org.mozilla.fenix.ext.nav
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.library.LibraryPageFragment
|
import org.mozilla.fenix.library.LibraryPageFragment
|
||||||
import org.mozilla.fenix.share.ShareTab
|
|
||||||
|
|
||||||
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
||||||
class HistoryFragment : LibraryPageFragment<HistoryItem>(), BackHandler {
|
class HistoryFragment : LibraryPageFragment<HistoryItem>(), BackHandler {
|
||||||
|
@ -143,18 +143,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), BackHandler {
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
||||||
R.id.share_history_multi_select -> {
|
R.id.share_history_multi_select -> {
|
||||||
val selectedHistory = historyStore.state.mode.selectedItems
|
val selectedHistory = historyStore.state.mode.selectedItems
|
||||||
val shareTabs = selectedHistory.map { ShareTab(it.url, it.title) }
|
val shareTabs = selectedHistory.map { ShareData(url = it.url, title = it.title) }
|
||||||
when {
|
share(shareTabs)
|
||||||
selectedHistory.size == 1 ->
|
|
||||||
share(
|
|
||||||
url = selectedHistory.first().url,
|
|
||||||
title = selectedHistory.first().title,
|
|
||||||
tabs = shareTabs
|
|
||||||
)
|
|
||||||
selectedHistory.size > 1 -> {
|
|
||||||
share(tabs = shareTabs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.delete_history_multi_select -> {
|
R.id.delete_history_multi_select -> {
|
||||||
|
@ -243,14 +233,11 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), BackHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun share(url: String? = null, title: String? = null, tabs: List<ShareTab>? = null) {
|
private fun share(data: List<ShareData>) {
|
||||||
requireComponents.analytics.metrics.track(Event.HistoryItemShared)
|
requireComponents.analytics.metrics.track(Event.HistoryItemShared)
|
||||||
val directions =
|
val directions = HistoryFragmentDirections.actionHistoryFragmentToShareFragment(
|
||||||
HistoryFragmentDirections.actionHistoryFragmentToShareFragment(
|
data = data.toTypedArray()
|
||||||
url = url,
|
)
|
||||||
title = title,
|
|
||||||
tabs = tabs?.toTypedArray()
|
|
||||||
)
|
|
||||||
nav(R.id.historyFragment, directions)
|
nav(R.id.historyFragment, directions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.share_close.*
|
import kotlinx.android.synthetic.main.share_close.*
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.share.listadapters.ShareTabsAdapter
|
import org.mozilla.fenix.share.listadapters.ShareTabsAdapter
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class ShareCloseView(
|
||||||
shared_site_list.adapter = adapter
|
shared_site_list.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTabs(tabs: List<ShareTab>) {
|
fun setTabs(tabs: List<ShareData>) {
|
||||||
adapter.submitList(tabs)
|
adapter.submitList(tabs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.concept.sync.Device
|
import mozilla.components.concept.sync.Device
|
||||||
import mozilla.components.concept.sync.TabData
|
import mozilla.components.concept.sync.TabData
|
||||||
import mozilla.components.feature.sendtab.SendTabUseCases
|
import mozilla.components.feature.sendtab.SendTabUseCases
|
||||||
|
@ -47,7 +48,7 @@ interface ShareController {
|
||||||
* Default behavior of [ShareController]. Other implementations are possible.
|
* Default behavior of [ShareController]. Other implementations are possible.
|
||||||
*
|
*
|
||||||
* @param context [Context] used for various Android interactions.
|
* @param context [Context] used for various Android interactions.
|
||||||
* @param sharedTabs the list of [ShareTab]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 [FenixSnackbarPresenter] for displaying styled snackbars
|
* @param snackbarPresenter - instance of [FenixSnackbarPresenter] for displaying styled snackbars
|
||||||
* @param navController - [NavController] used for navigation.
|
* @param navController - [NavController] used for navigation.
|
||||||
|
@ -56,12 +57,13 @@ interface ShareController {
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
class DefaultShareController(
|
class DefaultShareController(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val sharedTabs: List<ShareTab>,
|
private val shareData: List<ShareData>,
|
||||||
private val sendTabUseCases: SendTabUseCases,
|
private val sendTabUseCases: SendTabUseCases,
|
||||||
private val snackbarPresenter: FenixSnackbarPresenter,
|
private val snackbarPresenter: FenixSnackbarPresenter,
|
||||||
private val navController: NavController,
|
private val navController: NavController,
|
||||||
private val dismiss: () -> Unit
|
private val dismiss: () -> Unit
|
||||||
) : ShareController {
|
) : ShareController {
|
||||||
|
|
||||||
override fun handleReauth() {
|
override fun handleReauth() {
|
||||||
val directions = ShareFragmentDirections.actionShareFragmentToAccountProblemFragment()
|
val directions = ShareFragmentDirections.actionShareFragmentToAccountProblemFragment()
|
||||||
navController.nav(R.id.shareFragment, directions)
|
navController.nav(R.id.shareFragment, directions)
|
||||||
|
@ -99,11 +101,11 @@ class DefaultShareController(
|
||||||
|
|
||||||
override fun handleShareToDevice(device: Device) {
|
override fun handleShareToDevice(device: Device) {
|
||||||
context.metrics.track(Event.SendTab)
|
context.metrics.track(Event.SendTab)
|
||||||
shareToDevicesWithRetry { sendTabUseCases.sendToDeviceAsync(device.id, sharedTabs.toTabData()) }
|
shareToDevicesWithRetry { sendTabUseCases.sendToDeviceAsync(device.id, shareData.toTabData()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleShareToAllDevices(devices: List<Device>) {
|
override fun handleShareToAllDevices(devices: List<Device>) {
|
||||||
shareToDevicesWithRetry { sendTabUseCases.sendToAllAsync(sharedTabs.toTabData()) }
|
shareToDevicesWithRetry { sendTabUseCases.sendToAllAsync(shareData.toTabData()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleSignIn() {
|
override fun handleSignIn() {
|
||||||
|
@ -146,19 +148,20 @@ class DefaultShareController(
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
fun getSuccessMessage(): String = with(context) {
|
fun getSuccessMessage(): String = with(context) {
|
||||||
when (sharedTabs.size) {
|
when (shareData.size) {
|
||||||
1 -> getString(R.string.sync_sent_tab_snackbar)
|
1 -> getString(R.string.sync_sent_tab_snackbar)
|
||||||
else -> getString(R.string.sync_sent_tabs_snackbar)
|
else -> getString(R.string.sync_sent_tabs_snackbar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting
|
||||||
fun getShareText() = sharedTabs.joinToString("\n") { tab -> tab.url }
|
fun getShareText() = shareData.joinToString("\n") { data ->
|
||||||
|
listOfNotNull(data.url, data.text).joinToString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
// Navigation between app fragments uses ShareTab as arguments. SendTabUseCases uses TabData.
|
// Navigation between app fragments uses ShareTab as arguments. SendTabUseCases uses TabData.
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting
|
||||||
fun ShareTab.toTabData() = TabData(title, url)
|
fun List<ShareData>.toTabData() = map { data ->
|
||||||
|
TabData(data.title.orEmpty(), data.url.orEmpty())
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
}
|
||||||
fun List<ShareTab>.toTabData() = map { it.toTabData() }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.mozilla.fenix.share
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -16,7 +15,6 @@ import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
import kotlinx.android.synthetic.main.fragment_share.view.*
|
import kotlinx.android.synthetic.main.fragment_share.view.*
|
||||||
import mozilla.components.feature.sendtab.SendTabUseCases
|
import mozilla.components.feature.sendtab.SendTabUseCases
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
@ -51,15 +49,14 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_share, container, false)
|
val view = inflater.inflate(R.layout.fragment_share, container, false)
|
||||||
val args by navArgs<ShareFragmentArgs>()
|
val args by navArgs<ShareFragmentArgs>()
|
||||||
check(!(args.url == null && args.tabs.isNullOrEmpty())) { "URL and tabs cannot both be null." }
|
val shareData = args.data.toList()
|
||||||
|
|
||||||
val tabs = args.tabs?.toList() ?: listOf(ShareTab(args.url!!, args.title.orEmpty()))
|
|
||||||
val accountManager = requireComponents.backgroundServices.accountManager
|
val accountManager = requireComponents.backgroundServices.accountManager
|
||||||
|
|
||||||
shareInteractor = ShareInteractor(
|
shareInteractor = ShareInteractor(
|
||||||
DefaultShareController(
|
DefaultShareController(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
sharedTabs = tabs,
|
shareData = shareData,
|
||||||
snackbarPresenter = FenixSnackbarPresenter(activity!!.getRootView()!!),
|
snackbarPresenter = FenixSnackbarPresenter(activity!!.getRootView()!!),
|
||||||
navController = findNavController(),
|
navController = findNavController(),
|
||||||
sendTabUseCases = SendTabUseCases(accountManager),
|
sendTabUseCases = SendTabUseCases(accountManager),
|
||||||
|
@ -71,17 +68,16 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
shareToAccountDevicesView =
|
shareToAccountDevicesView =
|
||||||
ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
|
ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
|
||||||
|
|
||||||
if (args.url != null && args.tabs == null) {
|
if (args.showPage) {
|
||||||
// If sharing one tab from the browser fragment, show it.
|
// Show the previous fragment underneath the share background scrim
|
||||||
// If URL is set and tabs is null, we assume the browser is visible, since navigation
|
// by making it translucent.
|
||||||
// does not tell us the back stack state.
|
|
||||||
view.closeSharingScrim.alpha = SHOW_PAGE_ALPHA
|
view.closeSharingScrim.alpha = SHOW_PAGE_ALPHA
|
||||||
view.shareWrapper.setOnClickListener { shareInteractor.onShareClosed() }
|
view.shareWrapper.setOnClickListener { shareInteractor.onShareClosed() }
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, show a list of tabs to share.
|
// Otherwise, show a list of tabs to share.
|
||||||
view.closeSharingScrim.alpha = 1.0f
|
view.closeSharingScrim.alpha = 1.0f
|
||||||
shareCloseView = ShareCloseView(view.closeSharingContent, shareInteractor)
|
shareCloseView = ShareCloseView(view.closeSharingContent, shareInteractor)
|
||||||
shareCloseView.setTabs(tabs)
|
shareCloseView.setTabs(shareData)
|
||||||
}
|
}
|
||||||
shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor)
|
shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor)
|
||||||
|
|
||||||
|
@ -102,6 +98,3 @@ class ShareFragment : AppCompatDialogFragment() {
|
||||||
const val SHOW_PAGE_ALPHA = 0.6f
|
const val SHOW_PAGE_ALPHA = 0.6f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class ShareTab(val url: String, val title: String) : Parcelable
|
|
||||||
|
|
|
@ -11,16 +11,16 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.share_tab_item.view.*
|
import kotlinx.android.synthetic.main.share_tab_item.view.*
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.loadIntoView
|
import org.mozilla.fenix.ext.loadIntoView
|
||||||
import org.mozilla.fenix.share.ShareTab
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter for a list of tabs to be shared.
|
* Adapter for a list of tabs to be shared.
|
||||||
*/
|
*/
|
||||||
class ShareTabsAdapter :
|
class ShareTabsAdapter :
|
||||||
ListAdapter<ShareTab, ShareTabsAdapter.ShareTabViewHolder>(ShareTabDiffCallback) {
|
ListAdapter<ShareData, ShareTabsAdapter.ShareTabViewHolder>(ShareTabDiffCallback) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ShareTabViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ShareTabViewHolder(
|
||||||
LayoutInflater.from(parent.context)
|
LayoutInflater.from(parent.context)
|
||||||
|
@ -32,18 +32,18 @@ class ShareTabsAdapter :
|
||||||
|
|
||||||
class ShareTabViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
class ShareTabViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
fun bind(item: ShareTab) = with(itemView) {
|
fun bind(item: ShareData) = with(itemView) {
|
||||||
context.components.core.icons.loadIntoView(itemView.share_tab_favicon, item.url)
|
context.components.core.icons.loadIntoView(itemView.share_tab_favicon, item.url.orEmpty())
|
||||||
itemView.share_tab_title.text = item.title
|
itemView.share_tab_title.text = item.title
|
||||||
itemView.share_tab_url.text = item.url
|
itemView.share_tab_url.text = item.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object ShareTabDiffCallback : DiffUtil.ItemCallback<ShareTab>() {
|
private object ShareTabDiffCallback : DiffUtil.ItemCallback<ShareData>() {
|
||||||
override fun areItemsTheSame(oldItem: ShareTab, newItem: ShareTab) =
|
override fun areItemsTheSame(oldItem: ShareData, newItem: ShareData) =
|
||||||
oldItem.url == newItem.url
|
oldItem.url == newItem.url
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: ShareTab, newItem: ShareTab) =
|
override fun areContentsTheSame(oldItem: ShareData, newItem: ShareData) =
|
||||||
oldItem == newItem
|
oldItem == newItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -542,20 +542,12 @@
|
||||||
android:name="org.mozilla.fenix.share.ShareFragment"
|
android:name="org.mozilla.fenix.share.ShareFragment"
|
||||||
tools:layout="@layout/fragment_share">
|
tools:layout="@layout/fragment_share">
|
||||||
<argument
|
<argument
|
||||||
android:name="url"
|
android:name="data"
|
||||||
android:defaultValue="@null"
|
app:argType="mozilla.components.concept.engine.prompt.ShareData[]" />
|
||||||
app:argType="string"
|
|
||||||
app:nullable="true" />
|
|
||||||
<argument
|
<argument
|
||||||
android:name="title"
|
android:name="showPage"
|
||||||
android:defaultValue="@null"
|
app:argType="boolean"
|
||||||
app:argType="string"
|
android:defaultValue="false" />
|
||||||
app:nullable="true" />
|
|
||||||
<argument
|
|
||||||
android:name="tabs"
|
|
||||||
android:defaultValue="@null"
|
|
||||||
app:argType="org.mozilla.fenix.share.ShareTab[]"
|
|
||||||
app:nullable="true" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_shareFragment_to_turnOnSyncFragment"
|
android:id="@+id/action_shareFragment_to_turnOnSyncFragment"
|
||||||
app:destination="@+id/turnOnSyncFragment"
|
app:destination="@+id/turnOnSyncFragment"
|
||||||
|
|
|
@ -34,7 +34,6 @@ import org.junit.After
|
||||||
import org.junit.Before
|
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.NavGraphDirections
|
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.browser.BrowserFragment
|
import org.mozilla.fenix.browser.BrowserFragment
|
||||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||||
|
@ -306,12 +305,11 @@ class DefaultBrowserToolbarControllerTest {
|
||||||
val item = ToolbarMenu.Item.Share
|
val item = ToolbarMenu.Item.Share
|
||||||
|
|
||||||
every { currentSession.url } returns "https://mozilla.org"
|
every { currentSession.url } returns "https://mozilla.org"
|
||||||
val directions = NavGraphDirections.actionGlobalShareFragment(currentSession.url)
|
|
||||||
|
|
||||||
controller.handleToolbarItemInteraction(item)
|
controller.handleToolbarItemInteraction(item)
|
||||||
|
|
||||||
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SHARE)) }
|
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SHARE)) }
|
||||||
verify { navController.navigate(directions) }
|
verify { navController.navigate(any<NavDirections>()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.content.Intent
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import assertk.assertAll
|
import assertk.assertAll
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.isDataClassEqualTo
|
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
import assertk.assertions.isNotEqualTo
|
import assertk.assertions.isNotEqualTo
|
||||||
import assertk.assertions.isSameAs
|
import assertk.assertions.isSameAs
|
||||||
|
@ -25,6 +24,7 @@ import io.mockk.slot
|
||||||
import io.mockk.spyk
|
import io.mockk.spyk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.mockk.verifyOrder
|
import io.mockk.verifyOrder
|
||||||
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.concept.sync.Device
|
import mozilla.components.concept.sync.Device
|
||||||
import mozilla.components.concept.sync.DeviceType
|
import mozilla.components.concept.sync.DeviceType
|
||||||
import mozilla.components.concept.sync.TabData
|
import mozilla.components.concept.sync.TabData
|
||||||
|
@ -50,22 +50,22 @@ class ShareControllerTest {
|
||||||
// Need a valid context to retrieve Strings for example, but we also need it to return our "metrics"
|
// Need a valid context to retrieve Strings for example, but we also need it to return our "metrics"
|
||||||
private val context: Context = spyk(testContext)
|
private val context: Context = spyk(testContext)
|
||||||
private val metrics: MetricController = mockk(relaxed = true)
|
private val metrics: MetricController = mockk(relaxed = true)
|
||||||
private val shareTabs = listOf(
|
private val shareData = listOf(
|
||||||
ShareTab("url0", "title0"),
|
ShareData(url = "url0", title = "title0"),
|
||||||
ShareTab("url1", "title1")
|
ShareData(url = "url1", title = "title1")
|
||||||
)
|
)
|
||||||
// Navigation between app fragments uses ShareTab as arguments. SendTabUseCases uses TabData.
|
// Navigation between app fragments uses ShareTab as arguments. SendTabUseCases uses TabData.
|
||||||
private val tabsData = listOf(
|
private val tabsData = listOf(
|
||||||
TabData("title0", "url0"),
|
TabData("title0", "url0"),
|
||||||
TabData("title1", "url1")
|
TabData("title1", "url1")
|
||||||
)
|
)
|
||||||
private val textToShare = "${shareTabs[0].url}\n${shareTabs[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<FenixSnackbarPresenter>(relaxed = true)
|
private val snackbarPresenter = mockk<FenixSnackbarPresenter>(relaxed = true)
|
||||||
private val navController = mockk<NavController>(relaxed = true)
|
private val navController = mockk<NavController>(relaxed = true)
|
||||||
private val dismiss = mockk<() -> Unit>(relaxed = true)
|
private val dismiss = mockk<() -> Unit>(relaxed = true)
|
||||||
private val controller = DefaultShareController(
|
private val controller = DefaultShareController(
|
||||||
context, shareTabs, sendTabUseCases, snackbarPresenter, navController, dismiss
|
context, shareData, sendTabUseCases, snackbarPresenter, navController, dismiss
|
||||||
)
|
)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -90,7 +90,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, shareTabs, mockk(), mockk(), mockk(), dismiss)
|
val testController = DefaultShareController(activityContext, shareData, mockk(), mockk(), mockk(), dismiss)
|
||||||
every { activityContext.startActivity(capture(shareIntent)) } just Runs
|
every { activityContext.startActivity(capture(shareIntent)) } just Runs
|
||||||
|
|
||||||
testController.handleShareToApp(appShareOption)
|
testController.handleShareToApp(appShareOption)
|
||||||
|
@ -245,7 +245,7 @@ class ShareControllerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `getSuccessMessage should return different strings depending on the number of shared tabs`() {
|
fun `getSuccessMessage should return different strings depending on the number of shared tabs`() {
|
||||||
val controllerWithOneSharedTab = DefaultShareController(
|
val controllerWithOneSharedTab = DefaultShareController(
|
||||||
context, listOf(ShareTab("url0", "title0")), mockk(), mockk(), mockk(), mockk()
|
context, listOf(ShareData(url = "url0", title = "title0")), mockk(), mockk(), mockk(), mockk()
|
||||||
)
|
)
|
||||||
val controllerWithMoreSharedTabs = controller
|
val controllerWithMoreSharedTabs = controller
|
||||||
val expectedTabSharedMessage = context.getString(R.string.sync_sent_tab_snackbar)
|
val expectedTabSharedMessage = context.getString(R.string.sync_sent_tab_snackbar)
|
||||||
|
@ -266,23 +266,12 @@ class ShareControllerTest {
|
||||||
assertThat(controller.getShareText()).isEqualTo(textToShare)
|
assertThat(controller.getShareText()).isEqualTo(textToShare)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `ShareTab#toTabData maps a ShareTab to a TabData`() {
|
|
||||||
var tabData: TabData
|
|
||||||
|
|
||||||
with(controller) {
|
|
||||||
tabData = shareTabs[0].toTabData()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(tabData).isDataClassEqualTo(tabsData[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ShareTab#toTabData maps a list of ShareTab to a TabData list`() {
|
fun `ShareTab#toTabData maps a list of ShareTab to a TabData list`() {
|
||||||
var tabData: List<TabData>
|
var tabData: List<TabData>
|
||||||
|
|
||||||
with(controller) {
|
with(controller) {
|
||||||
tabData = shareTabs.toTabData()
|
tabData = shareData.toTabData()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(tabData).isEqualTo(tabsData)
|
assertThat(tabData).isEqualTo(tabsData)
|
||||||
|
|
Loading…
Reference in New Issue