Use CallbackList for collecting STDOUT in flash screen

Fix #2988
This commit is contained in:
topjohnwu 2020-07-17 00:13:03 -07:00
parent ec2d7d77eb
commit dd62fe89f7
3 changed files with 21 additions and 100 deletions

View File

@ -1,6 +1,7 @@
package com.topjohnwu.magisk.ktx package com.topjohnwu.magisk.ktx
import android.os.Build import android.os.Build
import androidx.collection.SparseArrayCompat
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -11,7 +12,6 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.NoSuchElementException
fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
var entry: ZipEntry? = nextEntry var entry: ZipEntry? = nextEntry
@ -36,13 +36,19 @@ inline fun <In : InputStream, Out : OutputStream> withStreams(
} }
} }
inline fun <T, R> List<T>.firstMap(mapper: (T) -> R?): R { fun <T> MutableList<T>.update(newList: List<T>) {
for (item: T in this) { clear()
return mapper(item) ?: continue addAll(newList)
}
throw NoSuchElementException("Collection contains no element matching the predicate.")
} }
operator fun <E> SparseArrayCompat<E>.set(key: Int, value: E) {
put(key, value)
}
fun <T> MutableList<T>.synchronized() = Collections.synchronizedList(this)
fun <T> MutableSet<T>.synchronized() = Collections.synchronizedSet(this)
fun <K, V> MutableMap<K, V>.synchronized() = Collections.synchronizedMap(this)
fun String.langTagToLocale(): Locale { fun String.langTagToLocale(): Locale {
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
return Locale.forLanguageTag(this) return Locale.forLanguageTag(this)

View File

@ -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 <T> MutableList<T>.update(newList: List<T>) {
clear()
addAll(newList)
}
fun List<String>.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 <T1, T2> ObservableList<T1>.sendUpdatesTo(
target: DiffObservableList<T2>,
scope: CoroutineScope,
mapper: (List<T1>) -> List<T2>
) = addOnListChangedCallback(object :
ObservableList.OnListChangedCallback<ObservableList<T1>>() {
override fun onChanged(sender: ObservableList<T1>?) {
updateAsync(sender ?: return)
}
override fun onItemRangeRemoved(sender: ObservableList<T1>?, p0: Int, p1: Int) {
updateAsync(sender ?: return)
}
override fun onItemRangeMoved(sender: ObservableList<T1>?, p0: Int, p1: Int, p2: Int) {
updateAsync(sender ?: return)
}
override fun onItemRangeInserted(sender: ObservableList<T1>?, p0: Int, p1: Int) {
updateAsync(sender ?: return)
}
override fun onItemRangeChanged(sender: ObservableList<T1>?, p0: Int, p1: Int) {
updateAsync(sender ?: return)
}
private var updater: Job? = null
private fun updateAsync(sender: List<T1>) {
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 <T1> ObservableList<T1>.copyNewInputInto(
target: MutableList<T1>
) = addOnListChangedCallback(object : ObservableList.OnListChangedCallback<ObservableList<T1>>() {
override fun onChanged(p0: ObservableList<T1>?) = Unit
override fun onItemRangeRemoved(p0: ObservableList<T1>?, p1: Int, p2: Int) = Unit
override fun onItemRangeMoved(p0: ObservableList<T1>?, p1: Int, p2: Int, p3: Int) = Unit
override fun onItemRangeChanged(p0: ObservableList<T1>?, p1: Int, p2: Int) = Unit
override fun onItemRangeInserted(
sender: ObservableList<T1>?,
positionStart: Int,
itemCount: Int
) {
val positionEnd = positionStart + itemCount
val addedValues = sender?.slice(positionStart until positionEnd).orEmpty()
target.addAll(addedValues)
}
})
operator fun <E> SparseArrayCompat<E>.set(key: Int, value: E) {
put(key, value)
}

View File

@ -4,7 +4,6 @@ import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.view.MenuItem import android.view.MenuItem
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R 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.diffListOf
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.*
class FlashViewModel( class FlashViewModel(
args: FlashFragmentArgs, args: FlashFragmentArgs,
@ -45,13 +44,16 @@ class FlashViewModel(
val items = diffListOf<ConsoleItem>() val items = diffListOf<ConsoleItem>()
val itemBinding = itemBindingOf<ConsoleItem>() val itemBinding = itemBindingOf<ConsoleItem>()
private val outItems = ObservableArrayList<String>() private val logItems = mutableListOf<String>().synchronized()
private val logItems = Collections.synchronizedList(mutableListOf<String>()) private val outItems = object : CallbackList<String>() {
override fun onAddElement(e: String?) {
e ?: return
items.add(ConsoleItem(e))
logItems.add(e)
}
}
init { init {
outItems.sendUpdatesTo(items, viewModelScope) { it.map { ConsoleItem(it) } }
outItems.copyNewInputInto(logItems)
args.dismissId.takeIf { it != -1 }?.also { args.dismissId.takeIf { it != -1 }?.also {
Notifications.mgr.cancel(it) Notifications.mgr.cancel(it)
} }