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

View File

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

View File

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

View File

@ -19,14 +19,14 @@ import org.mozilla.fenix.home.sessioncontrol.TabAction
import org.mozilla.fenix.home.sessioncontrol.onNext
class TabHeaderViewHolder(
view: View,
private val view: View,
private val actionEmitter: Observer<SessionControlAction>
) : RecyclerView.ViewHolder(view) {
private var isPrivate = false
private var tabsMenu: TabHeaderMenu
init {
tabsMenu = TabHeaderMenu(view.context) {
tabsMenu = TabHeaderMenu(view.context, isPrivate) {
when (it) {
is TabHeaderMenu.Item.Share -> actionEmitter.onNext(TabAction.ShareTabs)
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 {
setOnClickListener {
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(
private val context: Context,
var isPrivate: Boolean,
private val onItemTapped: (Item) -> Unit = {}
) {
sealed class Item {
@ -81,20 +89,13 @@ class TabHeaderViewHolder(
context.getString(R.string.tabs_menu_share_tabs)
) {
onItemTapped.invoke(Item.Share)
}
).let {
val list = it.toMutableList()
if (BuildConfig.COLLECTIONS_ENABLED) {
list.add(
SimpleBrowserMenuItem(
context.getString(R.string.tabs_menu_save_to_collection)
) {
onItemTapped.invoke(Item.SaveToCollection)
}
)
}
list
}
},
SimpleBrowserMenuItem(
context.getString(R.string.tabs_menu_save_to_collection)
) {
onItemTapped.invoke(Item.SaveToCollection)
}.apply { visible = { !isPrivate && BuildConfig.COLLECTIONS_ENABLED } }
)
}
}

View File

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

View File

@ -21,27 +21,30 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/add_tab_button"
android:layout_width="20dp"
android:layout_height="20dp"
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"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
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_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>