You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/app/src/main/java/org/tasks/activities/DragAndDropDiffer.kt

78 lines
2.3 KiB
Kotlin

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<T, R> : ListUpdateCallback {
val channel: Channel<List<T>>
val updates: Queue<Pair<R, DiffUtil.DiffResult?>>
var items: R
var dragging: Boolean
val scope: CoroutineScope
fun submitList(list: List<T>) {
channel.trySend(list)
}
fun calculateDiff(last: Pair<R, DiffUtil.DiffResult?>, next: R): Pair<R, DiffUtil.DiffResult?> {
AndroidUtilities.assertNotMainThread()
return Pair(next, diff(last.first!!, next))
}
fun applyDiff(update: Pair<R, DiffUtil.DiffResult?>) {
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<T>): R {
val initial = transform(list)
channel
.consumeAsFlow()
.map { transform(it) }
.scan(Pair(initial, null)) { last: Pair<R, DiffUtil.DiffResult?>, next: R ->
calculateDiff(last, next)
}
.drop(1)
.flowOn(Dispatchers.Default)
.onEach { applyDiff(it) }
.launchIn(CoroutineScope(Dispatchers.Main + Job()))
return initial
}
fun transform(list: List<T>): R
fun diff(last: R, next: R): DiffUtil.DiffResult
fun dispose() {
scope.cancel()
}
}