1
0
Fork 0

Use a ListAdapter for SessionControl

master
Tiger Oakes 2019-05-21 14:03:32 -04:00 committed by Emily Kager
parent 2fd91daa5b
commit 207a8d6772
10 changed files with 141 additions and 164 deletions

View File

@ -205,8 +205,10 @@ class HomeFragment : Fragment(), CoroutineScope, AccountObserver {
BrowsingModeManager.Mode.Private -> BrowsingModeManager.Mode.Normal BrowsingModeManager.Mode.Private -> BrowsingModeManager.Mode.Normal
} }
val mode = if (newMode == BrowsingModeManager.Mode.Private) Mode.Private else Mode.Normal if (onboarding.userHasBeenOnboarded()) {
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.ModeChange(mode)) val mode = if (newMode == BrowsingModeManager.Mode.Private) Mode.Private else Mode.Normal
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.ModeChange(mode))
}
browsingModeManager.mode = newMode browsingModeManager.mode = newMode
} }

View File

@ -7,18 +7,22 @@ package org.mozilla.fenix.home.sessioncontrol
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer import io.reactivex.Observer
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import org.mozilla.fenix.home.sessioncontrol.viewholders.SaveTabGroupViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoTabMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoContentMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.SaveTabGroupViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFirefoxAccountViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFirefoxAccountViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder
@ -28,69 +32,69 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingSe
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
import mozilla.components.feature.tab.collections.Tab as ComponentTab import mozilla.components.feature.tab.collections.Tab as ComponentTab
import java.lang.IllegalStateException
sealed class AdapterItem { sealed class AdapterItem(@LayoutRes val viewType: Int) {
data class TabHeader(val isPrivate: Boolean, val hasTabs: Boolean) : AdapterItem() data class TabHeader(val isPrivate: Boolean, val hasTabs: Boolean) : AdapterItem(TabHeaderViewHolder.LAYOUT_ID)
object NoTabMessage : AdapterItem() data class TabItem(val tab: Tab) : AdapterItem(TabViewHolder.LAYOUT_ID) {
data class TabItem(val tab: Tab) : AdapterItem() override fun sameAs(other: AdapterItem) = other is TabItem && tab.sessionId == other.tab.sessionId
object SaveTabGroup : AdapterItem() }
object SaveTabGroup : AdapterItem(SaveTabGroupViewHolder.LAYOUT_ID)
object PrivateBrowsingDescription : AdapterItem() object PrivateBrowsingDescription : AdapterItem(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID)
data class NoContentMessage(
@DrawableRes val icon: Int,
@StringRes val header: Int,
@StringRes val description: Int
) : AdapterItem(NoContentMessageViewHolder.LAYOUT_ID)
object CollectionHeader : AdapterItem() object CollectionHeader : AdapterItem(CollectionHeaderViewHolder.LAYOUT_ID)
object NoCollectionMessage : AdapterItem() data class CollectionItem(
data class CollectionItem(val collection: TabCollection) : AdapterItem() val collection: TabCollection,
val expanded: Boolean
) : AdapterItem(CollectionViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem) = other is CollectionItem && collection.id == other.collection.id
}
data class TabInCollectionItem( data class TabInCollectionItem(
val collection: TabCollection, val collection: TabCollection,
val tab: ComponentTab, val tab: ComponentTab,
val isLastTab: Boolean val isLastTab: Boolean
) : AdapterItem() ) : AdapterItem(TabInCollectionViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem) = other is TabInCollectionItem && tab.id == other.tab.id
}
object OnboardingHeader : AdapterItem() object OnboardingHeader : AdapterItem(OnboardingHeaderViewHolder.LAYOUT_ID)
data class OnboardingSectionHeader(val labelBuilder: (Context) -> String) : AdapterItem() data class OnboardingSectionHeader(
data class OnboardingFirefoxAccount(val state: OnboardingState) : AdapterItem() val labelBuilder: (Context) -> String
object OnboardingThemePicker : AdapterItem() ) : AdapterItem(OnboardingSectionHeaderViewHolder.LAYOUT_ID) {
object OnboardingTrackingProtection : AdapterItem() override fun sameAs(other: AdapterItem) = other is OnboardingSectionHeader && labelBuilder == other.labelBuilder
object OnboardingPrivateBrowsing : AdapterItem() }
object OnboardingPrivacyNotice : AdapterItem() data class OnboardingFirefoxAccount(
object OnboardingFinish : AdapterItem() val state: OnboardingState
) : AdapterItem(OnboardingFirefoxAccountViewHolder.LAYOUT_ID)
object OnboardingThemePicker : AdapterItem(OnboardingThemePickerViewHolder.LAYOUT_ID)
object OnboardingTrackingProtection : AdapterItem(OnboardingTrackingProtectionViewHolder.LAYOUT_ID)
object OnboardingPrivateBrowsing : AdapterItem(OnboardingPrivateBrowsingViewHolder.LAYOUT_ID)
object OnboardingPrivacyNotice : AdapterItem(OnboardingPrivacyNoticeViewHolder.LAYOUT_ID)
object OnboardingFinish : AdapterItem(OnboardingFinishViewHolder.LAYOUT_ID)
val viewType: Int /**
get() = when (this) { * True if this item represents the same value as other. Used by [AdapterItemDiffCallback].
is TabHeader -> TabHeaderViewHolder.LAYOUT_ID */
NoTabMessage -> NoTabMessageViewHolder.LAYOUT_ID open fun sameAs(other: AdapterItem) = this::class == other::class
is TabItem -> TabViewHolder.LAYOUT_ID }
SaveTabGroup -> SaveTabGroupViewHolder.LAYOUT_ID
PrivateBrowsingDescription -> PrivateBrowsingDescriptionViewHolder.LAYOUT_ID class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
CollectionHeader -> CollectionHeaderViewHolder.LAYOUT_ID override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) = oldItem.sameAs(newItem)
NoCollectionMessage -> NoCollectionMessageViewHolder.LAYOUT_ID
is CollectionItem -> CollectionViewHolder.LAYOUT_ID @Suppress("DiffUtilEquals")
is TabInCollectionItem -> TabInCollectionViewHolder.LAYOUT_ID override fun areContentsTheSame(oldItem: AdapterItem, newItem: AdapterItem) = oldItem == newItem
OnboardingHeader -> OnboardingHeaderViewHolder.LAYOUT_ID
is OnboardingSectionHeader -> OnboardingSectionHeaderViewHolder.LAYOUT_ID
is OnboardingFirefoxAccount -> OnboardingFirefoxAccountViewHolder.LAYOUT_ID
OnboardingThemePicker -> OnboardingThemePickerViewHolder.LAYOUT_ID
OnboardingTrackingProtection -> OnboardingTrackingProtectionViewHolder.LAYOUT_ID
OnboardingPrivateBrowsing -> OnboardingPrivateBrowsingViewHolder.LAYOUT_ID
OnboardingPrivacyNotice -> OnboardingPrivacyNoticeViewHolder.LAYOUT_ID
OnboardingFinish -> OnboardingFinishViewHolder.LAYOUT_ID
}
} }
class SessionControlAdapter( class SessionControlAdapter(
private val actionEmitter: Observer<SessionControlAction> private val actionEmitter: Observer<SessionControlAction>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
private var items: List<AdapterItem> = listOf()
private lateinit var job: Job private lateinit var job: Job
private lateinit var expandedCollections: Set<Long>
fun reloadData(items: List<AdapterItem>, expandedCollections: Set<Long>) {
this.items = items
this.expandedCollections = expandedCollections
notifyDataSetChanged()
}
// This method triggers the ComplexMethod lint error when in fact it's quite simple. // This method triggers the ComplexMethod lint error when in fact it's quite simple.
@SuppressWarnings("ComplexMethod") @SuppressWarnings("ComplexMethod")
@ -98,12 +102,11 @@ class SessionControlAdapter(
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) { return when (viewType) {
TabHeaderViewHolder.LAYOUT_ID -> TabHeaderViewHolder(view, actionEmitter) TabHeaderViewHolder.LAYOUT_ID -> TabHeaderViewHolder(view, actionEmitter)
NoTabMessageViewHolder.LAYOUT_ID -> NoTabMessageViewHolder(view)
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, actionEmitter, job) TabViewHolder.LAYOUT_ID -> TabViewHolder(view, actionEmitter, job)
SaveTabGroupViewHolder.LAYOUT_ID -> SaveTabGroupViewHolder(view, actionEmitter) SaveTabGroupViewHolder.LAYOUT_ID -> SaveTabGroupViewHolder(view, actionEmitter)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, actionEmitter) PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, actionEmitter)
NoContentMessageViewHolder.LAYOUT_ID -> NoContentMessageViewHolder(view)
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view) CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
NoCollectionMessageViewHolder.LAYOUT_ID -> NoCollectionMessageViewHolder(view)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter, job) CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter, job)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, actionEmitter, job) TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, actionEmitter, job)
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view) OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
@ -128,32 +131,35 @@ class SessionControlAdapter(
job.cancel() job.cancel()
} }
override fun getItemViewType(position: Int) = items[position].viewType override fun getItemViewType(position: Int) = getItem(position).viewType
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position)
when (holder) { when (holder) {
is TabHeaderViewHolder -> { is TabHeaderViewHolder -> {
val tabHeader = items[position] as AdapterItem.TabHeader val tabHeader = item as AdapterItem.TabHeader
holder.bind(tabHeader.isPrivate, tabHeader.hasTabs) holder.bind(tabHeader.isPrivate, tabHeader.hasTabs)
} }
is TabViewHolder -> holder.bindSession( is TabViewHolder -> holder.bindSession(
(items[position] as AdapterItem.TabItem).tab (item as AdapterItem.TabItem).tab
) )
is NoContentMessageViewHolder -> {
val (icon, header, description) = item as AdapterItem.NoContentMessage
holder.bind(icon, header, description)
}
is CollectionViewHolder -> { is CollectionViewHolder -> {
val collection = (items[position] as AdapterItem.CollectionItem).collection val (collection, expanded) = item as AdapterItem.CollectionItem
holder.bindSession(collection, expandedCollections.contains(collection.id)) holder.bindSession(collection, expanded)
} }
is TabInCollectionViewHolder -> { is TabInCollectionViewHolder -> {
val item = items[position] as AdapterItem.TabInCollectionItem val (collection, tab, isLastTab) = item as AdapterItem.TabInCollectionItem
holder.bindSession(item.collection, item.tab, item.isLastTab) holder.bindSession(collection, tab, isLastTab)
} }
is OnboardingSectionHeaderViewHolder -> holder.bind( is OnboardingSectionHeaderViewHolder -> holder.bind(
(items[position] as AdapterItem.OnboardingSectionHeader).labelBuilder (item as AdapterItem.OnboardingSectionHeader).labelBuilder
) )
is OnboardingFirefoxAccountViewHolder -> holder.bind( is OnboardingFirefoxAccountViewHolder -> holder.bind(
(items[position] as AdapterItem.OnboardingFirefoxAccount).state == OnboardingState.AutoSignedIn (item as AdapterItem.OnboardingFirefoxAccount).state == OnboardingState.AutoSignedIn
) )
} }
} }

