Massive tabs tray customization options

pull/65/head
Abhijit Valluri 4 years ago
parent 23ff09fba6
commit 275c5c1a7b

@ -57,6 +57,8 @@ class CustomizationFragment : PreferenceFragmentCompat() {
bindAutoBatteryTheme()
setupRadioGroups()
setupToolbarCategory()
setupTabsTrayCategory()
setupFabCategory()
setupHomeCategory()
setupAddonsCustomizationCategory()
}
@ -142,10 +144,40 @@ class CustomizationFragment : PreferenceFragmentCompat() {
addToRadioGroup(topPreference, bottomPreference)
}
private fun setupTabsTrayCategory() {
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_compact_tab).apply {
isChecked = context.settings().enableCompactTabs
onPreferenceChangeListener = SharedPreferenceUpdater()
}
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_reverse_tab_order).apply {
isChecked = context.settings().reverseTabOrderInTabsTray
onPreferenceChangeListener = SharedPreferenceUpdater()
}
}
private fun setupFabCategory() {
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_use_fab).apply {
isChecked = context.settings().useNewTabFloatingActionButton
onPreferenceChangeListener = SharedPreferenceUpdater()
}
requirePreference<SwitchPreference>(R.string.pref_key_tabs_tray_fab_top_position).apply {
isChecked = context.settings().placeNewTabFloatingActionButtonAtTop
onPreferenceChangeListener = SharedPreferenceUpdater()
}
}
private fun setupHomeCategory() {
requirePreference<PreferenceCategory>(R.string.pref_home_category).apply {
isVisible = FeatureFlags.topFrecentSite
}
requirePreference<SwitchPreference>(R.string.pref_key_enable_top_frecent_sites).apply {
isVisible = FeatureFlags.topFrecentSite
isChecked = context.settings().showTopFrecentSites

@ -18,6 +18,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.settings
class FenixTabsAdapter(
private val context: Context,
@ -26,7 +27,7 @@ class FenixTabsAdapter(
viewHolderProvider = { parentView ->
TabTrayViewHolder(
LayoutInflater.from(context).inflate(
R.layout.tab_tray_item,
if (context.settings().enableCompactTabs) R.layout.tab_tray_item_compact else R.layout.tab_tray_item,
parentView,
false
),

@ -21,6 +21,7 @@ import mozilla.components.browser.storage.sync.SyncedDeviceTabs
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.sync.ListenerDelegate
import org.mozilla.fenix.sync.SyncedTabsAdapter
import org.mozilla.fenix.sync.ext.toAdapterList
@ -63,7 +64,11 @@ class SyncedTabsController(
override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) {
scope.launch {
val tabsList = listOf(SyncedTabsAdapter.AdapterItem.Title) + syncedTabs.toAdapterList()
adapter.submitList(tabsList)
if (view.context.settings().reverseTabOrderInTabsTray) {
adapter.submitList(tabsList.reversed())
} else {
adapter.submitList(tabsList)
}
}
}

@ -21,7 +21,8 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.component_tabstray.view.*
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.*
import kotlinx.android.synthetic.main.component_tabstray_fab_bottom.view.*
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.*
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.*
import kotlinx.coroutines.Dispatchers
@ -66,7 +67,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
private lateinit var tabTrayDialogStore: TabTrayDialogFragmentStore
private val snackbarAnchor: View?
get() = null
get() = if (tabTrayView.fabView.new_tab_button.isVisible) tabTrayView.fabView.new_tab_button
else null
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
override fun onCollectionCreated(title: String, sessions: List<Session>) {

@ -21,10 +21,12 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
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.view.*
import kotlinx.android.synthetic.main.component_tabstray_bottom.view.*
import kotlinx.android.synthetic.main.component_tabstray_fab_bottom.view.*
import kotlinx.android.synthetic.main.tabs_tray_tab_counter.*
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay
@ -45,13 +47,14 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.INFINITE_CHAR_PADDING_BOTTOM
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.MAX_VISIBLE_TABS
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.SO_MANY_TABS_OPEN
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.components.topsheet.TopSheetBehavior
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.tabtray.SaveToCollectionsButtonAdapter.MultiselectModeChange
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode
import java.text.NumberFormat
import kotlin.math.max
import kotlin.math.min
import mozilla.components.browser.storage.sync.Tab as SyncTab
/**
@ -70,16 +73,27 @@ class TabTrayView(
) : LayoutContainer, TabLayout.OnTabSelectedListener {
val lifecycleScope = lifecycleOwner.lifecycleScope
val view = when (container.context.settings().toolbarPosition) {
ToolbarPosition.BOTTOM -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray_bottom, container, true)
ToolbarPosition.TOP -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray, container, true)
private val useFab = container.context.settings().useNewTabFloatingActionButton
val fabView: View = when (container.context.settings().placeNewTabFloatingActionButtonAtTop) {
true -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray_fab_top, container, true)
false -> LayoutInflater.from(container.context).inflate(R.layout.component_tabstray_fab_bottom, container, true)
}
private val enableCompactTabs = container.context.settings().enableCompactTabs
private val reverseTabOrderInTabsTray = container.context.settings().reverseTabOrderInTabsTray
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)
}
private val isPrivateModeSelected: Boolean get() = view.tab_layout.selectedTabPosition == PRIVATE_TAB_ID
private val behavior = when (container.context.settings().toolbarPosition) {
ToolbarPosition.BOTTOM -> TopSheetBehavior.from(view.tab_wrapper)
ToolbarPosition.TOP -> BottomSheetBehavior.from(view.tab_wrapper)
private val behavior = when (useTopTabsTray) {
true -> TopSheetBehavior.from(view.tab_wrapper)
false -> BottomSheetBehavior.from(view.tab_wrapper)
}
private val concatAdapter = ConcatAdapter(tabsAdapter)
@ -102,27 +116,43 @@ class TabTrayView(
init {
components.analytics.metrics.track(Event.TabsTrayOpened)
if (container.context.settings().toolbarPosition == ToolbarPosition.TOP) {
(behavior as BottomSheetBehavior).addBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
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()
}
}
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
override fun onStateChanged(topSheet: View, newState: Int) {
if (newState == TopSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
}
}
})
} else {
(behavior as TopSheetBehavior).setTopSheetCallback(object :
TopSheetBehavior.TopSheetCallback() {
override fun onSlide(topSheet: View, slideOffset: Float, isOpening: Boolean?) {
(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(topSheet: View, newState: Int) {
if (newState == TopSheetBehavior.STATE_HIDDEN) {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
}
@ -186,19 +216,28 @@ class TabTrayView(
tabsAdapter.tabTrayInteractor = interactor
tabsAdapter.onTabsUpdated = {
// Put the 'Add to collections' button after the tabs have loaded.
concatAdapter.addAdapter(collectionsButtonAdapter)
// Put the Synced Tabs adapter at the end.
concatAdapter.addAdapter(syncedTabsController.adapter)
if (view.context.settings().accessibilityServicesEnabled) {
tabsAdapter.notifyDataSetChanged()
// And, put the Synced Tabs adapter at the end.
if (reverseTabOrderInTabsTray) {
concatAdapter.addAdapter(0, collectionsButtonAdapter)
concatAdapter.addAdapter(0, syncedTabsController.adapter)
} else {
concatAdapter.addAdapter(collectionsButtonAdapter)
concatAdapter.addAdapter(syncedTabsController.adapter)
}
// Disabling the following block of code because it causes a crash when
// accessibility services are enabled! `notifyDataSetChanged()` is incompatible
// with concatAdapter. See: https://github.com/mozilla-mobile/fenix/issues/14540
// WARNING: Merging the upstream fix for this will cause lot of conflicts!
//
// if (hasAccessibilityEnabled) {
// tabsAdapter.notifyDataSetChanged()
// }
if (!hasLoaded) {
hasLoaded = true
scrollToTab(view.context.components.core.store.state.selectedTabId)
if (view.context.settings().accessibilityServicesEnabled) {
if (hasAccessibilityEnabled) {
lifecycleScope.launch {
delay(SELECTION_DELAY.toLong())
lifecycleScope.launch(Main) {
@ -260,7 +299,15 @@ class TabTrayView(
private fun adjustNewTabButtonForNormalMode() {
view.tab_tray_new_tab.apply {
visibility = View.VISIBLE
visibility = if (useFab) View.GONE else View.VISIBLE
setOnClickListener {
sendNewTabEvent(isPrivateModeSelected)
interactor.onNewTabTapped(isPrivateModeSelected)
}
}
fabView.new_tab_button.apply {
isVisible = useFab
setOnClickListener {
sendNewTabEvent(isPrivateModeSelected)
interactor.onNewTabTapped(isPrivateModeSelected)
@ -280,30 +327,65 @@ class TabTrayView(
fun updateTabsTrayLayout() {
view.tabsTray.apply {
val gridLayoutManager = GridLayoutManager(container.context, gridViewNumberOfCols(container.context))
if (container.context.settings().toolbarPosition == ToolbarPosition.BOTTOM) {
gridLayoutManager.reverseLayout = true
}
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val numTabs = tabsAdapter.itemCount
return if (position < numTabs) {
1
if (enableCompactTabs) {
val gridLayoutManager = GridLayoutManager(container.context, gridViewNumberOfCols(container.context))
if (useTopTabsTray) {
if (!reverseTabOrderInTabsTray) {
gridLayoutManager.reverseLayout = true
}
} else {
if (reverseTabOrderInTabsTray) {
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)
}
} else {
if (position < numTabs) {
1
} else {
gridViewNumberOfCols(container.context)
}
}
}
}
layoutManager = gridLayoutManager
} else {
val linearLayoutManager = LinearLayoutManager(container.context)
if (useTopTabsTray) {
if (!reverseTabOrderInTabsTray) {
linearLayoutManager.reverseLayout = true
} else {
gridViewNumberOfCols(container.context)
linearLayoutManager.stackFromEnd = true
}
} else {
if (reverseTabOrderInTabsTray) {
linearLayoutManager.reverseLayout = true
linearLayoutManager.stackFromEnd = true
}
}
}
layoutManager = gridLayoutManager
layoutManager = linearLayoutManager
}
}
}
fun expand() {
if (container.context.settings().toolbarPosition == ToolbarPosition.TOP) {
(behavior as BottomSheetBehavior).state = BottomSheetBehavior.STATE_EXPANDED
} else {
if (useTopTabsTray) {
(behavior as TopSheetBehavior).state = TopSheetBehavior.STATE_EXPANDED
} else {
(behavior as BottomSheetBehavior).state = BottomSheetBehavior.STATE_EXPANDED
}
}
@ -319,6 +401,7 @@ class TabTrayView(
}
override fun onTabSelected(tab: TabLayout.Tab?) {
toggleFabText(isPrivateModeSelected)
filterTabs.invoke(isPrivateModeSelected)
toggleSaveToCollectionButton(isPrivateModeSelected)
@ -343,7 +426,7 @@ class TabTrayView(
if (oldMode::class != state.mode::class) {
updateTabsForMultiselectModeChanged(state.mode is Mode.MultiSelect)
if (view.context.settings().accessibilityServicesEnabled) {
if (hasAccessibilityEnabled) {
view.announceForAccessibility(
if (state.mode == Mode.Normal) view.context.getString(
R.string.tab_tray_exit_multiselect_content_description
@ -369,6 +452,7 @@ class TabTrayView(
toggleUIMultiselect(multiselect = true)
fabView.new_tab_button.isVisible = false
view.tab_tray_new_tab.isVisible = false
view.collect_multi_select.isVisible = state.mode.selectedItems.isNotEmpty()
@ -389,7 +473,7 @@ class TabTrayView(
val unselectedItems = oldMode.selectedItems - state.mode.selectedItems
state.mode.selectedItems.union(unselectedItems).forEach { item ->
if (view.context.settings().accessibilityServicesEnabled) {
if (hasAccessibilityEnabled) {
view.announceForAccessibility(
if (unselectedItems.contains(item)) view.context.getString(
R.string.tab_tray_item_unselected_multiselect_content_description,
@ -463,12 +547,12 @@ class TabTrayView(
if (multiselect) MULTISELECT_HANDLE_HEIGHT.dpToPx(displayMetrics) else NORMAL_HANDLE_HEIGHT.dpToPx(
displayMetrics
)
if (container.context.settings().toolbarPosition == ToolbarPosition.TOP) {
topMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_TOP_MARGIN.dpToPx(
if (useTopTabsTray) {
bottomMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_BOTTOM_MARGIN.dpToPx(
displayMetrics
)
} else {
bottomMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_BOTTOM_MARGIN.dpToPx(
topMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_TOP_MARGIN.dpToPx(
displayMetrics
)
}
@ -544,7 +628,7 @@ class TabTrayView(
view.context.resources.getDimension(R.dimen.tab_tray_top_offset).toInt()
}
if (container.context.settings().toolbarPosition == ToolbarPosition.TOP) {
if (!useTopTabsTray) {
(behavior as BottomSheetBehavior).setExpandedOffset(topOffset)
}
}
@ -553,6 +637,18 @@ class TabTrayView(
menu?.dismiss()
}
private fun toggleFabText(private: Boolean) {
if (private) {
fabView.new_tab_button.extend()
fabView.new_tab_button.contentDescription =
view.context.resources.getString(R.string.add_private_tab)
} else {
fabView.new_tab_button.shrink()
fabView.new_tab_button.contentDescription =
view.context.resources.getString(R.string.add_tab)
}
}
fun onBackPressed(): Boolean {
return interactor.onBackPressed()
}
@ -568,7 +664,25 @@ class TabTrayView(
val selectedBrowserTabIndex = tabs
.indexOfFirst { it.id == sessionId }
layoutManager?.scrollToPosition(selectedBrowserTabIndex)
val recyclerViewIndex = if (reverseTabOrderInTabsTray && !enableCompactTabs) {
// 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.
max(0, selectedBrowserTabIndex - 1)
}
layoutManager?.scrollToPosition(recyclerViewIndex)
}
}

@ -19,6 +19,7 @@ import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.tabstray.TabViewHolder
import mozilla.components.browser.tabstray.TabsTrayStyling
import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
import mozilla.components.browser.toolbar.MAX_URI_LENGTH
import mozilla.components.concept.tabstray.Tab
import mozilla.components.concept.tabstray.TabsTray
import mozilla.components.feature.media.ext.pauseIfPlaying
@ -29,12 +30,7 @@ import mozilla.components.support.images.loader.ImageLoader
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getMediaStateForSession
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.removeAndDisable
import org.mozilla.fenix.ext.removeTouchDelegate
import org.mozilla.fenix.ext.showAndEnable
import org.mozilla.fenix.ext.*
import org.mozilla.fenix.utils.Do
import kotlin.math.max
@ -57,6 +53,7 @@ class TabTrayViewHolder(
itemView.findViewById(R.id.mozac_browser_tabstray_thumbnail)
@VisibleForTesting
internal val urlView: TextView? = itemView.findViewById(R.id.mozac_browser_tabstray_url)
private val playPauseButtonView: ImageButton = itemView.findViewById(R.id.play_pause_button)
override var tab: Tab? = null
@ -74,6 +71,7 @@ class TabTrayViewHolder(
// Basic text
updateTitle(tab)
updateUrl(tab)
updateIcon(tab)
updateCloseButtonDescription(tab.title)
@ -148,6 +146,24 @@ class TabTrayViewHolder(
titleView.text = title
}
private fun updateUrl(tab: Tab) {
// Truncate to MAX_URI_LENGTH to prevent the UI from locking up for
// extremely large URLs such as data URIs or bookmarklets. The same
// is done in the toolbar and awesomebar:
// https://github.com/mozilla-mobile/fenix/issues/1824
// https://github.com/mozilla-mobile/android-components/issues/6985
urlView?.apply {
text =
if (context.settings().shouldStripUrl) {
tab.url
.toShortUrl(itemView.context.components.publicSuffixList)
.take(MAX_URI_LENGTH)
} else {
tab.url.take(MAX_URI_LENGTH)
}
}
}
private fun updateIcon(tab: Tab) {
if (tab.icon != null) {
iconCard.visibility = View.VISIBLE

@ -875,6 +875,31 @@ class Settings(private val appContext: Context) : PreferencesHolder {
BuildConfig.AMO_COLLECTION
)
val enableCompactTabs by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_compact_tab),
default = false
)
val useTopTabsTray by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_top_tray),
default = false
)
val reverseTabOrderInTabsTray by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_reverse_tab_order),
default = true
)
val useNewTabFloatingActionButton by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_use_fab),
default = true
)
val placeNewTabFloatingActionButtonAtTop by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_tabs_tray_fab_top_position),
default = false
)
private var savedLoginsSortingStrategyString by stringPreference(
appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy),
default = SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort.strategyString

@ -6,44 +6,45 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab_wrapper"
style="@style/TopSheetModal"
style="@style/BottomSheetModal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/foundation_normal_theme"
app:layout_behavior="org.mozilla.fenix.components.topsheet.TopSheetBehavior">
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<View
android:id="@+id/handle"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="@color/secondary_text_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="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="wrap_content"
android:layout_height="0dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:paddingBottom="80dp"
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_constraintBottom_toTopOf="@+id/divider" />
app:layout_constraintTop_toBottomOf="@id/topBar" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/foundation_normal_theme"
app:layout_constraintBottom_toTopOf="@id/handle">
app:layout_constraintTop_toBottomOf="@+id/handle">
<ImageButton
android:id="@+id/exit_multi_select"
@ -161,17 +162,18 @@
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" />
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_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/divider" />
app:layout_constraintTop_toBottomOf="@+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,22 @@
<?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/. -->
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/new_tab_button"
style="@style/TabTrayFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:scrollbars="none"
android:layout_margin="16dp"
android:backgroundTint="@color/accent_normal_theme"
android:contentDescription="@string/add_tab"
android:elevation="99dp"
android:text="@string/tab_drawer_fab_content"
android:textColor="@color/photonWhite"
app:elevation="99dp"
app:borderWidth="0dp"
app:icon="@drawable/ic_new"
app:iconTint="@color/photonWhite" />

@ -0,0 +1,22 @@
<?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/. -->
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/new_tab_button"
style="@style/TabTrayFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:scrollbars="none"
android:layout_margin="16dp"
android:backgroundTint="@color/accent_normal_theme"
android:contentDescription="@string/add_tab"
android:elevation="99dp"
android:text="@string/tab_drawer_fab_content"
android:textColor="@color/photonWhite"
app:elevation="99dp"
app:borderWidth="0dp"
app:icon="@drawable/ic_new"
app:iconTint="@color/photonWhite" />

@ -6,45 +6,44 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab_wrapper"
style="@style/BottomSheetModal"
style="@style/TopSheetModal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/foundation_normal_theme"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
app:layout_behavior="org.mozilla.fenix.components.topsheet.TopSheetBehavior">
<View
android:id="@+id/handle"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@color/secondary_text_normal_theme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="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="0dp"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:paddingTop="80dp"
android:paddingBottom="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" />
app:layout_constraintBottom_toTopOf="@+id/divider" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/foundation_normal_theme"
app:layout_constraintTop_toBottomOf="@+id/handle">
app:layout_constraintBottom_toTopOf="@id/handle">
<ImageButton
android:id="@+id/exit_multi_select"
@ -162,18 +161,17 @@
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" />
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: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" />
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -7,7 +7,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab_item"
android:layout_width="match_parent"
android:layout_height="165dp"
android:layout_height="88dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:selectableItemBackground">
@ -16,21 +16,22 @@
android:id="@+id/play_pause_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="23dp"
android:layout_marginStart="80dp"
android:layout_marginTop="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/mozac_feature_media_notification_action_pause"
android:elevation="10dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/media_state_play" />
<androidx.cardview.widget.CardView
android:id="@+id/mozac_browser_tabstray_card"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_tray_thumbnail_height"
android:layout_marginHorizontal="7dp"
android:layout_marginTop="30dp"
android:layout_width="@dimen/tab_tray_thumbnail_width_original"
android:layout_height="@dimen/tab_tray_thumbnail_height_original"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:backgroundTint="?tabTrayThumbnailItemBackground"
app:cardBackgroundColor="@color/photonWhite"
app:layout_constraintStart_toStartOf="parent"
@ -77,9 +78,9 @@
android:id="@+id/mozac_browser_tabstray_icon_card"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintStart_toStartOf="@id/mozac_browser_tabstray_card"
app:layout_constraintBottom_toTopOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card"
app:layout_constraintBottom_toBottomOf="@id/mozac_browser_tabstray_title"
app:cardCornerRadius="5dp" >
<ImageView
@ -90,7 +91,6 @@
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/mozac_browser_tabstray_title"
android:layout_width="0dp"
@ -100,24 +100,42 @@
android:fadingEdgeLength="25dp"
android:ellipsize="none"
android:singleLine="true"
android:paddingHorizontal="7dp"
android:paddingVertical="5dp"
android:paddingStart="8dp"
android:paddingTop="22dp"
android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="14sp"
android:visibility="visible"
tools:text="Webpage tile that is long"
android:textSize="16sp"
tools:text="This is the title of the tab and it is long"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/mozac_browser_tabstray_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="25dp"
android:ellipsize="none"
android:singleLine="true"
android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp"
tools:text="https://www.google.com"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toBottomOf="@id/mozac_browser_tabstray_title" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/mozac_browser_tabstray_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/close_tab"
app:layout_constraintEnd_toEndOf="@id/mozac_browser_tabstray_card"
app:layout_constraintBottom_toTopOf="@id/mozac_browser_tabstray_card"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/mozac_ic_close"
app:tint="@color/tab_tray_item_text_normal_theme" />

@ -0,0 +1,141 @@
<?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_item"
android:layout_width="match_parent"
android:layout_height="165dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:selectableItemBackground">
<ImageButton
android:id="@+id/play_pause_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="23dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/mozac_feature_media_notification_action_pause"
android:elevation="10dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/media_state_play" />
<androidx.cardview.widget.CardView
android:id="@+id/mozac_browser_tabstray_card"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_tray_thumbnail_height"
android:layout_marginHorizontal="7dp"
android:layout_marginTop="30dp"
android:backgroundTint="?tabTrayThumbnailItemBackground"
app:cardBackgroundColor="@color/photonWhite"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/default_tab_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:padding="22dp"
app:srcCompat="@drawable/mozac_ic_globe"
app:tint="?tabTrayThumbnailIcon" />
<mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
android:id="@+id/mozac_browser_tabstray_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/mozac_browser_tabstray_open_tab" />
<View
android:id="@+id/selected_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tab_tray_selected_mask_normal_theme"
android:visibility="gone" />
<ImageView
android:id="@+id/checkmark"
android:contentDescription="@string/tab_tray_multiselect_selected_content_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:background="@drawable/favicon_background"
android:backgroundTint="@color/accent_normal_theme"
android:elevation="1dp"
android:padding="10dp"
android:visibility="gone"
app:srcCompat="@drawable/mozac_ic_check" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/mozac_browser_tabstray_icon_card"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintStart_toStartOf="@id/mozac_browser_tabstray_card"
app:layout_constraintBottom_toTopOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toTopOf="parent"
app:cardCornerRadius="5dp" >
<ImageView
android:id="@+id/mozac_browser_tabstray_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/mozac_browser_tabstray_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="25dp"
android:ellipsize="none"
android:singleLine="true"
android:paddingHorizontal="7dp"
android:paddingVertical="5dp"
android:textColor="@color/tab_tray_item_text_normal_theme"
android:textSize="14sp"
android:visibility="visible"
tools:text="Webpage tile that is long and will overflow textview widget"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/mozac_browser_tabstray_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:lines="1"
android:paddingStart="8dp"
android:textColor="@color/tab_tray_item_url_normal_theme"
android:textSize="14sp"
tools:text="https://www.google.com"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toBottomOf="@id/mozac_browser_tabstray_title" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/mozac_browser_tabstray_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/close_tab"
app:layout_constraintEnd_toEndOf="@id/mozac_browser_tabstray_card"
app:layout_constraintBottom_toTopOf="@id/mozac_browser_tabstray_card"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/mozac_ic_close"
app:tint="@color/tab_tray_item_text_normal_theme" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -159,6 +159,9 @@
<dimen name="tab_tray_top_offset">40dp</dimen>
<dimen name="tab_tray_thumbnail_width">125dp</dimen>
<dimen name="tab_tray_thumbnail_height">130dp</dimen>
<!-- Tabs Tray -->
<dimen name="tab_tray_thumbnail_width_original">92dp</dimen>
<dimen name="tab_tray_thumbnail_height_original">69dp</dimen>
<!-- Saved Logins Fragment -->
<dimen name="saved_logins_sort_menu_dropdown_chevron_icon_margin_start">10dp</dimen>

@ -126,6 +126,17 @@
<!-- Customization Settings -->
<string name="pref_home_category" translatable="false">pref_home_category</string>
<!-- Tabs Tray Customization Settings -->
<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_tabs_tray_reverse_tab_order" translatable="false">pref_key_tabs_tray_reverse_tab_order</string>
<!-- Tabs Tray FAB Customization Settings -->
<string name="pref_tabs_tray_fab_settings_category" translatable="false">pref_tabs_tray_fab_settings_category</string>
<string name="pref_key_tabs_tray_use_fab" translatable="false">pref_key_tabs_tray_use_fab</string>
<string name="pref_key_tabs_tray_fab_top_position" translatable="false">pref_key_tabs_tray_fab_top_position</string>
<!-- Add-ons Source Customization Settings -->
<string name="pref_addons_settings_category" translatable="false">pref_addons_settings_category</string>
<string name="pref_key_addons_custom_account" translatable="false">pref_key_addons_custom_account</string>

@ -282,6 +282,10 @@
<string name="preferences_theme">Theme</string>
<!-- Preference for customizing the home screen -->
<string name="preferences_home">Home</string>
<!-- Preference for customizing the tabs tray -->
<string name="preferences_tabs_tray">Tabs tray</string>
<!-- Preference for customizing the tabs tray FAB -->
<string name="preferences_tabs_tray_fab">Floating action button (FAB)</string>
<!-- Preference for settings related to visual options -->
<string name="preferences_customize">Customize</string>
<!-- Preference description for banner about signing in -->
@ -1530,11 +1534,26 @@
<!-- Label for the show most visited sites preference -->
<string name="top_sites_toggle_top_frecent_sites">Show most visited sites</string>
<!-- Label for add-ons custom source account -->
<!-- Label for add-ons custom source account preference -->
<string name="addons_custom_source_account">Set custom add-ons account</string>
<!-- Label for add-ons custom source collection -->
<!-- Label for add-ons custom source collection preference -->
<string name="addons_custom_source_collection">Set custom add-ons collection</string>
<!-- Label for enable compact tabs in tabs tray preference -->
<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 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 -->
<string name="reverse_tab_order_description">Enable to put new tabs at start of the tabs list, disable to put new tabs at end of the tabs list</string>
<!-- Label for enable FAB in tabs tray preference -->
<string name="enable_fab_tabs_tray">Enable FAB for new tab</string>
<!-- Label for top FAB position in tabs tray preference -->
<string name="fab_tabs_tray_top">Place FAB at the top</string>
<!-- Label for top FAB position in tabs tray preference -->
<string name="fab_tabs_tray_top_description">Enable to place button for new tab at the top, disable to place it at the bottom</string>
<!-- Content description for close button in collection placeholder. -->
<string name="remove_home_collection_placeholder_content_description">Remove</string>

@ -35,7 +35,7 @@
<androidx.preference.PreferenceCategory
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_toolbar"
app:allowDividerAbove="false"
app:allowDividerAbove="true"
app:iconSpaceReserved="false">
<org.mozilla.fenix.settings.RadioButtonPreference
android:key="@string/pref_key_toolbar_top"
@ -50,11 +50,49 @@
android:title="@string/preferences_strip_url_title" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="@string/pref_tabs_tray_settings_category"
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_tabs_tray"
app:allowDividerAbove="true"
app:iconSpaceReserved="false">
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_key_tabs_tray_compact_tab"
android:title="@string/enable_compact_tabs" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_key_tabs_tray_top_tray"
android:title="@string/enable_top_tabs_tray" />
<SwitchPreference
android:defaultValue="true"
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" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="@string/pref_tabs_tray_fab_settings_category"
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_tabs_tray_fab"
app:allowDividerAbove="false"
app:iconSpaceReserved="false">
<SwitchPreference
android:defaultValue="true"
android:key="@string/pref_key_tabs_tray_use_fab"
android:title="@string/enable_fab_tabs_tray" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_key_tabs_tray_fab_top_position"
android:title="@string/fab_tabs_tray_top"
android:summary="@string/fab_tabs_tray_top_description" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="@string/pref_home_category"
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_home"
app:allowDividerAbove="false"
app:allowDividerAbove="true"
app:iconSpaceReserved="false"
app:isPreferenceVisible="false">
<androidx.preference.SwitchPreference
@ -66,7 +104,7 @@
android:key="@string/pref_addons_settings_category"
android:layout="@layout/preference_cat_style"
android:title="@string/preferences_addons_customization"
app:allowDividerAbove="false"
app:allowDividerAbove="true"
app:iconSpaceReserved="false">
<androidx.preference.EditTextPreference
android:key="@string/pref_key_addons_custom_account"

Loading…
Cancel
Save