mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-09 19:10:42 +00:00
parent
54cb8f0194
commit
248237290e
@ -22,6 +22,7 @@ import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.android.synthetic.main.tab_header.view.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
@ -71,6 +72,7 @@ import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
|
||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.FindInPageIntegration
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
@ -96,6 +98,7 @@ import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.home.SharedViewModel
|
||||
import org.mozilla.fenix.tabtray.TabTrayDialogFragment
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
import org.mozilla.fenix.utils.allowUndo
|
||||
import org.mozilla.fenix.wifi.SitePermissionsWifiIntegration
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@ -117,6 +120,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||
protected val browserToolbarView: BrowserToolbarView
|
||||
get() = _browserToolbarView!!
|
||||
|
||||
private val sessionManager: SessionManager
|
||||
get() = requireComponents.core.sessionManager
|
||||
|
||||
protected val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
|
||||
|
||||
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
|
||||
@ -225,6 +231,75 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||
tabTrayDialog.dismiss()
|
||||
findNavController().navigate(BrowserFragmentDirections.actionGlobalHome())
|
||||
}
|
||||
|
||||
override fun onShareTabsClicked(private: Boolean) {
|
||||
share(getListOfSessions(private))
|
||||
}
|
||||
|
||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||
val tabs = getListOfSessions(private)
|
||||
|
||||
val selectedIndex = sessionManager
|
||||
.selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: 0
|
||||
|
||||
val snapshot = tabs
|
||||
.map(sessionManager::createSessionSnapshot)
|
||||
.map {
|
||||
it.copy(engineSession = null, engineSessionState = it.engineSession?.saveState())
|
||||
}
|
||||
.let { SessionManager.Snapshot(it, selectedIndex) }
|
||||
|
||||
tabs.forEach {
|
||||
sessionManager.remove(it)
|
||||
}
|
||||
|
||||
val isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
val snackbarMessage = if (isPrivate) {
|
||||
getString(R.string.snackbar_private_tabs_closed)
|
||||
} else {
|
||||
getString(R.string.snackbar_tabs_closed)
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.allowUndo(
|
||||
requireView(),
|
||||
snackbarMessage,
|
||||
getString(R.string.snackbar_deleted_undo),
|
||||
{
|
||||
sessionManager.restore(snapshot)
|
||||
},
|
||||
operation = { },
|
||||
anchorView = view.tabs_header
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSaveToCollectionClicked() {
|
||||
val tabs = getListOfSessions(false)
|
||||
val tabIds = tabs.map { it.id }.toList().toTypedArray()
|
||||
val tabCollectionStorage = (activity as HomeActivity).components.core.tabCollectionStorage
|
||||
val navController = findNavController()
|
||||
|
||||
val step = when {
|
||||
// Show the SelectTabs fragment if there are multiple opened tabs to select which tabs
|
||||
// you want to save to a collection.
|
||||
tabs.size > 1 -> SaveCollectionStep.SelectTabs
|
||||
// If there is an existing tab collection, show the SelectCollection fragment to save
|
||||
// the selected tab to a collection of your choice.
|
||||
tabCollectionStorage.cachedTabCollections.isNotEmpty() ->
|
||||
SaveCollectionStep.SelectCollection
|
||||
// Show the NameCollection fragment to create a new collection for the selected tab.
|
||||
else -> SaveCollectionStep.NameCollection
|
||||
}
|
||||
|
||||
if (navController.currentDestination?.id == R.id.collectionCreationFragment) return
|
||||
|
||||
val directions = BrowserFragmentDirections.actionBrowserFragmentToCreateCollectionFragment(
|
||||
tabIds = tabIds,
|
||||
previousFragmentId = R.id.tabTrayFragment,
|
||||
saveCollectionStep = step,
|
||||
selectedTabIds = tabIds
|
||||
)
|
||||
navController.nav(R.id.browserFragment, directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -965,6 +1040,23 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||
}
|
||||
}
|
||||
|
||||
private fun share(tabs: List<Session>) {
|
||||
val data = tabs.map {
|
||||
ShareData(url = it.url, title = it.title)
|
||||
}
|
||||
val directions = BrowserFragmentDirections.actionGlobalShareFragment(
|
||||
data = data.toTypedArray()
|
||||
)
|
||||
nav(R.id.browserFragment, directions)
|
||||
}
|
||||
|
||||
private fun getListOfSessions(
|
||||
private: Boolean = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
): List<Session> {
|
||||
return requireComponents.core.sessionManager.sessionsOfType(private = private)
|
||||
.toList()
|
||||
}
|
||||
|
||||
/*
|
||||
* Dereference these views when the fragment view is destroyed to prevent memory leaks
|
||||
*/
|
||||
|
@ -45,6 +45,7 @@ import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
import kotlinx.android.synthetic.main.fragment_home.view.*
|
||||
import kotlinx.android.synthetic.main.tab_header.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
@ -59,6 +60,7 @@ import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.state.MediaState.State.PLAYING
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.prompt.ShareData
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.AuthType
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
@ -75,6 +77,7 @@ import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
||||
import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.collections.SaveCollectionStep
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.PrivateShortcutCreateManager
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
@ -371,6 +374,72 @@ class HomeFragment : Fragment() {
|
||||
(activity as HomeActivity).browsingModeManager.mode = BrowsingMode.fromBoolean(private)
|
||||
tabTrayDialog.dismiss()
|
||||
}
|
||||
|
||||
override fun onShareTabsClicked(private: Boolean) {
|
||||
share(getListOfSessions(private))
|
||||
}
|
||||
|
||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||
val tabs = getListOfSessions(private)
|
||||
|
||||
val selectedIndex = sessionManager
|
||||
.selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: 0
|
||||
|
||||
val snapshot = tabs
|
||||
.map(sessionManager::createSessionSnapshot)
|
||||
.map { it.copy(engineSession = null, engineSessionState = it.engineSession?.saveState()) }
|
||||
.let { SessionManager.Snapshot(it, selectedIndex) }
|
||||
|
||||
tabs.forEach {
|
||||
sessionManager.remove(it)
|
||||
}
|
||||
|
||||
val isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
val snackbarMessage = if (isPrivate) {
|
||||
getString(R.string.snackbar_private_tabs_closed)
|
||||
} else {
|
||||
getString(R.string.snackbar_tabs_closed)
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.allowUndo(
|
||||
requireView(),
|
||||
snackbarMessage,
|
||||
getString(R.string.snackbar_deleted_undo),
|
||||
{
|
||||
sessionManager.restore(snapshot)
|
||||
},
|
||||
operation = { },
|
||||
anchorView = view.tabs_header
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSaveToCollectionClicked() {
|
||||
val tabs = getListOfSessions(false)
|
||||
val tabIds = tabs.map { it.id }.toList().toTypedArray()
|
||||
val tabCollectionStorage = (activity as HomeActivity).components.core.tabCollectionStorage
|
||||
val navController = findNavController()
|
||||
|
||||
val step = when {
|
||||
// Show the SelectTabs fragment if there are multiple opened tabs to select which tabs
|
||||
// you want to save to a collection.
|
||||
tabs.size > 1 -> SaveCollectionStep.SelectTabs
|
||||
// If there is an existing tab collection, show the SelectCollection fragment to save
|
||||
// the selected tab to a collection of your choice.
|
||||
tabCollectionStorage.cachedTabCollections.isNotEmpty() -> SaveCollectionStep.SelectCollection
|
||||
// Show the NameCollection fragment to create a new collection for the selected tab.
|
||||
else -> SaveCollectionStep.NameCollection
|
||||
}
|
||||
|
||||
if (navController.currentDestination?.id == R.id.collectionCreationFragment) return
|
||||
|
||||
val directions = HomeFragmentDirections.actionHomeFragmentToCreateCollectionFragment(
|
||||
tabIds = tabIds,
|
||||
previousFragmentId = R.id.tabTrayFragment,
|
||||
saveCollectionStep = step,
|
||||
selectedTabIds = tabIds
|
||||
)
|
||||
navController.nav(R.id.homeFragment, directions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -846,8 +915,8 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getListOfSessions(): List<Session> {
|
||||
return sessionManager.sessionsOfType(private = browsingModeManager.mode.isPrivate)
|
||||
private fun getListOfSessions(private: Boolean = browsingModeManager.mode.isPrivate): List<Session> {
|
||||
return sessionManager.sessionsOfType(private = private)
|
||||
.filter { session: Session -> session.id != pendingSessionDeletion?.sessionId }
|
||||
.toList()
|
||||
}
|
||||
@ -1022,6 +1091,16 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun share(tabs: List<Session>) {
|
||||
val data = tabs.map {
|
||||
ShareData(url = it.url, title = it.title)
|
||||
}
|
||||
val directions = HomeFragmentDirections.actionGlobalShareFragment(
|
||||
data = data.toTypedArray()
|
||||
)
|
||||
nav(R.id.homeFragment, directions)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANIMATION_DELAY = 100L
|
||||
|
||||
|
@ -15,6 +15,7 @@ import kotlinx.android.synthetic.main.component_tabstray.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.*
|
||||
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.*
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
@ -25,6 +26,9 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
||||
interface Interactor {
|
||||
fun onTabSelected(tab: Tab)
|
||||
fun onNewTabTapped(private: Boolean)
|
||||
fun onShareTabsClicked(private: Boolean)
|
||||
fun onSaveToCollectionClicked()
|
||||
fun onCloseAllTabsClicked(private: Boolean)
|
||||
}
|
||||
|
||||
private lateinit var tabTrayView: TabTrayView
|
||||
@ -49,7 +53,9 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
||||
(activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
)
|
||||
|
||||
tabLayout.setOnClickListener { dismissAllowingStateLoss() }
|
||||
tabLayout.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
view.tabLayout.setOnApplyWindowInsetsListener { v, insets ->
|
||||
v.updatePadding(
|
||||
@ -64,6 +70,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
||||
|
||||
insets
|
||||
}
|
||||
|
||||
consumeFrom(requireComponents.core.store) { tabTrayView.updateState(it) }
|
||||
}
|
||||
|
||||
override fun onTabClosed(tab: Tab) {
|
||||
@ -108,6 +116,18 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), TabTrayInteractor {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
override fun onShareTabsClicked(private: Boolean) {
|
||||
interactor?.onShareTabsClicked(private)
|
||||
}
|
||||
|
||||
override fun onSaveToCollectionClicked() {
|
||||
interactor?.onSaveToCollectionClicked()
|
||||
}
|
||||
|
||||
override fun onCloseAllTabsClicked(private: Boolean) {
|
||||
interactor?.onCloseAllTabsClicked(private)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ELEVATION = 80f
|
||||
}
|
||||
|
@ -4,15 +4,22 @@
|
||||
|
||||
package org.mozilla.fenix.tabtray
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_tabstray.*
|
||||
import kotlinx.android.synthetic.main.component_tabstray.view.*
|
||||
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.selector.privateTabs
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.tabstray.BrowserTabsTray
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
@ -26,6 +33,9 @@ interface TabTrayInteractor {
|
||||
fun onTabSelected(tab: Tab)
|
||||
fun onNewTabTapped(private: Boolean)
|
||||
fun onTabTrayDismissed()
|
||||
fun onShareTabsClicked(private: Boolean)
|
||||
fun onSaveToCollectionClicked()
|
||||
fun onCloseAllTabsClicked(private: Boolean)
|
||||
}
|
||||
/**
|
||||
* View that contains and configures the BrowserAwesomeBar
|
||||
@ -41,8 +51,11 @@ class TabTrayView(
|
||||
val view = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_tabstray, container, true)
|
||||
|
||||
val isPrivateModeSelected: Boolean get() = view.tab_layout.selectedTabPosition == PRIVATE_TAB_ID
|
||||
|
||||
private val behavior = BottomSheetBehavior.from(view.tab_wrapper)
|
||||
private var tabsFeature: TabsFeature
|
||||
private var tabTrayItemMenu: TabTrayItemMenu
|
||||
|
||||
override val containerView: View?
|
||||
get() = container
|
||||
@ -89,8 +102,26 @@ class TabTrayView(
|
||||
TabsTouchHelper(tray.tabsAdapter).attachToRecyclerView(tray)
|
||||
}
|
||||
|
||||
tabTrayItemMenu = TabTrayItemMenu(view.context, { view.tab_layout.selectedTabPosition == 0 }) {
|
||||
when (it) {
|
||||
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
|
||||
isPrivateModeSelected
|
||||
)
|
||||
is TabTrayItemMenu.Item.SaveToCollection -> interactor.onSaveToCollectionClicked()
|
||||
is TabTrayItemMenu.Item.CloseAllTabs -> interactor.onCloseAllTabsClicked(
|
||||
isPrivateModeSelected
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
view.tab_tray_overflow.setOnClickListener {
|
||||
tabTrayItemMenu.menuBuilder
|
||||
.build(view.context)
|
||||
.show(anchor = it)
|
||||
}
|
||||
|
||||
fabView.new_tab_button.setOnClickListener {
|
||||
interactor.onNewTabTapped(view.tab_layout.selectedTabPosition == 1)
|
||||
interactor.onNewTabTapped(isPrivateModeSelected)
|
||||
}
|
||||
|
||||
tabsTray.register(this)
|
||||
@ -109,6 +140,17 @@ class TabTrayView(
|
||||
}
|
||||
|
||||
tabsFeature.filterTabs(filter)
|
||||
|
||||
updateState(view.context.components.core.store.state)
|
||||
}
|
||||
|
||||
fun updateState(state: BrowserState) {
|
||||
val shouldHide = if (isPrivateModeSelected) {
|
||||
state.privateTabs.isEmpty()
|
||||
} else {
|
||||
state.normalTabs.isEmpty()
|
||||
}
|
||||
view?.tab_tray_overflow?.isVisible = !shouldHide
|
||||
}
|
||||
|
||||
override fun onTabClosed(tab: Tab) {
|
||||
@ -124,3 +166,43 @@ class TabTrayView(
|
||||
private const val ELEVATION = 90f
|
||||
}
|
||||
}
|
||||
|
||||
class TabTrayItemMenu(
|
||||
private val context: Context,
|
||||
private val shouldShowSaveToCollection: () -> Boolean,
|
||||
private val onItemTapped: (Item) -> Unit = {}
|
||||
) {
|
||||
|
||||
sealed class Item {
|
||||
object ShareAllTabs : Item()
|
||||
object SaveToCollection : Item()
|
||||
object CloseAllTabs : Item()
|
||||
}
|
||||
|
||||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_save),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.SaveToCollection)
|
||||
}.apply { visible = shouldShowSaveToCollection },
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_share),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.ShareAllTabs)
|
||||
},
|
||||
|
||||
SimpleBrowserMenuItem(
|
||||
context.getString(R.string.tab_tray_menu_item_close),
|
||||
textColorResource = R.color.primary_text_normal_theme
|
||||
) {
|
||||
onItemTapped.invoke(Item.CloseAllTabs)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -58,11 +58,11 @@
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/open_tabs_menu"
|
||||
app:srcCompat="@drawable/ic_menu"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:visibility="gone"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tab_layout"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout"/>
|
||||
app:layout_constraintBottom_toBottomOf="@id/tab_layout" />
|
||||
<mozilla.components.concept.tabstray.TabsTray
|
||||
android:id="@+id/tabsTray"
|
||||
android:layout_width="0dp"
|
||||
|
@ -164,6 +164,9 @@
|
||||
<action
|
||||
android:id="@+id/action_browserFragment_to_tabsTrayFragment"
|
||||
app:destination="@+id/tabTrayFragment" />
|
||||
<action
|
||||
android:id="@+id/action_browserFragment_to_createCollectionFragment"
|
||||
app:destination="@id/collectionCreationFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
Loading…
Reference in New Issue
Block a user