From ff08d0dbb067e6017dce6291d8e7427445cdaaab Mon Sep 17 00:00:00 2001 From: Jeff Boek Date: Mon, 22 Apr 2019 22:07:15 -0700 Subject: [PATCH] For #1843 - Passes selected tabs through the MVI loop --- .../CollectionCreationComponent.kt | 14 ++++- .../CollectionCreationTabListAdapter.kt | 60 +++++++++++++++---- .../collections/CollectionCreationUIView.kt | 2 +- .../collections/CreateCollectionFragment.kt | 9 +-- .../viewholders/HistoryListItemViewHolder.kt | 2 +- .../res/layout/collection_tab_list_row.xml | 11 ++++ app/src/main/res/values/styles.xml | 5 ++ 7 files changed, 85 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationComponent.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationComponent.kt index 97ef431fc..67aba3661 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationComponent.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationComponent.kt @@ -19,14 +19,18 @@ data class Tab( val title: String ) -data class CollectionCreationState(val tabs: List = listOf()) : ViewState +data class CollectionCreationState(val tabs: List = listOf(), val selectedTabs: List = listOf()) : ViewState sealed class CollectionCreationChange : Change { data class TabListChange(val tabs: List) : CollectionCreationChange() + data class TabAdded(val tab: Tab) : CollectionCreationChange() + data class TabRemoved(val tab: Tab) : CollectionCreationChange() } sealed class CollectionCreationAction : Action { object Close : CollectionCreationAction() + data class AddTabToSelection(val tab: Tab) : CollectionCreationAction() + data class RemoveTabFromSelection(val tab: Tab) : CollectionCreationAction() } class CollectionCreationComponent( @@ -40,6 +44,14 @@ class CollectionCreationComponent( override val reducer: Reducer = { state, change -> when (change) { is CollectionCreationChange.TabListChange -> state.copy(tabs = change.tabs) + is CollectionCreationChange.TabAdded -> { + val selectedTabs = listOf(change.tab) + state.selectedTabs + state.copy(selectedTabs = selectedTabs) + } + is CollectionCreationChange.TabRemoved -> { + val selectedTabs = state.selectedTabs.filter { it.sessionId != change.tab.sessionId } + state.copy(selectedTabs = selectedTabs) + } } } diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt index b64416f53..3b65da4d2 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationTabListAdapter.kt @@ -3,10 +3,11 @@ package org.mozilla.fenix.collections import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.CompoundButton +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.reactivex.Observer import kotlinx.android.synthetic.main.collection_tab_list_row.view.* -import kotlinx.android.synthetic.main.tab_list_row.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -21,7 +22,8 @@ class CollectionCreationTabListAdapter( ) : RecyclerView.Adapter() { - private var data: List = listOf() + private var tabs: List = listOf() + private var selectedTabs: List = listOf() private lateinit var job: Job override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabViewHolder { @@ -31,10 +33,12 @@ class CollectionCreationTabListAdapter( } override fun onBindViewHolder(holder: TabViewHolder, position: Int) { - holder.bind(data[position]) + val tab = tabs[position] + val isSelected = selectedTabs.contains(tab) + holder.bind(tab, isSelected) } - override fun getItemCount(): Int = data.size + override fun getItemCount(): Int = tabs.size override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) @@ -46,12 +50,27 @@ class CollectionCreationTabListAdapter( job.cancel() } - fun updateData(tabs: List) { - data = tabs - notifyDataSetChanged() + fun updateData(tabs: List, selectedTabs: List) { + val diffUtil = DiffUtil.calculateDiff(TabDiffUtil(this.tabs, tabs)) + + this.tabs = tabs + this.selectedTabs = selectedTabs + + diffUtil.dispatchUpdatesTo(this) } } +private class TabDiffUtil(val old: List, val new: List) : DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + old[oldItemPosition].sessionId == new[newItemPosition].sessionId + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + old[oldItemPosition].url == new[newItemPosition].url + + override fun getOldListSize(): Int = old.size + override fun getNewListSize(): Int = new.size +} + class TabViewHolder( val view: View, actionEmitter: Observer, @@ -62,15 +81,34 @@ class TabViewHolder( override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job - var tab: Tab? = null + private var tab: Tab? = null + private val checkbox = view.tab_selected_checkbox!! + private val checkboxListener = CompoundButton.OnCheckedChangeListener { _, isChecked -> + tab?.apply { + val action = if (isChecked) CollectionCreationAction.AddTabToSelection(this) + else CollectionCreationAction.RemoveTabFromSelection(this) - init { } + actionEmitter.onNext(action) + } + } - fun bind(tab: Tab) { + init { + view.collection_item_tab.setOnClickListener { + checkbox.isChecked = !checkbox.isChecked + } + } + + fun bind(tab: Tab, isSelected: Boolean) { this.tab = tab view.hostname.text = tab.hostname view.tab_title.text = tab.title + checkbox.setOnCheckedChangeListener(null) + if (checkbox.isChecked != isSelected) { + checkbox.isChecked = isSelected + } + checkbox.setOnCheckedChangeListener(checkboxListener) + launch(Dispatchers.IO) { val bitmap = view.favicon_image.context.components.utils.icons .loadIcon(IconRequest(tab.url)).await().bitmap @@ -83,4 +121,4 @@ class TabViewHolder( companion object { const val LAYOUT_ID = R.layout.collection_tab_list_row } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationUIView.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationUIView.kt index cfb09f6f5..4d8e72e72 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationUIView.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationUIView.kt @@ -46,6 +46,6 @@ class CollectionCreationUIView( } override fun updateView() = Consumer { - collectionCreationTabListAdapter.updateData(it.tabs) + collectionCreationTabListAdapter.updateData(it.tabs, it.selectedTabs) } } \ No newline at end of file diff --git a/app/src/main/java/org/mozilla/fenix/collections/CreateCollectionFragment.kt b/app/src/main/java/org/mozilla/fenix/collections/CreateCollectionFragment.kt index 646a245f6..63748fee2 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CreateCollectionFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CreateCollectionFragment.kt @@ -4,10 +4,7 @@ package org.mozilla.fenix.collections 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/. */ -import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -52,7 +49,11 @@ class CreateCollectionFragment : DialogFragment() { getAutoDisposeObservable().subscribe { when (it) { - is CollectionCreationAction.Close -> dismissAllowingStateLoss() + is CollectionCreationAction.Close -> dismiss() + is CollectionCreationAction.AddTabToSelection -> getManagedEmitter() + .onNext(CollectionCreationChange.TabAdded(it.tab)) + is CollectionCreationAction.RemoveTabFromSelection -> getManagedEmitter() + .onNext(CollectionCreationChange.TabRemoved(it.tab)) } } } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt index ab62e9ea8..7ae804062 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt @@ -106,7 +106,7 @@ class HistoryListItemViewHolder( // This prevent us from cutting off the animation val shouldCheck = mode.selectedItems.contains(item) if (checkbox.isChecked != shouldCheck) { - checkbox.isChecked = mode.selectedItems.contains(item) + checkbox.isChecked = shouldCheck } checkbox.setOnCheckedChangeListener(checkListener) } diff --git a/app/src/main/res/layout/collection_tab_list_row.xml b/app/src/main/res/layout/collection_tab_list_row.xml index 1d624f16a..ccb03ccca 100644 --- a/app/src/main/res/layout/collection_tab_list_row.xml +++ b/app/src/main/res/layout/collection_tab_list_row.xml @@ -64,5 +64,16 @@ app:layout_constraintEnd_toEndOf="@id/hostname" app:layout_constraintTop_toBottomOf="@id/hostname" app:layout_constraintBottom_toBottomOf="parent"/> + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 40bedf406..40b6fa7cb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -110,6 +110,11 @@