Commit dd62fe89 authored by topjohnwu's avatar topjohnwu

Use CallbackList for collecting STDOUT in flash screen

Fix #2988
parent ec2d7d77
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 ...@@ -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( ...@@ -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)
......
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)
}
...@@ -4,7 +4,6 @@ import android.content.res.Resources ...@@ -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 ...@@ -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( ...@@ -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)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment