package org.tasks.activities import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback import com.todoroo.andlib.utility.AndroidUtilities import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.scan import java.util.Queue interface DragAndDropDiffer : ListUpdateCallback { val channel: Channel> val updates: Queue> var items: R var dragging: Boolean val scope: CoroutineScope fun submitList(list: List) { channel.trySend(list) } fun calculateDiff(last: Pair, next: R): Pair { AndroidUtilities.assertNotMainThread() return Pair(next, diff(last.first!!, next)) } fun applyDiff(update: Pair) { AndroidUtilities.assertMainThread() updates.add(update) if (!dragging) { drainQueue() } } fun drainQueue() { AndroidUtilities.assertMainThread() var update = updates.poll() while (update != null) { items = update.first update.second?.dispatchUpdatesTo(this as ListUpdateCallback) update = updates.poll() } } @ExperimentalCoroutinesApi fun initializeDiffer(list: List): R { val initial = transform(list) channel .consumeAsFlow() .map { transform(it) } .scan(Pair(initial, null)) { last: Pair, next: R -> calculateDiff(last, next) } .drop(1) .flowOn(Dispatchers.Default) .onEach { applyDiff(it) } .launchIn(CoroutineScope(Dispatchers.Main + Job())) return initial } fun transform(list: List): R fun diff(last: R, next: R): DiffUtil.DiffResult fun dispose() { scope.cancel() } }