From 754b6c7e8c3ff5492e87e9f2a27506f216e83099 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Sat, 1 Aug 2020 08:35:04 -0500 Subject: [PATCH] Ensure DragAndDropDiffer works sequentially --- .../astrid/activity/TaskListFragment.kt | 22 +++++++++---------- .../astrid/adapter/NavigationDrawerAdapter.kt | 10 +++++++++ .../org/tasks/activities/DragAndDropDiffer.kt | 21 ++++++++++++------ .../tasklist/DragAndDropRecyclerAdapter.kt | 12 ++++++---- .../tasklist/PagedListRecyclerAdapter.kt | 12 +++++----- .../tasks/tasklist/TaskListRecyclerAdapter.kt | 2 +- 6 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt index dae9973c6..146c4c31b 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt @@ -201,15 +201,13 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL (recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false recyclerView.layoutManager = LinearLayoutManager(context) listViewModel.observe(this) { - lifecycleScope.launch { - submitList(it) - if (it.isEmpty()) { - swipeRefreshLayout.visibility = View.GONE - emptyRefreshLayout.visibility = View.VISIBLE - } else { - swipeRefreshLayout.visibility = View.VISIBLE - emptyRefreshLayout.visibility = View.GONE - } + submitList(it) + if (it.isEmpty()) { + swipeRefreshLayout.visibility = View.GONE + emptyRefreshLayout.visibility = View.VISIBLE + } else { + swipeRefreshLayout.visibility = View.VISIBLE + emptyRefreshLayout.visibility = View.GONE } } setupRefresh(swipeRefreshLayout) @@ -222,8 +220,8 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL return parent } - private suspend fun submitList(tasks: List) { - if (tasks is PagedList<*>) { + private fun submitList(tasks: List) { + if (tasks is PagedList) { if (recyclerAdapter !is PagedListRecyclerAdapter) { setAdapter( PagedListRecyclerAdapter( @@ -233,7 +231,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL } else if (recyclerAdapter !is DragAndDropRecyclerAdapter) { setAdapter( DragAndDropRecyclerAdapter( - lifecycleScope, taskAdapter, recyclerView, viewHolderFactory, this, tasks, preferences)) + taskAdapter, recyclerView, viewHolderFactory, this, tasks, preferences)) return } recyclerAdapter?.submitList(tasks) diff --git a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt index 9788041a1..3b9c0d082 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt @@ -16,6 +16,9 @@ import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.FilterListItem import io.reactivex.disposables.CompositeDisposable import io.reactivex.subjects.PublishSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.asCoroutineDispatcher import org.tasks.LocalBroadcastManager import org.tasks.activities.DragAndDropDiffer import org.tasks.billing.Inventory @@ -26,6 +29,7 @@ import org.tasks.locale.Locale import org.tasks.preferences.Preferences import org.tasks.themes.ColorProvider import java.util.* +import java.util.concurrent.Executors import javax.inject.Inject import kotlin.math.max @@ -47,6 +51,8 @@ class NavigationDrawerAdapter @Inject constructor( override val updates: Queue, DiffUtil.DiffResult?>> = LinkedList() override var items = initializeDiffer(ArrayList()) override var dragging = false + override val scope: CoroutineScope = + CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job()) fun setOnClick(onClick: (FilterListItem?) -> Unit) { this.onClick = onClick @@ -60,6 +66,10 @@ class NavigationDrawerAdapter @Inject constructor( selected = savedInstanceState.getParcelable(TOKEN_SELECTED) } + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.dispose() + } + override fun getItemId(position: Int) = position.toLong() override fun getItemCount() = items.size diff --git a/app/src/main/java/org/tasks/activities/DragAndDropDiffer.kt b/app/src/main/java/org/tasks/activities/DragAndDropDiffer.kt index 4462e503d..882be245f 100644 --- a/app/src/main/java/org/tasks/activities/DragAndDropDiffer.kt +++ b/app/src/main/java/org/tasks/activities/DragAndDropDiffer.kt @@ -7,8 +7,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import java.util.* interface DragAndDropDiffer : ListUpdateCallback { @@ -17,12 +18,13 @@ interface DragAndDropDiffer : ListUpdateCallback { val disposables: CompositeDisposable var items: R var dragging: Boolean + val scope: CoroutineScope - suspend fun submitList(list: List) { - val transform = withContext(Dispatchers.Default) { - transform(list) + fun submitList(list: List) { + scope.launch { + val transform = transform(list) + publishSubject.onNext(transform) } - publishSubject.onNext(transform) } fun calculateDiff(last: Pair, next: R): Pair { @@ -51,7 +53,7 @@ interface DragAndDropDiffer : ListUpdateCallback { fun initializeDiffer(list: List): R { val initial = transform(list) disposables.add(publishSubject - .observeOn(Schedulers.computation()) + .observeOn(Schedulers.single()) .scan(Pair(initial, null), { last: Pair, next: R -> calculateDiff(last, next) }) @@ -64,4 +66,9 @@ interface DragAndDropDiffer : ListUpdateCallback { fun transform(list: List): R fun diff(last: R, next: R): DiffUtil.DiffResult + + fun dispose() { + disposables.dispose() + scope.cancel() + } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt b/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt index 59fc15d56..30c074d5a 100644 --- a/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt +++ b/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt @@ -2,7 +2,6 @@ package org.tasks.tasklist import android.graphics.Canvas import android.view.ViewGroup -import androidx.lifecycle.LifecycleCoroutineScope import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.* @@ -13,16 +12,19 @@ import com.todoroo.astrid.adapter.TaskAdapter import com.todoroo.astrid.utility.Flags import io.reactivex.disposables.CompositeDisposable import io.reactivex.subjects.PublishSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.runBlocking import org.tasks.activities.DragAndDropDiffer import org.tasks.data.TaskContainer import org.tasks.preferences.Preferences import java.util.* +import java.util.concurrent.Executors import kotlin.math.max import kotlin.math.min class DragAndDropRecyclerAdapter( - private val scope: LifecycleCoroutineScope, private val adapter: TaskAdapter, private val recyclerView: RecyclerView, viewHolderFactory: ViewHolderFactory, @@ -43,6 +45,8 @@ class DragAndDropRecyclerAdapter( override val updates: Queue> = LinkedList() override var dragging = false override var items = initializeDiffer(tasks) + override val scope: CoroutineScope = + CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher() + Job()) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val viewType = getItemViewType(position) @@ -59,7 +63,7 @@ class DragAndDropRecyclerAdapter( override fun getItemViewType(position: Int) = if (items.isHeader(position)) 1 else 0 - override suspend fun submitList(list: List) { + override fun submitList(list: List) { super.submitList(list) } @@ -95,8 +99,8 @@ class DragAndDropRecyclerAdapter( } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.dispose() itemTouchHelper.attachToRecyclerView(null) - disposables.dispose() } override fun getTaskCount() = items.taskCount diff --git a/app/src/main/java/org/tasks/tasklist/PagedListRecyclerAdapter.kt b/app/src/main/java/org/tasks/tasklist/PagedListRecyclerAdapter.kt index 017f0d138..cd042b2cc 100644 --- a/app/src/main/java/org/tasks/tasklist/PagedListRecyclerAdapter.kt +++ b/app/src/main/java/org/tasks/tasklist/PagedListRecyclerAdapter.kt @@ -14,15 +14,17 @@ class PagedListRecyclerAdapter( private val recyclerView: RecyclerView, viewHolderFactory: ViewHolderFactory, taskList: TaskListFragment, - list: List, - preferences: Preferences) : TaskListRecyclerAdapter(adapter, viewHolderFactory, taskList, preferences) { + list: PagedList, + preferences: Preferences +) : TaskListRecyclerAdapter(adapter, viewHolderFactory, taskList, preferences) { private val differ: AsyncPagedListDiffer = AsyncPagedListDiffer(this, AsyncDifferConfig.Builder(ItemCallback()).build()) override fun getItem(position: Int) = differ.getItem(position) - override suspend fun submitList(list: List) = differ.submitList(list as PagedList) + override fun submitList(list: List) = + differ.submitList(list as PagedList) override fun onMoved(fromPosition: Int, toPosition: Int) { val recyclerViewState = recyclerView.layoutManager!!.onSaveInstanceState() @@ -37,8 +39,6 @@ class PagedListRecyclerAdapter( override fun getTaskCount() = itemCount init { - if (list is PagedList<*>) { - differ.submitList(list as PagedList?) - } + differ.submitList(list as PagedList?) } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.kt b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.kt index 903017235..ead2d64eb 100644 --- a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.kt +++ b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.kt @@ -44,7 +44,7 @@ abstract class TaskListRecyclerAdapter internal constructor( abstract fun dragAndDropEnabled(): Boolean - abstract suspend fun submitList(list: List) + abstract fun submitList(list: List) override fun onInserted(position: Int, count: Int) { notifyItemRangeInserted(position, count)