View File

@ -6,6 +6,7 @@ package org.mozilla.fenix.home.sessioncontrol
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable import io.reactivex.Observable
@ -13,7 +14,18 @@ import io.reactivex.Observer
import io.reactivex.functions.Consumer import io.reactivex.functions.Consumer
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.UIView import org.mozilla.fenix.mvi.UIView
import androidx.recyclerview.widget.ItemTouchHelper
val noTabMessage = AdapterItem.NoContentMessage(
R.drawable.ic_tabs,
R.string.no_open_tabs_header,
R.string.no_open_tabs_description
)
val noCollectionMessage = AdapterItem.NoContentMessage(
R.drawable.ic_tab_collection,
R.string.no_collections_header,
R.string.no_collections_description
)
private fun normalModeAdapterItems( private fun normalModeAdapterItems(
tabs: List<Tab>, tabs: List<Tab>,
@ -27,21 +39,23 @@ private fun normalModeAdapterItems(
items.addAll(tabs.reversed().map(AdapterItem::TabItem)) items.addAll(tabs.reversed().map(AdapterItem::TabItem))
items.add(AdapterItem.SaveTabGroup) items.add(AdapterItem.SaveTabGroup)
} else { } else {
items.add(AdapterItem.NoTabMessage) items.add(noTabMessage)
} }
items.add(AdapterItem.CollectionHeader) items.add(AdapterItem.CollectionHeader)
if (collections.isNotEmpty()) { if (collections.isNotEmpty()) {
// If the collection is expanded, we want to add all of its tabs beneath it in the adapter // If the collection is expanded, we want to add all of its tabs beneath it in the adapter
collections.map(AdapterItem::CollectionItem).forEach { collections.map {
AdapterItem.CollectionItem(it, expandedCollections.contains(it.id))
}.forEach {
items.add(it) items.add(it)
if (it.collection.isExpanded(expandedCollections)) { if (it.expanded) {
items.addAll(collectionTabItems(it.collection)) items.addAll(collectionTabItems(it.collection))
} }
} }
} else { } else {
items.add(AdapterItem.NoCollectionMessage) items.add(noCollectionMessage)
} }
return items return items
@ -104,10 +118,6 @@ private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapI
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex) AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
} }
private fun TabCollection.isExpanded(expandedCollections: Set<Long>): Boolean {
return expandedCollections.contains(this.id)
}
class SessionControlUIView( class SessionControlUIView(
container: ViewGroup, container: ViewGroup,
actionEmitter: Observer<SessionControlAction>, actionEmitter: Observer<SessionControlAction>,
@ -129,6 +139,7 @@ class SessionControlUIView(
view.apply { view.apply {
adapter = sessionControlAdapter adapter = sessionControlAdapter
layoutManager = LinearLayoutManager(container.context) layoutManager = LinearLayoutManager(container.context)
itemAnimator = null // TODO #2785: Remove this line
val itemTouchHelper = val itemTouchHelper =
ItemTouchHelper( ItemTouchHelper(
SwipeToDeleteCallback( SwipeToDeleteCallback(
@ -140,7 +151,7 @@ class SessionControlUIView(
} }
override fun updateView() = Consumer<SessionControlState> { override fun updateView() = Consumer<SessionControlState> {
sessionControlAdapter.reloadData(it.toAdapterList(), it.expandedCollections) sessionControlAdapter.submitList(it.toAdapterList())
actionEmitter.onNext(SessionControlAction.ReloadData) actionEmitter.onNext(SessionControlAction.ReloadData)
} }
} }

View File

@ -84,11 +84,7 @@ class CollectionViewHolder(
private fun updateCollectionUI() { private fun updateCollectionUI() {
view.collection_title.text = collection.title view.collection_title.text = collection.title
var hostNameList = listOf<String>() val hostNameList = collection.tabs.map { it.url.urlToTrimmedHost().capitalize() }
collection.tabs.forEach {
hostNameList += it.url.urlToTrimmedHost().capitalize()
}
var tabsDisplayed = 0 var tabsDisplayed = 0
val tabTitlesList = hostNameList.joinToString(", ") { val tabTitlesList = hostNameList.joinToString(", ") {

View File

@ -1,17 +0,0 @@
/* 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
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
class NoCollectionMessageViewHolder(
view: View
) : RecyclerView.ViewHolder(view) {
companion object {
const val LAYOUT_ID = R.layout.no_collection_message
}
}

View File

@ -0,0 +1,31 @@
/* 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
import android.view.View
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.no_content_message.view.*
import org.mozilla.fenix.R
class NoContentMessageViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind(
@DrawableRes icon: Int,
@StringRes header: Int,
@StringRes description: Int
) {
with(view.context) {
view.no_content_header.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, icon, 0)
view.no_content_header.text = getString(header)
view.no_content_description.text = getString(description)
}
}
companion object {
const val LAYOUT_ID = R.layout.no_content_message
}
}

View File

@ -1,17 +0,0 @@
/* 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
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
class NoTabMessageViewHolder(
view: View
) : RecyclerView.ViewHolder(view) {
companion object {
const val LAYOUT_ID = R.layout.no_tab_message
}
}

View File

@ -4,7 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/no_tabs_wrapper" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/no_content_wrapper"
android:background="@drawable/empty_session_control_background" android:background="@drawable/empty_session_control_background"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:padding="16dp" android:padding="16dp"
@ -13,23 +13,23 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/no_collection_header" android:id="@+id/no_content_header"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_tab_collection"
android:drawableTint="?primaryText" android:drawableTint="?primaryText"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:text="@string/no_collections_header" tools:text="@tools:sample/lorem"
android:textAppearance="@style/HeaderTextStyle" android:textAppearance="@style/HeaderTextStyle"
android:textSize="16sp" /> android:textSize="16sp" />
<TextView <TextView
android:id="@+id/no_collection_description" android:id="@+id/no_content_description"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:text="@string/no_collections_description" tools:text="@tools:sample/lorem"
android:textColor="?primaryText" android:textColor="?primaryText"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="normal" /> android:textStyle="normal" />
</LinearLayout> </LinearLayout>

View File

@ -1,35 +0,0 @@
<?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/. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/no_tabs_wrapper"
android:background="@drawable/empty_session_control_background"
android:layout_marginBottom="12dp"
android:padding="16dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/no_tab_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_tabs"
android:drawablePadding="8dp"
android:text="@string/no_open_tabs_header"
android:textAppearance="@style/HeaderTextStyle"
android:textSize="16sp" />
<TextView
android:id="@+id/no_tab_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/no_open_tabs_description"
android:textColor="?primaryText"
android:textSize="14sp"
android:textStyle="normal" />
</LinearLayout>

View File

@ -4,13 +4,13 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<FrameLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/onboarding_header" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/onboarding_header"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp"> android:layout_marginBottom="16dp">
<TextView <TextView
android:id="@+id/section_header_text" android:id="@+id/section_header_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/HeaderTextStyle" /> android:textAppearance="@style/HeaderTextStyle" tools:text="@tools:sample/lorem"/>
</FrameLayout> </FrameLayout>