Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
The Tor Project
Applications
fenix
Commits
24823729
Unverified
Commit
24823729
authored
May 27, 2020
by
David Walsh
Committed by
GitHub
May 27, 2020
Browse files
For #10865 - Implement 3 dot menu for tab tray (#10869)
parent
54cb8f01
Changes
6
Hide whitespace changes
Inline
Side-by-side
app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
View file @
24823729
...
...
@@ -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
*/
...
...
app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
View file @
24823729
...
...
@@ -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
.
isP
rivate
)
private
fun
getListOfSessions
(
private
:
Boolean
=
browsingModeManager
.
mode
.
isPrivate
):
List
<
Session
>
{
return
sessionManager
.
sessionsOfType
(
private
=
p
rivate
)
.
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
...
...
app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt
View file @
24823729
...
...
@@ -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
}
...
...
app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt
View file @
24823729
...
...
@@ -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
)
}
)
}
}
app/src/main/res/layout/component_tabstray.xml
View file @
24823729
...
...
@@ -58,11 +58,11 @@
android:background=
"?android:attr/selectableItemBackgroundBorderless"
android:contentDescription=
"@string/open_tabs_menu"
app:srcCompat=
"@drawable/ic_menu"
android:layout_marginEnd=
"
8
dp"
android:visibility=
"
gon
e"
android:layout_marginEnd=
"
0
dp"
android:visibility=
"
visibl
e"
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"
...
...
app/src/main/res/navigation/nav_graph.xml
View file @
24823729
...
...
@@ -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
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment