From dd62fe89f7820de02ddf2fa0c62784fbdd5064e7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 17 Jul 2020 00:13:03 -0700 Subject: [PATCH] Use CallbackList for collecting STDOUT in flash screen Fix #2988 --- .../java/com/topjohnwu/magisk/ktx/XJava.kt | 18 ++-- .../java/com/topjohnwu/magisk/ktx/XList.kt | 87 ------------------- .../magisk/ui/flash/FlashViewModel.kt | 16 ++-- 3 files changed, 21 insertions(+), 100 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/ktx/XList.kt diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt index 53adb4b5c..8c816b58e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ktx/XJava.kt @@ -1,6 +1,7 @@ package com.topjohnwu.magisk.ktx import android.os.Build +import androidx.collection.SparseArrayCompat import timber.log.Timber import java.io.File import java.io.InputStream @@ -11,7 +12,6 @@ import java.text.SimpleDateFormat import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream -import kotlin.NoSuchElementException fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { var entry: ZipEntry? = nextEntry @@ -36,13 +36,19 @@ inline fun withStreams( } } -inline fun List.firstMap(mapper: (T) -> R?): R { - for (item: T in this) { - return mapper(item) ?: continue - } - throw NoSuchElementException("Collection contains no element matching the predicate.") +fun MutableList.update(newList: List) { + clear() + addAll(newList) } +operator fun SparseArrayCompat.set(key: Int, value: E) { + put(key, value) +} + +fun MutableList.synchronized() = Collections.synchronizedList(this) +fun MutableSet.synchronized() = Collections.synchronizedSet(this) +fun MutableMap.synchronized() = Collections.synchronizedMap(this) + fun String.langTagToLocale(): Locale { if (Build.VERSION.SDK_INT >= 21) { return Locale.forLanguageTag(this) diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/XList.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/XList.kt deleted file mode 100644 index 93822f1bc..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ktx/XList.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.topjohnwu.magisk.ktx - -import androidx.collection.SparseArrayCompat -import androidx.databinding.ObservableList -import com.topjohnwu.magisk.utils.DiffObservableList -import kotlinx.coroutines.* - -fun MutableList.update(newList: List) { - clear() - addAll(newList) -} - -fun List.toShellCmd(): String { - val sb = StringBuilder() - for (s in this) { - if (s.contains(" ")) { - sb.append('"').append(s).append('"') - } else { - sb.append(s) - } - sb.append(' ') - } - sb.deleteCharAt(sb.length - 1) - return sb.toString() -} - -fun ObservableList.sendUpdatesTo( - target: DiffObservableList, - scope: CoroutineScope, - mapper: (List) -> List -) = addOnListChangedCallback(object : - ObservableList.OnListChangedCallback>() { - override fun onChanged(sender: ObservableList?) { - updateAsync(sender ?: return) - } - - override fun onItemRangeRemoved(sender: ObservableList?, p0: Int, p1: Int) { - updateAsync(sender ?: return) - } - - override fun onItemRangeMoved(sender: ObservableList?, p0: Int, p1: Int, p2: Int) { - updateAsync(sender ?: return) - } - - override fun onItemRangeInserted(sender: ObservableList?, p0: Int, p1: Int) { - updateAsync(sender ?: return) - } - - override fun onItemRangeChanged(sender: ObservableList?, p0: Int, p1: Int) { - updateAsync(sender ?: return) - } - - private var updater: Job? = null - - private fun updateAsync(sender: List) { - updater?.cancel() - updater = scope.launch { - val (list, diff) = withContext(Dispatchers.Default) { - val list = mapper(sender) - list to target.calculateDiff(list) - } - target.update(list, diff) - } - } -}) - -fun ObservableList.copyNewInputInto( - target: MutableList -) = addOnListChangedCallback(object : ObservableList.OnListChangedCallback>() { - override fun onChanged(p0: ObservableList?) = Unit - override fun onItemRangeRemoved(p0: ObservableList?, p1: Int, p2: Int) = Unit - override fun onItemRangeMoved(p0: ObservableList?, p1: Int, p2: Int, p3: Int) = Unit - override fun onItemRangeChanged(p0: ObservableList?, p1: Int, p2: Int) = Unit - override fun onItemRangeInserted( - sender: ObservableList?, - positionStart: Int, - itemCount: Int - ) { - val positionEnd = positionStart + itemCount - val addedValues = sender?.slice(positionStart until positionEnd).orEmpty() - target.addAll(addedValues) - } -}) - -operator fun SparseArrayCompat.set(key: Int, value: E) { - put(key, value) -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index 6069c758b..b6844d755 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -4,7 +4,6 @@ import android.content.res.Resources import android.net.Uri import android.view.MenuItem import androidx.databinding.Bindable -import androidx.databinding.ObservableArrayList import androidx.lifecycle.viewModelScope import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R @@ -21,12 +20,12 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.diffListOf import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.utils.set +import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.Shell import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.* class FlashViewModel( args: FlashFragmentArgs, @@ -45,13 +44,16 @@ class FlashViewModel( val items = diffListOf() val itemBinding = itemBindingOf() - private val outItems = ObservableArrayList() - private val logItems = Collections.synchronizedList(mutableListOf()) + private val logItems = mutableListOf().synchronized() + private val outItems = object : CallbackList() { + override fun onAddElement(e: String?) { + e ?: return + items.add(ConsoleItem(e)) + logItems.add(e) + } + } init { - outItems.sendUpdatesTo(items, viewModelScope) { it.map { ConsoleItem(it) } } - outItems.copyNewInputInto(logItems) - args.dismissId.takeIf { it != -1 }?.also { Notifications.mgr.cancel(it) }