Merge pull request #69 from abhijitvalluri/tabstray_fixes

Fix some issues with tabs tray + enable proper Fennec style full screen layout + add onboarding setup for tabs tray layout
pull/76/head
interfect 4 years ago committed by GitHub
commit 625289da6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -35,7 +35,7 @@ import kotlin.math.abs
* limitations under the License.
*/ /**
* An interaction behavior plugin for a child view of [CoordinatorLayout] to make it work as
* a bottom sheet.
* a top sheet.
*/
class TopSheetBehavior<V : View?>
/**

@ -23,17 +23,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageVie
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSitePagerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingAutomaticSignInViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingManualSignInViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingPrivacyNoticeViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingPrivateBrowsingViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingSectionHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingWhatsNewViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.*
import org.mozilla.fenix.home.tips.ButtonTipViewHolder
import mozilla.components.feature.tab.collections.Tab as ComponentTab
@ -106,6 +96,7 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
object OnboardingFinish : AdapterItem(OnboardingFinishViewHolder.LAYOUT_ID)
object OnboardingToolbarPositionPicker :
AdapterItem(OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID)
object OnboardingTabsTrayLayoutPicker : AdapterItem(OnboardingTabsTrayLayoutViewHolder.LAYOUT_ID)
object OnboardingWhatsNew : AdapterItem(OnboardingWhatsNewViewHolder.LAYOUT_ID)
@ -189,6 +180,7 @@ class SessionControlAdapter(
OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder(
view
)
OnboardingTabsTrayLayoutViewHolder.LAYOUT_ID -> OnboardingTabsTrayLayoutViewHolder(view)
else -> throw IllegalStateException()
}
}

@ -99,6 +99,7 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
AdapterItem.OnboardingThemePicker,
AdapterItem.OnboardingPrivateBrowsing,
AdapterItem.OnboardingToolbarPositionPicker,
AdapterItem.OnboardingTabsTrayLayoutPicker,
AdapterItem.OnboardingPrivacyNotice,
AdapterItem.OnboardingFinish
)

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.onboarding_tabs_tray_layout.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.Event.OnboardingTrackingProtection.Setting
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
class OnboardingTabsTrayLayoutViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private var fennecStyleTabsScreen: OnboardingRadioButton = view.tabs_tray_fennec_style
private var fenixStyleTabsTray: OnboardingRadioButton = view.tabs_tray_fenix_style
init {
addToRadioGroup(fennecStyleTabsScreen, fenixStyleTabsTray)
fennecStyleTabsScreen.isChecked =
itemView.context.settings().shouldUseFennecStyleTabsScreen
fenixStyleTabsTray.isChecked =
!itemView.context.settings().shouldUseFennecStyleTabsScreen
fennecStyleTabsScreen.onClickListener {
setFennecStyleTabsScreen(true)
}
fenixStyleTabsTray.onClickListener {
setFennecStyleTabsScreen(false)
}
}
private fun setFennecStyleTabsScreen(enabled: Boolean) {
itemView.context.settings().apply {
enableCompactTabs = enabled
useFullScreenTabScreen = enabled
reverseTabOrderInTabsTray = !enabled
useNewTabFloatingActionButton = !enabled
placeNewTabFloatingActionButtonAtTop = false
}
}
companion object {
const val LAYOUT_ID = R.layout.onboarding_tabs_tray_layout
}
}

@ -9,10 +9,8 @@ import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import androidx.core.content.edit
import androidx.preference.*
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
@ -145,32 +143,74 @@ class CustomizationFragment : PreferenceFragmentCompat() {
}
private fun setupTabsTrayCategory() {
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_compact_tab).apply {
isChecked = context.settings().enableCompactTabs
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_top_tray).apply {
isChecked = context.settings().useTopTabsTray
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_top_tray).apply {
isChecked = context.settings().useTopTabsTray
requirePreference<SwitchPreference>(R.string.pref_key_use_fullscreen_tabs_screen).apply {
isChecked = context.settings().useFullScreenTabScreen
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_reverse_tab_order).apply {
isChecked = context.settings().reverseTabOrderInTabsTray
val reverseOrderPref = requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_reverse_tab_order).apply {
if (context.settings().enableCompactTabs) {
isChecked = false
isEnabled = false
} else {
isChecked = context.settings().reverseTabOrderInTabsTray
isEnabled = true
}
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_compact_tab).apply {
isChecked = context.settings().enableCompactTabs
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue ->
val newValueBoolean = newValue as Boolean
preference.context.settings().preferences.edit {
putBoolean(preference.key, newValueBoolean)
if (newValueBoolean) {
reverseOrderPref.isChecked = false
putBoolean(getString(R.string.pref_key_tabs_tray_reverse_tab_order), false)
}
reverseOrderPref.isEnabled = !newValueBoolean
}
true
}
}
}
private fun setupFabCategory() {
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_use_fab).apply {
isChecked = context.settings().useNewTabFloatingActionButton
val fabPositionTop = requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_fab_top_position).apply {
if (context.settings().useNewTabFloatingActionButton) {
isChecked = context.settings().placeNewTabFloatingActionButtonAtTop
isEnabled = true
} else {
isChecked = false
isEnabled = false
}
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_fab_top_position).apply {
isChecked = context.settings().placeNewTabFloatingActionButtonAtTop
onPreferenceChangeListener = SharedPreferenceUpdater()
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_use_fab).apply {
isChecked = context.settings().useNewTabFloatingActionButton
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue ->
val newValueBoolean = newValue as Boolean
preference.context.settings().preferences.edit {
putBoolean(preference.key, newValueBoolean)
if (!newValueBoolean) {
fabPositionTop.isChecked = false
putBoolean(getString(R.string.pref_key_tabs_tray_fab_top_position), false)
}
fabPositionTop.isEnabled = newValueBoolean
}
true
}
}
}
private fun setupHomeCategory() {

@ -25,7 +25,18 @@ import androidx.recyclerview.widget.LinearLayoutManager
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_bottom.view.*
import kotlinx.android.synthetic.main.component_tabs_screen_top.view.*
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.collect_multi_select
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.exit_multi_select
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.handle
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.multiselect_title
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tab_layout
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tab_tray_empty_view
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tab_tray_new_tab
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tab_tray_overflow
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tab_wrapper
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.tabsTray
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.topBar
import kotlinx.android.synthetic.main.component_tabstray_fab_bottom.view.*
import kotlinx.android.synthetic.main.tabs_tray_tab_counter.*
import kotlinx.coroutines.Dispatchers.Main
@ -81,19 +92,33 @@ class TabTrayView(
private val enableCompactTabs = container.context.settings().enableCompactTabs
private val reverseTabOrderInTabsTray = container.context.settings().reverseTabOrderInTabsTray
private val isTabsTrayFullScreenMode = container.context.settings().useFullScreenTabScreen
private val hasAccessibilityEnabled = container.context.settings().accessibilityServicesEnabled
private val useTopTabsTray = container.context.settings().useTopTabsTray
val view: View = when (useTopTabsTray) {
true -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray_top, container, true)
false -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray_bottom, container, true)
val view: View = if (isTabsTrayFullScreenMode) {
when (useTopTabsTray) {
true -> LayoutInflater.from(container.context)
.inflate(R.layout.component_tabs_screen_bottom, container, true)
false -> LayoutInflater.from(container.context)
.inflate(R.layout.component_tabs_screen_top, container, true)
}
} else {
when (useTopTabsTray) {
true -> LayoutInflater.from(container.context)
.inflate(R.layout.component_tabstray_top, container, true)
false -> LayoutInflater.from(container.context)
.inflate(R.layout.component_tabstray_bottom, container, true)
}
}
private val isPrivateModeSelected: Boolean get() = view.tab_layout.selectedTabPosition == PRIVATE_TAB_ID
private val behavior = when (useTopTabsTray) {
true -> TopSheetBehavior.from(view.tab_wrapper)
false -> BottomSheetBehavior.from(view.tab_wrapper)
private val behavior = if (isTabsTrayFullScreenMode) null else {
when (useTopTabsTray) {
true -> TopSheetBehavior.from(view.tab_wrapper)
false -> BottomSheetBehavior.from(view.tab_wrapper)
}
}
private val concatAdapter = ConcatAdapter(tabsAdapter)
@ -118,46 +143,54 @@ class TabTrayView(
toggleFabText(isPrivate)
if (useTopTabsTray) {
(behavior as TopSheetBehavior).setTopSheetCallback(object :
TopSheetBehavior.TopSheetCallback() {
override fun onSlide(topSheet: View, slideOffset: Float, isOpening: Boolean?) {
if (interactor.onModeRequested() is Mode.Normal && useFab) {
if (slideOffset >= SLIDE_OFFSET) {
fabView.new_tab_button.show()
} else {
fabView.new_tab_button.hide()
if (!isTabsTrayFullScreenMode) {
if (useTopTabsTray) {
(behavior as TopSheetBehavior).setTopSheetCallback(object :
TopSheetBehavior.TopSheetCallback() {
override fun onSlide(topSheet: View, slideOffset: Float, isOpening: Boolean?) {
if (interactor.onModeRequested() is Mode.Normal && useFab) {
if (slideOffset >= SLIDE_OFFSET) {
fabView.new_tab_button.show()
} else {
fabView.new_tab_button.hide()
}
}
}
}
override fun onStateChanged(topSheet: View, newState: Int) {
if (newState == TopSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
override fun onStateChanged(topSheet: View, newState: Int) {
if (newState == TopSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
}
}
}
})
} else {
(behavior as BottomSheetBehavior).addBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
if (interactor.onModeRequested() is Mode.Normal && useFab) {
if (slideOffset >= SLIDE_OFFSET) {
fabView.new_tab_button.show()
} else {
fabView.new_tab_button.hide()
})
} else {
(behavior as BottomSheetBehavior).addBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
if (interactor.onModeRequested() is Mode.Normal && useFab) {
if (slideOffset >= SLIDE_OFFSET) {
fabView.new_tab_button.show()
} else {
fabView.new_tab_button.hide()
}
}
}
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
}
}
}
})
})
}
}
if (isTabsTrayFullScreenMode) {
view.exit_tabs_screen.setOnClickListener {
interactor.onTabTrayDismissed()
}
}
val selectedTabIndex = if (!isPrivate) {
@ -218,6 +251,8 @@ class TabTrayView(
// Put the 'Add to collections' button after the tabs have loaded.
// And, put the Synced Tabs adapter at the end.
if (reverseTabOrderInTabsTray) {
// Put these at the start when reverse tab order is enabled. Also, we disallow
// reverse tab order for compact tabs in settings.
concatAdapter.addAdapter(0, collectionsButtonAdapter)
concatAdapter.addAdapter(0, syncedTabsController.adapter)
} else {
@ -330,32 +365,15 @@ class TabTrayView(
if (enableCompactTabs) {
val gridLayoutManager = GridLayoutManager(container.context, gridViewNumberOfCols(container.context))
if (useTopTabsTray) {
if (!reverseTabOrderInTabsTray) {
gridLayoutManager.reverseLayout = true
}
} else {
if (reverseTabOrderInTabsTray) {
gridLayoutManager.reverseLayout = true
}
gridLayoutManager.reverseLayout = true
}
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val numTabs = tabsAdapter.itemCount
val totalItems = numTabs + collectionsButtonAdapter.itemCount +
syncedTabsController.adapter.itemCount
return if (reverseTabOrderInTabsTray) {
if (totalItems - 1 - position < numTabs) {
1
} else {
gridViewNumberOfCols(container.context)
}
return if (position < numTabs) {
1
} else {
if (position < numTabs) {
1
} else {
gridViewNumberOfCols(container.context)
}
gridViewNumberOfCols(container.context)
}
}
}
@ -382,10 +400,12 @@ class TabTrayView(
}
fun expand() {
if (useTopTabsTray) {
(behavior as TopSheetBehavior).state = TopSheetBehavior.STATE_EXPANDED
} else {
(behavior as BottomSheetBehavior).state = BottomSheetBehavior.STATE_EXPANDED
if (!isTabsTrayFullScreenMode) {
if (useTopTabsTray) {
(behavior as TopSheetBehavior).state = TopSheetBehavior.STATE_EXPANDED
} else {
(behavior as BottomSheetBehavior).state = BottomSheetBehavior.STATE_EXPANDED
}
}
}
@ -570,10 +590,12 @@ class TabTrayView(
)
)
if (isTabsTrayFullScreenMode) {
view.exit_tabs_screen.isVisible = !multiselect
}
view.tab_layout.isVisible = !multiselect
view.tab_tray_empty_view.isVisible = !multiselect
view.tab_tray_overflow.isVisible = !multiselect
view.tab_layout.isVisible = !multiselect
}
private fun updateTabsForMultiselectModeChanged(inMultiselectMode: Boolean) {
@ -622,14 +644,16 @@ class TabTrayView(
}
fun setTopOffset(landscape: Boolean) {
val topOffset = if (landscape) {
0
} else {
view.context.resources.getDimension(R.dimen.tab_tray_top_offset).toInt()
}
if (!isTabsTrayFullScreenMode) {
val topOffset = if (landscape) {
0
} else {
view.context.resources.getDimension(R.dimen.tab_tray_top_offset).toInt()
}
if (!useTopTabsTray) {
(behavior as BottomSheetBehavior).setExpandedOffset(topOffset)
if (!useTopTabsTray) {
(behavior as BottomSheetBehavior).setExpandedOffset(topOffset)
}
}
}
@ -664,18 +688,12 @@ class TabTrayView(
val selectedBrowserTabIndex = tabs
.indexOfFirst { it.id == sessionId }
val recyclerViewIndex = if (reverseTabOrderInTabsTray && !enableCompactTabs) {
val recyclerViewIndex = if (reverseTabOrderInTabsTray) {
// For reverse tab order and non-compact tabs, we add the items in collections button
// adapter and synced tabs adapter, and offset by 1 to show the tab above the
// current tab, unless current tab is first in reverse order.
min(selectedBrowserTabIndex + 1, tabsAdapter.itemCount - 1) +
collectionsButtonAdapter.itemCount + syncedTabsController.adapter.itemCount
} else if (reverseTabOrderInTabsTray && enableCompactTabs) {
// For reverse tab order and compact tabs, we add the items in collections button
// adapter and synced tabs adapter, and offset by -1 to show the tab above the
// current tab, unless current tab is first in reverse order.
max(0, selectedBrowserTabIndex - 1 + collectionsButtonAdapter.itemCount +
syncedTabsController.adapter.itemCount)
} else {
// We offset index by -1 to show the tab above the current tab, unless current tab
// is the first.

@ -875,9 +875,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
BuildConfig.AMO_COLLECTION
)
val enableCompactTabs by booleanPreference(
var enableCompactTabs by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_compact_tab),
default = false
default = true
)
val useTopTabsTray by booleanPreference(
@ -885,17 +885,25 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = false
)
val reverseTabOrderInTabsTray by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_reverse_tab_order),
var useFullScreenTabScreen by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_use_fullscreen_tabs_screen),
default = true
)
val useNewTabFloatingActionButton by booleanPreference(
val shouldUseFennecStyleTabsScreen: Boolean
get() = enableCompactTabs && useFullScreenTabScreen
var reverseTabOrderInTabsTray by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_reverse_tab_order),
default = false
)
var useNewTabFloatingActionButton by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_use_fab),
default = true
default = false
)
val placeNewTabFloatingActionButtonAtTop by booleanPreference(
var placeNewTabFloatingActionButtonAtTop by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_fab_top_position),
default = false
)

@ -8,6 +8,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?primaryText"
android:fillColor="@color/primary_text_normal_theme"
android:pathData="M13,4a1,1 0,1 0,-2 0v7H4a1,1 0,1 0,0 2h7v7a1,1 0,1 0,2 0v-7h7a1,1 0,1 0,0 -2h-7V4z" />
</vector>

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab_wrapper"
style="@style/TopSheetModal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/foundation_normal_theme">
<View
android:id="@+id/handle"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:background="@color/secondary_text_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.1" />
<TextView
android:id="@+id/tab_tray_empty_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:paddingBottom="80dp"
android:text="@string/no_open_tabs_description"
android:textColor="?secondaryText"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/divider" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/foundation_normal_theme"
app:layout_constraintBottom_toTopOf="@id/handle">
<ImageButton
android:id="@+id/exit_multi_select"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tab_tray_close_multiselect_content_description"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/multiselect_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/multiselect_title"
app:srcCompat="@drawable/ic_close"
app:tint="@color/contrast_text_normal_theme" />
<TextView
android:id="@+id/multiselect_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:textColor="@color/contrast_text_normal_theme"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/collect_multi_select"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@+id/exit_multi_select"
app:layout_constraintTop_toTopOf="parent"
tools:text="3 selected" />
<TextView
android:id="@+id/collect_multi_select"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tab_tray_collection_button_multiselect_content_description"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:text="@string/tab_tray_save_to_collection"
android:textAllCaps="true"
android:textColor="@color/contrast_text_normal_theme"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_tab_collection"
app:drawableTint="@color/contrast_text_normal_theme"
app:fontFamily="@font/metropolis_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/exit_tabs_screen"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tabs_screen_close_screen_description"
app:layout_constraintBottom_toBottomOf="@+id/tab_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/tab_layout"
app:srcCompat="@drawable/mozac_ic_back"
app:tint="@color/primary_text_normal_theme" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@color/foundation_normal_theme"
app:layout_constraintStart_toEndOf="@id/exit_tabs_screen"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.35"
app:tabGravity="fill"
app:tabIconTint="@color/tab_icon"
app:tabIndicatorColor="@color/accent_normal_theme"
app:tabIndicatorGravity="top"
app:tabRippleColor="@android:color/transparent">
<com.google.android.material.tabs.TabItem
android:id="@+id/default_tab_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:contentDescription="@string/tab_header_label"
android:layout="@layout/tabs_tray_tab_counter"
app:tabIconTint="@color/tab_icon" />
<com.google.android.material.tabs.TabItem
android:id="@+id/private_tab_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:contentDescription="@string/tabs_header_private_tabs_title"
android:icon="@drawable/ic_private_browsing" />
</com.google.android.material.tabs.TabLayout>
<ImageButton
android:id="@+id/tab_tray_new_tab"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/add_tab"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
app:layout_constraintTop_toTopOf="@id/tab_layout"
app:srcCompat="@drawable/ic_new" />
<ImageButton
android:id="@+id/tab_tray_overflow"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/open_tabs_menu"
android:visibility="visible"
app:tint="@color/accent_normal_theme"
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tab_layout"
app:srcCompat="@drawable/ic_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/tab_tray_item_divider_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/topBar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabsTray"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab_wrapper"
style="@style/BottomSheetModal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/foundation_normal_theme">
<View
android:id="@+id/handle"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginTop="8dp"
android:visibility="gone"
android:background="@color/secondary_text_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.1" />
<TextView
android:id="@+id/tab_tray_empty_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:paddingTop="80dp"
android:text="@string/no_open_tabs_description"
android:textColor="?secondaryText"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topBar" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/foundation_normal_theme"
app:layout_constraintTop_toBottomOf="@+id/handle">
<ImageButton
android:id="@+id/exit_multi_select"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tab_tray_close_multiselect_content_description"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/multiselect_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/multiselect_title"
app:srcCompat="@drawable/ic_close"
app:tint="@color/contrast_text_normal_theme" />
<TextView
android:id="@+id/multiselect_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:textColor="@color/contrast_text_normal_theme"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/collect_multi_select"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@+id/exit_multi_select"
app:layout_constraintTop_toTopOf="parent"
tools:text="3 selected" />
<TextView
android:id="@+id/collect_multi_select"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tab_tray_collection_button_multiselect_content_description"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:text="@string/tab_tray_save_to_collection"
android:textAllCaps="true"
android:textColor="@color/contrast_text_normal_theme"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_tab_collection"
app:drawableTint="@color/contrast_text_normal_theme"
app:fontFamily="@font/metropolis_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/exit_tabs_screen"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/tabs_screen_close_screen_description"
app:layout_constraintBottom_toBottomOf="@+id/tab_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/tab_layout"
app:srcCompat="@drawable/mozac_ic_back"
app:tint="@color/primary_text_normal_theme" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="@color/foundation_normal_theme"
app:layout_constraintStart_toEndOf="@id/exit_tabs_screen"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.35"
app:tabGravity="fill"
app:tabIconTint="@color/tab_icon"
app:tabIndicatorColor="@color/accent_normal_theme"
app:tabRippleColor="@android:color/transparent">
<com.google.android.material.tabs.TabItem
android:id="@+id/default_tab_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:contentDescription="@string/tab_header_label"
android:layout="@layout/tabs_tray_tab_counter"
app:tabIconTint="@color/tab_icon" />
<com.google.android.material.tabs.TabItem
android:id="@+id/private_tab_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:contentDescription="@string/tabs_header_private_tabs_title"
android:icon="@drawable/ic_private_browsing" />
</com.google.android.material.tabs.TabLayout>
<ImageButton
android:id="@+id/tab_tray_new_tab"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/add_tab"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
app:layout_constraintEnd_toStartOf="@id/tab_tray_overflow"
app:layout_constraintTop_toTopOf="@id/tab_layout"
app:srcCompat="@drawable/ic_new" />
<ImageButton
android:id="@+id/tab_tray_overflow"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/open_tabs_menu"
android:visibility="visible"
app:tint="@color/accent_normal_theme"
app:layout_constraintBottom_toBottomOf="@id/tab_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tab_layout"
app:srcCompat="@drawable/ic_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/tab_tray_item_divider_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/topBar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabsTray"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingBottom="140dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -108,6 +108,7 @@
app:tabGravity="fill"
app:tabIconTint="@color/tab_icon"
app:tabIndicatorColor="@color/accent_normal_theme"
app:tabIndicatorGravity="top"
app:tabRippleColor="@android:color/transparent">
<com.google.android.material.tabs.TabItem

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/onboarding_card"
style="@style/OnboardingCardLightWithPadding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<TextView
android:id="@+id/header_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:gravity="center_vertical"
android:lines="1"
android:text="@string/onboarding_tabs_layout_header"
android:textAppearance="@style/HeaderTextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/description_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/header_text"
android:text="@string/onboarding_tabs_tray_layout_description" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/tabs_tray_fennec_style"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:checked="true"
android:foreground="@drawable/rounded_ripple"
android:gravity="top"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:theme="@style/Checkable.Colored"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:onboardingKey="@string/pref_key_use_fullscreen_tabs_screen"
app:onboardingKeyDescription="@string/onboarding_tabs_tray_fennec_layout_description"
app:onboardingKeyTitle="@string/onboarding_tabs_tray_fennec_layout_button"
tools:text="Standard" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/tabs_tray_fenix_style"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:checked="false"
android:foreground="@drawable/rounded_ripple"
android:gravity="top"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/primary_state_list_text_color"
android:theme="@style/Checkable.Colored"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs_tray_fennec_style"
app:onboardingKey="@string/pref_key_use_fenix_tabs_tray"
app:onboardingKeyDescription="@string/onboarding_tabs_tray_fenix_layout_description"
app:onboardingKeyTitle="@string/onboarding_tabs_tray_fenix_layout_button"
tools:text="Strict" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -130,6 +130,8 @@
<string name="pref_tabs_tray_settings_category" translatable="false">pref_tabs_tray_settings_category</string>
<string name="pref_key_tabs_tray_compact_tab" translatable="false">pref_key_tabs_tray_compact_tab</string>
<string name="pref_key_tabs_tray_top_tray" translatable="false">pref_key_tabs_tray_top_tray</string>
<string name="pref_key_use_fullscreen_tabs_screen" translatable="false">pref_key_use_fullscreen_tabs_screen</string>
<string name="pref_key_use_fenix_tabs_tray" translatable="false">pref_key_use_fenix_tabs_tray</string>
<string name="pref_key_tabs_tray_reverse_tab_order" translatable="false">pref_key_tabs_tray_reverse_tab_order</string>
<!-- Tabs Tray FAB Customization Settings -->

@ -33,6 +33,8 @@
<string name="tab_tray_select_collection">Select collection</string>
<!-- Content description for close button while in multiselect mode in tab tray -->
<string name="tab_tray_close_multiselect_content_description">Exit multiselect mode</string>
<!-- Content description for back button in full screen tabs screen -->
<string name="tabs_screen_close_screen_description">Close tabs screen</string>
<!-- Content description for save to collection button while in multiselect mode in tab tray -->
<string name="tab_tray_collection_button_multiselect_content_description">Save selected tabs to collection</string>
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
@ -1072,6 +1074,18 @@
<string name="onboarding_tracking_protection_strict_option">Strict</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description_2">Blocks more trackers, ads, and popups. Pages load faster, but some functionality might not work.</string>
<!-- text for the tabs tray layout onboarding card header -->
<string name="onboarding_tabs_layout_header">Choose your tabs tray layout</string>
<!-- text for the tabs tray layout card description-->
<string name="onboarding_tabs_tray_layout_description">Pick either the old Firefox (Fennec) style tabs screen or the new and fancy (Fenix) style tabs tray.</string>
<!-- text for old Fennec style tab screen layout -->
<string name="onboarding_tabs_tray_fennec_layout_button">Old Fennec style (default)</string>
<!-- text for old Fennec style description -->
<string name="onboarding_tabs_tray_fennec_layout_description">Clicking the tab count on the toolbar will show you a tabs screen with a layout similar to the previous (Fennec) version of Firefox.</string>
<!-- text for Fenix style tab tray layout -->
<string name="onboarding_tabs_tray_fenix_layout_button">New Fenix style</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tabs_tray_fenix_layout_description">Clicking the tab count on the toolbar will show you a new tabs tray layout from the Fenix version of Firefox.</string>
<!-- text for the toolbar position card header
In English this is an idiom for "choose a side as in an argument or fight"
but it is ok to make this more literally about "choosing a position in a physical space -->
@ -1549,6 +1563,8 @@
<string name="enable_compact_tabs">Enable compact tabs</string>
<!-- Label for enable top tabs tray preference -->
<string name="enable_top_tabs_tray">Enable top tabs tray</string>
<!-- Label for fullscreen tabs tray preference -->
<string name="use_fullscreen_tabs_screen">Enable fullscreen tabs screen</string>
<!-- Label for reverse tab order in tabs tray preference -->
<string name="reverse_tab_order_tabs_tray">Reverse tab order in tray</string>
<!-- Summary for reverse tab order preference -->

@ -57,7 +57,7 @@
app:allowDividerAbove="true"
app:iconSpaceReserved="false">
<SwitchPreference
android:defaultValue="false"
android:defaultValue="true"
android:key="@string/pref_key_tabs_tray_compact_tab"
android:title="@string/enable_compact_tabs" />
<SwitchPreference
@ -66,6 +66,10 @@
android:title="@string/enable_top_tabs_tray" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/pref_key_use_fullscreen_tabs_screen"
android:title="@string/use_fullscreen_tabs_screen" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_key_tabs_tray_reverse_tab_order"
android:title="@string/reverse_tab_order_tabs_tray"
android:summary="@string/reverse_tab_order_description" />
@ -78,7 +82,7 @@
app:allowDividerAbove="false"
app:iconSpaceReserved="false">
<SwitchPreference
android:defaultValue="true"
android:defaultValue="false"
android:key="@string/pref_key_tabs_tray_use_fab"
android:title="@string/enable_fab_tabs_tray" />
<SwitchPreference

Loading…
Cancel
Save