Updated removing of "empty list" messages

Before this commit, the loader removed messages _after_ it updated the
 list. Coincidentally the list updating mechanism is asynchronous to
 some extent and so slower devices might've had the message removed
 after changes have been dispatched which confused the recyclerview and
 caused the crash.
Now, the loader is stripped of the responsibility update the list
 holding helper messages. The responsibility is for the user itself to
 notify listeners and then clear the helper list. This should hopefully
 delay the removal to the point where choreographer had enough time to
 traverse through the hierarchy.

Stupid recycler view / layout managers. Literally unnecessary crash.
This commit is contained in:
Viktor De Pasquale 2020-01-24 17:52:31 +01:00 committed by topjohnwu
parent 0dc9f5c324
commit 851ee81486
2 changed files with 82 additions and 9 deletions

View File

@ -1,8 +1,71 @@
package com.topjohnwu.magisk.extensions package com.topjohnwu.magisk.extensions
import androidx.databinding.ObservableList
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
fun KObservableField<Boolean>.toggle() { fun KObservableField<Boolean>.toggle() {
value = !value value = !value
} }
fun <T> ObservableList<T>.addOnListChangedCallback(
onChanged: ((sender: ObservableList<T>) -> Unit)? = null,
onItemRangeRemoved: ((sender: ObservableList<T>, positionStart: Int, itemCount: Int) -> Unit)? = null,
onItemRangeMoved: ((sender: ObservableList<T>, fromPosition: Int, toPosition: Int, itemCount: Int) -> Unit)? = null,
onItemRangeInserted: ((sender: ObservableList<T>, positionStart: Int, itemCount: Int) -> Unit)? = null,
onItemRangeChanged: ((sender: ObservableList<T>, positionStart: Int, itemCount: Int) -> Unit)? = null
) = addOnListChangedCallback(object : ObservableList.OnListChangedCallback<ObservableList<T>>() {
override fun onChanged(sender: ObservableList<T>?) {
onChanged?.invoke(sender ?: return)
}
override fun onItemRangeRemoved(
sender: ObservableList<T>?,
positionStart: Int,
itemCount: Int
) {
onItemRangeRemoved?.invoke(
sender ?: return,
positionStart,
itemCount
)
}
override fun onItemRangeMoved(
sender: ObservableList<T>?,
fromPosition: Int,
toPosition: Int,
itemCount: Int
) {
onItemRangeMoved?.invoke(
sender ?: return,
fromPosition,
toPosition,
itemCount
)
}
override fun onItemRangeInserted(
sender: ObservableList<T>?,
positionStart: Int,
itemCount: Int
) {
onItemRangeInserted?.invoke(
sender ?: return,
positionStart,
itemCount
)
}
override fun onItemRangeChanged(
sender: ObservableList<T>?,
positionStart: Int,
itemCount: Int
) {
onItemRangeChanged?.invoke(
sender ?: return,
positionStart,
itemCount
)
}
})

View File

@ -13,6 +13,7 @@ import com.topjohnwu.magisk.core.tasks.RepoUpdater
import com.topjohnwu.magisk.data.database.RepoByNameDao import com.topjohnwu.magisk.data.database.RepoByNameDao
import com.topjohnwu.magisk.data.database.RepoByUpdatedDao import com.topjohnwu.magisk.data.database.RepoByUpdatedDao
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.addOnListChangedCallback
import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.extensions.reboot
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
@ -156,6 +157,21 @@ class ModuleViewModel(
} }
update(subject.module, progress.times(100).roundToInt()) update(subject.module, progress.times(100).roundToInt())
} }
itemsInstalled.addOnListChangedCallback(
onItemRangeInserted = { sender, _, count ->
if (count > 0 || sender.size > 0) {
itemsInstalledHelpers.clear()
}
}
)
itemsUpdatable.addOnListChangedCallback(
onItemRangeInserted = { sender, _, count ->
if (count > 0 || sender.size > 0) {
itemsUpdatableHelpers.clear()
}
}
)
} }
// --- // ---
@ -175,19 +191,13 @@ class ModuleViewModel(
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.map { .map {
itemsInstalled.update(it.first, it.second) itemsInstalled.update(it.first, it.second)
if (itemsInstalled.isNotEmpty())
itemsInstalledHelpers.remove(itemNoneInstalled)
it.first it.first
} }
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
.map { loadUpdates(it) } .map { loadUpdates(it) }
.map { it to itemsUpdatable.calculateDiff(it) } .map { it to itemsUpdatable.calculateDiff(it) }
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnSuccess { .doOnSuccess { itemsUpdatable.update(it.first, it.second) }
itemsUpdatable.update(it.first, it.second)
if (itemsUpdatable.isNotEmpty())
itemsUpdatableHelpers.remove(itemNoneUpdatable)
}
.ignoreElement()!! .ignoreElement()!!
@Synchronized @Synchronized
@ -261,7 +271,7 @@ class ModuleViewModel(
@WorkerThread @WorkerThread
private fun List<ModuleItem>.loadDetail() = onEach { module -> private fun List<ModuleItem>.loadDetail() = onEach { module ->
Single.fromCallable { dao.getRepoById(module.item.id)!! } Single.fromCallable { dao.getRepoById(module.item.id)!! }
.subscribeK { module.repo = it } .subscribeK(onError = {}) { module.repo = it }
.add() .add()
} }