1
0
Fork 0

Small refactor before we add onboarding cards (#2541)

* For #2390 - Cleans up the toAdapterList method before we add onboarding

* For #2514 - Hide tabs menu when no tabs are open
master
Jeff Boek 2019-05-15 17:06:49 -07:00 committed by GitHub
parent c34946b88f
commit b3a3c94169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 94 deletions

View File

@ -22,7 +22,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHold
import java.lang.IllegalStateException import java.lang.IllegalStateException
sealed class AdapterItem { sealed class AdapterItem {
object TabHeader : AdapterItem() data class TabHeader(val isPrivate: Boolean, val hasTabs: Boolean) : AdapterItem()
object NoTabMessage : AdapterItem() object NoTabMessage : AdapterItem()
data class TabItem(val tab: Tab) : AdapterItem() data class TabItem(val tab: Tab) : AdapterItem()
object PrivateBrowsingDescription : AdapterItem() object PrivateBrowsingDescription : AdapterItem()
@ -35,7 +35,7 @@ sealed class AdapterItem {
val viewType: Int val viewType: Int
get() = when (this) { get() = when (this) {
TabHeader -> TabHeaderViewHolder.LAYOUT_ID is TabHeader -> TabHeaderViewHolder.LAYOUT_ID
NoTabMessage -> NoTabMessageViewHolder.LAYOUT_ID NoTabMessage -> NoTabMessageViewHolder.LAYOUT_ID
is TabItem -> TabViewHolder.LAYOUT_ID is TabItem -> TabViewHolder.LAYOUT_ID
SaveTabGroup -> SaveTabGroupViewHolder.LAYOUT_ID SaveTabGroup -> SaveTabGroupViewHolder.LAYOUT_ID
@ -52,7 +52,7 @@ class SessionControlAdapter(
private val actionEmitter: Observer<SessionControlAction> private val actionEmitter: Observer<SessionControlAction>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var items: List<AdapterItem> = listOf() private var items: List<AdapterItem> = listOf()
private lateinit var job: Job private lateinit var job: Job
fun reloadData(items: List<AdapterItem>) { fun reloadData(items: List<AdapterItem>) {
@ -69,15 +69,10 @@ class SessionControlAdapter(
NoTabMessageViewHolder.LAYOUT_ID -> NoTabMessageViewHolder(view) 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( PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, actionEmitter)
view,
actionEmitter
)
DeleteTabsViewHolder.LAYOUT_ID -> DeleteTabsViewHolder(view, actionEmitter) DeleteTabsViewHolder.LAYOUT_ID -> DeleteTabsViewHolder(view, actionEmitter)
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view) CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
NoCollectionMessageViewHolder.LAYOUT_ID -> NoCollectionMessageViewHolder( NoCollectionMessageViewHolder.LAYOUT_ID -> NoCollectionMessageViewHolder(view)
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)
else -> throw IllegalStateException() else -> throw IllegalStateException()
@ -100,6 +95,10 @@ class SessionControlAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) { when (holder) {
is TabHeaderViewHolder -> {
val tabHeader = items[position] as AdapterItem.TabHeader
holder.bind(tabHeader.isPrivate, tabHeader.hasTabs)
}
is TabViewHolder -> holder.bindSession( is TabViewHolder -> holder.bindSession(
(items[position] as AdapterItem.TabItem).tab (items[position] as AdapterItem.TabItem).tab
) )
@ -107,7 +106,7 @@ class SessionControlAdapter(
(items[position] as AdapterItem.CollectionItem).collection (items[position] as AdapterItem.CollectionItem).collection
) )
is TabInCollectionViewHolder -> { is TabInCollectionViewHolder -> {
val item = (items[position] as AdapterItem.TabInCollectionItem) val item = items[position] as AdapterItem.TabInCollectionItem
holder.bindSession(item.collection, item.tab, item.isLastTab) holder.bindSession(item.collection, item.tab, item.isLastTab)
} }
} }

View File

@ -58,9 +58,16 @@ data class TabCollection(
var expanded: Boolean = false var expanded: Boolean = false
) : Parcelable ) : Parcelable
sealed class FirefoxAcocuntsState {
object SignedOut : FirefoxAcocuntsState()
data class AutoSignedIn(val email: String) : FirefoxAcocuntsState()
data class SignedIn(val email: String) : FirefoxAcocuntsState()
}
sealed class Mode { sealed class Mode {
object Normal : Mode() object Normal : Mode()
object Private : Mode() object Private : Mode()
data class Onboarding(val firefoxAccountsState: FirefoxAcocuntsState) : Mode()
} }
data class SessionControlState( data class SessionControlState(
@ -110,12 +117,9 @@ sealed class SessionControlChange : Change {
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange() data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
} }
class SessionControlViewModel(initialState: SessionControlState) : class SessionControlViewModel(
UIComponentViewModelBase<SessionControlState, SessionControlChange>( initialState: SessionControlState
initialState, ) : UIComponentViewModelBase<SessionControlState, SessionControlChange>(initialState, reducer) {
reducer
) {
companion object { companion object {
val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change -> val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
when (change) { when (change) {

View File

@ -16,60 +16,60 @@ import org.mozilla.fenix.mvi.UIView
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.BuildConfig
// Convert HomeState into a data structure HomeAdapter understands private fun normalModeAdapterItems(tabs: List<Tab>, collections: List<TabCollection>): List<AdapterItem> {
@SuppressWarnings("ComplexMethod", "NestedBlockDepth")
private fun SessionControlState.toAdapterList(): List<AdapterItem> {
val items = mutableListOf<AdapterItem>() val items = mutableListOf<AdapterItem>()
items.add(AdapterItem.TabHeader) items.add(AdapterItem.TabHeader(false, tabs.isNotEmpty()))
// Populate tabs
if (tabs.isNotEmpty()) { if (tabs.isNotEmpty()) {
tabs.reversed().map(AdapterItem::TabItem).forEach { items.add(it) } items.addAll(tabs.reversed().map(AdapterItem::TabItem))
if (mode == Mode.Private) { if (BuildConfig.COLLECTIONS_ENABLED) {
items.add(AdapterItem.DeleteTabs)
} else if (BuildConfig.COLLECTIONS_ENABLED) {
items.add(AdapterItem.SaveTabGroup) items.add(AdapterItem.SaveTabGroup)
} }
} else { } else {
val item = if (mode == Mode.Private) AdapterItem.PrivateBrowsingDescription items.add(AdapterItem.NoTabMessage)
else AdapterItem.NoTabMessage
items.add(item)
} }
// Populate collections items.add(AdapterItem.CollectionHeader)
if (mode == Mode.Normal) { if (collections.isNotEmpty()) {
items.add(AdapterItem.CollectionHeader)
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.reversed().map(AdapterItem::CollectionItem).forEach { collections.reversed().map(AdapterItem::CollectionItem).forEach {
if (it.collection.expanded) { items.add(it)
items.add(it) if (it.collection.expanded) {
addCollectionTabItems(it.collection, it.collection.tabs, items) items.addAll(collectionTabItems(it.collection))
} else {
items.add(it)
}
} }
} else {
items.add(AdapterItem.NoCollectionMessage)
} }
} else {
items.add(AdapterItem.NoCollectionMessage)
} }
return items return items
} }
private fun addCollectionTabItems( private fun privateModeAdapterItems(tabs: List<Tab>): List<AdapterItem> {
collection: TabCollection, val items = mutableListOf<AdapterItem>()
tabs: MutableList<Tab>, items.add(AdapterItem.TabHeader(true, tabs.isNotEmpty()))
itemList: MutableList<AdapterItem>
) { if (tabs.isNotEmpty()) {
for (tabIndex in 0 until tabs.size) { items.addAll(tabs.reversed().map(AdapterItem::TabItem))
itemList.add(AdapterItem.TabInCollectionItem items.add(AdapterItem.DeleteTabs)
(collection, collection.tabs[tabIndex], tabIndex == collection.tabs.size - 1)) } else {
items.add(AdapterItem.PrivateBrowsingDescription)
} }
return items
} }
private fun SessionControlState.toAdapterList(): List<AdapterItem> = when (mode) {
is Mode.Normal -> normalModeAdapterItems(tabs, collections)
is Mode.Private -> privateModeAdapterItems(tabs)
is Mode.Onboarding -> listOf()
}
private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapIndexed { index, tab ->
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
}
class SessionControlUIView( class SessionControlUIView(
container: ViewGroup, container: ViewGroup,
actionEmitter: Observer<SessionControlAction>, actionEmitter: Observer<SessionControlAction>,

View File

@ -19,14 +19,14 @@ import org.mozilla.fenix.home.sessioncontrol.TabAction
import org.mozilla.fenix.home.sessioncontrol.onNext import org.mozilla.fenix.home.sessioncontrol.onNext
class TabHeaderViewHolder( class TabHeaderViewHolder(
view: View, private val view: View,
private val actionEmitter: Observer<SessionControlAction> private val actionEmitter: Observer<SessionControlAction>
) : RecyclerView.ViewHolder(view) { ) : RecyclerView.ViewHolder(view) {
private var isPrivate = false private var isPrivate = false
private var tabsMenu: TabHeaderMenu private var tabsMenu: TabHeaderMenu
init { init {
tabsMenu = TabHeaderMenu(view.context) { tabsMenu = TabHeaderMenu(view.context, isPrivate) {
when (it) { when (it) {
is TabHeaderMenu.Item.Share -> actionEmitter.onNext(TabAction.ShareTabs) is TabHeaderMenu.Item.Share -> actionEmitter.onNext(TabAction.ShareTabs)
is TabHeaderMenu.Item.CloseAll -> actionEmitter.onNext(TabAction.CloseAll(isPrivate)) is TabHeaderMenu.Item.CloseAll -> actionEmitter.onNext(TabAction.CloseAll(isPrivate))
@ -37,11 +37,8 @@ class TabHeaderViewHolder(
) )
} }
} }
view.apply {
val headerTextResourceId =
if (isPrivate) R.string.tabs_header_private_title else R.string.tab_header_label
header_text.text = context.getString(headerTextResourceId)
view.apply {
add_tab_button.run { add_tab_button.run {
setOnClickListener { setOnClickListener {
actionEmitter.onNext(TabAction.Add) actionEmitter.onNext(TabAction.Add)
@ -58,8 +55,19 @@ class TabHeaderViewHolder(
} }
} }
fun bind(isPrivate: Boolean, hasTabs: Boolean) {
this.isPrivate = isPrivate
tabsMenu.isPrivate = isPrivate
val headerTextResourceId =
if (isPrivate) R.string.tabs_header_private_title else R.string.tab_header_label
view.header_text.text = view.context.getString(headerTextResourceId)
view.tabs_overflow_button.visibility = if (hasTabs) View.VISIBLE else View.GONE
}
class TabHeaderMenu( class TabHeaderMenu(
private val context: Context, private val context: Context,
var isPrivate: Boolean,
private val onItemTapped: (Item) -> Unit = {} private val onItemTapped: (Item) -> Unit = {}
) { ) {
sealed class Item { sealed class Item {
@ -81,20 +89,13 @@ class TabHeaderViewHolder(
context.getString(R.string.tabs_menu_share_tabs) context.getString(R.string.tabs_menu_share_tabs)
) { ) {
onItemTapped.invoke(Item.Share) onItemTapped.invoke(Item.Share)
} },
).let { SimpleBrowserMenuItem(
val list = it.toMutableList() context.getString(R.string.tabs_menu_save_to_collection)
if (BuildConfig.COLLECTIONS_ENABLED) { ) {
list.add( onItemTapped.invoke(Item.SaveToCollection)
SimpleBrowserMenuItem( }.apply { visible = { !isPrivate && BuildConfig.COLLECTIONS_ENABLED } }
context.getString(R.string.tabs_menu_save_to_collection) )
) {
onItemTapped.invoke(Item.SaveToCollection)
}
)
}
list
}
} }
} }

View File

@ -47,7 +47,6 @@ class TabInCollectionViewHolder(
var isLastTab = false var isLastTab = false
init { init {
collection_tab_icon.clipToOutline = true collection_tab_icon.clipToOutline = true
collection_tab_icon.outlineProvider = object : ViewOutlineProvider() { collection_tab_icon.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) { override fun getOutline(view: View?, outline: Outline?) {

View File

@ -21,27 +21,30 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageButton <LinearLayout
android:id="@+id/add_tab_button" android:layout_width="wrap_content"
android:layout_width="20dp" android:layout_height="wrap_content"
android:layout_height="20dp" android:orientation="horizontal"
android:layout_marginEnd="30dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/add_tab"
android:src="@drawable/ic_new"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tabs_overflow_button"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/tabs_overflow_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="4.5dp" android:layout_marginEnd="4.5dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/open_tabs_menu"
android:src="@drawable/ic_menu"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintEnd_toEndOf="parent">
<ImageButton
android:id="@+id/add_tab_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/add_tab"
android:src="@drawable/ic_new"/>
<ImageButton
android:id="@+id/tabs_overflow_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/open_tabs_menu"
android:src="@drawable/ic_menu" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>