From 4d581071b2e2235e3b59b053b439a5fdb92b8493 Mon Sep 17 00:00:00 2001 From: nift4 Date: Sun, 11 Aug 2024 19:39:09 +0200 Subject: [PATCH] wip refactor --- .../java/org/andbootmgr/app/CreatePartFlow.kt | 12 ++-- .../java/org/andbootmgr/app/DroidBootFlow.kt | 14 +++-- .../java/org/andbootmgr/app/MainActivity.kt | 63 ++++++++++--------- .../org/andbootmgr/app/UpdateDroidBootFlow.kt | 4 +- .../java/org/andbootmgr/app/WizardActivity.kt | 19 +++--- .../java/org/andbootmgr/app/themes/Themes.kt | 34 +++++----- .../andbootmgr/app/util/StayAliveService.kt | 24 +++++++ .../java/org/andbootmgr/app/util/Toolkit.kt | 29 +++++---- 8 files changed, 118 insertions(+), 81 deletions(-) create mode 100644 app/src/main/java/org/andbootmgr/app/util/StayAliveService.kt diff --git a/app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt b/app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt index 8bf58186..95da7ccc 100644 --- a/app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt +++ b/app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt @@ -24,6 +24,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.io.SuFile +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import okhttp3.* import okio.* import org.andbootmgr.app.util.AbmTheme @@ -351,7 +355,7 @@ private fun Shop(c: CreatePartDataHolder) { val ctx = LocalContext.current LaunchedEffect(Unit) { c.run { - Thread { + CoroutineScope(Dispatchers.IO).launch { try { val jsonText = try { ctx.assets.open("abm.json").readBytes().toString(Charsets.UTF_8) @@ -838,7 +842,7 @@ private fun Download(c: CreatePartDataHolder) { SOUtils.humanReadableByteCountBin(bytesRead), SOUtils.humanReadableByteCountBin(contentLength)) } } - Thread { + CoroutineScope(Dispatchers.IO).launch { try { val downloadedFile = File(c.vm.logic.cacheDir, i) val request = @@ -868,7 +872,7 @@ private fun Download(c: CreatePartDataHolder) { c.chosen[i] = DledFile(null, downloadedFile) } catch (e: Exception) { Log.e("ABM", Log.getStackTraceString(e)) - c.vm.activity.runOnUiThread { + withContext(Dispatchers.Main) { Toast.makeText( c.vm.activity, c.vm.activity.getString(R.string.dl_error), @@ -878,7 +882,7 @@ private fun Download(c: CreatePartDataHolder) { } c.pl = null downloading = false - }.start() + } }) { Text(stringResource(R.string.download)) } diff --git a/app/src/main/java/org/andbootmgr/app/DroidBootFlow.kt b/app/src/main/java/org/andbootmgr/app/DroidBootFlow.kt index ebfac44f..d65adffd 100644 --- a/app/src/main/java/org/andbootmgr/app/DroidBootFlow.kt +++ b/app/src/main/java/org/andbootmgr/app/DroidBootFlow.kt @@ -20,6 +20,10 @@ import androidx.compose.ui.unit.dp import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFileInputStream +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.andbootmgr.app.util.AbmTheme import org.andbootmgr.app.util.ConfigFile import org.andbootmgr.app.util.SDUtils @@ -142,7 +146,7 @@ fun SelectDroidBoot(vm: WizardActivityState) { } val ctx = LocalContext.current Button(onClick = { - Thread { + CoroutineScope(Dispatchers.Default).launch { try { val jsonText = URL("https://raw.githubusercontent.com/Android-Boot-Manager/ABM-json/master/devices/" + vm.codename + ".json").readText() @@ -160,7 +164,7 @@ fun SelectDroidBoot(vm: WizardActivityState) { } Log.e("ABM droidboot json", Log.getStackTraceString(e)) } - }.start() + } }) { Text(stringResource(id = R.string.download)) } @@ -193,7 +197,7 @@ fun SelectInstallSh(vm: WizardActivityState, update: Boolean = false) { } val ctx = LocalContext.current Button(onClick = { - Thread { + CoroutineScope(Dispatchers.IO).launch { try { val jsonText = URL("https://raw.githubusercontent.com/Android-Boot-Manager/ABM-json/master/devices/" + vm.codename + ".json").readText() @@ -211,7 +215,7 @@ fun SelectInstallSh(vm: WizardActivityState, update: Boolean = false) { } Log.e("ABM install json", Log.getStackTraceString(e)) } - }.start() + } }) { Text(stringResource(id = R.string.download)) } @@ -354,7 +358,7 @@ private fun Flash(vm: WizardActivityState) { } terminal.add(vm.activity.getString(R.string.term_success)) vm.logic.unmountBootset() - vm.activity.runOnUiThread { + withContext(Dispatchers.Main) { vm.btnsOverride = true vm.nextText.value = vm.activity.getString(R.string.finish) vm.onNext.value = { diff --git a/app/src/main/java/org/andbootmgr/app/MainActivity.kt b/app/src/main/java/org/andbootmgr/app/MainActivity.kt index 1e13812c..d19cc9e9 100644 --- a/app/src/main/java/org/andbootmgr/app/MainActivity.kt +++ b/app/src/main/java/org/andbootmgr/app/MainActivity.kt @@ -177,7 +177,7 @@ class MainActivity : ComponentActivity() { val toast = Toast.makeText(this, getString(R.string.toolkit_extracting), Toast.LENGTH_LONG) - Thread { + CoroutineScope(Dispatchers.IO).launch { if (Shell.getCachedShell() == null) { Shell.enableVerboseLogging = BuildConfig.DEBUG Shell.setDefaultBuilder( @@ -186,17 +186,39 @@ class MainActivity : ComponentActivity() { .setTimeout(30) ) } - Toolkit(this).copyAssets({ - runOnUiThread { + Toolkit(this@MainActivity).copyAssets({ + withContext(Dispatchers.Main) { toast.show() } }) { fail -> - runOnUiThread { + withContext(Dispatchers.Main) { toast.cancel() + if (fail) { + setContent { + AlertDialog( + onDismissRequest = {}, + title = { + Text(text = getString(R.string.error)) + }, + text = { + Text(getString(R.string.toolkit_error)) + }, + confirmButton = { + Button( + onClick = { + finish() + }) { + Text(getString(R.string.quit)) + } + } + ) + } + vm.isReady = true + } } if (!fail) { Shell.getShell { shell -> - Thread { + CoroutineScope(Dispatchers.IO).launch { vm.root = shell.isRoot vm.deviceInfo = JsonDeviceInfoFactory(vm.activity!!).get(Build.DEVICE) // == temp migration code start == @@ -218,7 +240,7 @@ class MainActivity : ComponentActivity() { vm.deviceInfo!!.isBooted(vm.logic!!) && !(!vm.logic!!.mounted || vm.deviceInfo!!.isCorrupt(vm.logic!!))) } - runOnUiThread { + withContext(Dispatchers.Main) { setContent { val navController = rememberNavController() val drawerState = rememberDrawerState(DrawerValue.Closed) @@ -235,32 +257,11 @@ class MainActivity : ComponentActivity() { } vm.isReady = true } - }.start() - } - } else { - setContent { - AlertDialog( - onDismissRequest = {}, - title = { - Text(text = getString(R.string.error)) - }, - text = { - Text(getString(R.string.toolkit_error)) - }, - confirmButton = { - Button( - onClick = { - finish() - }) { - Text(getString(R.string.quit)) - } - } - ) + } } - vm.isReady = true } } - }.start() + } } } @@ -1076,7 +1077,7 @@ private fun PartTool(vm: MainActivityState) { Button(onClick = { processing = true delete = false - Thread { + CoroutineScope(Dispatchers.Default).launch { var tresult = "" if (e.has("xpart") && !e["xpart"].isNullOrBlank()) { val allp = e["xpart"]!!.split(":") @@ -1100,7 +1101,7 @@ private fun PartTool(vm: MainActivityState) { processing = false parts = SDUtils.generateMeta(vm.deviceInfo!!) result = tresult - }.start() + } }) { Text(stringResource(R.string.delete)) } diff --git a/app/src/main/java/org/andbootmgr/app/UpdateDroidBootFlow.kt b/app/src/main/java/org/andbootmgr/app/UpdateDroidBootFlow.kt index 2f975cea..9908fe78 100644 --- a/app/src/main/java/org/andbootmgr/app/UpdateDroidBootFlow.kt +++ b/app/src/main/java/org/andbootmgr/app/UpdateDroidBootFlow.kt @@ -11,6 +11,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFileInputStream +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.andbootmgr.app.util.AbmTheme import java.io.File import java.io.IOException @@ -82,7 +84,7 @@ private fun Flash(vm: WizardActivityState) { tmpFile.delete() } terminal.add(vm.activity.getString(R.string.term_success)) - vm.activity.runOnUiThread { + withContext(Dispatchers.Main) { vm.btnsOverride = true vm.nextText.value = vm.activity.getString(R.string.finish) vm.onNext.value = { diff --git a/app/src/main/java/org/andbootmgr/app/WizardActivity.kt b/app/src/main/java/org/andbootmgr/app/WizardActivity.kt index 3bea8486..bcfaa109 100644 --- a/app/src/main/java/org/andbootmgr/app/WizardActivity.kt +++ b/app/src/main/java/org/andbootmgr/app/WizardActivity.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.andbootmgr.app.util.AbmTheme import java.io.File import java.io.FileInputStream @@ -320,7 +321,7 @@ fun Terminal(vm: WizardActivityState, logFile: String? = null, action: suspend ( val scope = rememberCoroutineScope() val text = remember { mutableStateOf("") } val ctx = LocalContext.current - DisposableEffect(Unit) { + DisposableEffect(Unit) { // TODO don't run again on UI recreation val log = logFile?.let { FileOutputStream(File(ctx.externalCacheDir, it)) } // Budget CallbackList val s = object : MutableList { @@ -417,21 +418,21 @@ fun Terminal(vm: WizardActivityState, logFile: String? = null, action: suspend ( } fun onAdd(element: String) { - vm.activity.runOnUiThread { + scope.launch { text.value += element + "\n" - scope.launch { - delay(200) // Give it time to re-measure - scrollV.animateScrollTo(scrollV.maxValue) - scrollH.animateScrollTo(0) - } + delay(200) // Give it time to re-measure + scrollV.animateScrollTo(scrollV.maxValue) + scrollH.animateScrollTo(0) } log?.write((element + "\n").encodeToByteArray()) } } - CoroutineScope(Dispatchers.Default).launch { + CoroutineScope(Dispatchers.Default).launch { // do not bind to composable, we must never die try { - action(s) + withContext(Dispatchers.Default) { + action(s) + } } catch (e: Throwable) { s.add(vm.activity.getString(R.string.term_failure)) s.add(vm.activity.getString(R.string.dev_details)) diff --git a/app/src/main/java/org/andbootmgr/app/themes/Themes.kt b/app/src/main/java/org/andbootmgr/app/themes/Themes.kt index c1570c20..ef3021af 100644 --- a/app/src/main/java/org/andbootmgr/app/themes/Themes.kt +++ b/app/src/main/java/org/andbootmgr/app/themes/Themes.kt @@ -237,7 +237,7 @@ fun Themes(vm: ThemeViewModel) { label = { Text(stringResource(id = cfg.text)) }, isError = error, keyboardOptions = KeyboardOptions( - keyboardType = if (cfg is NumberConfig) + keyboardType = if (cfg is IntConfig || cfg is ShortConfig) KeyboardType.Decimal else KeyboardType.Text, capitalization = KeyboardCapitalization.None, imeAction = ImeAction.Next, @@ -256,20 +256,22 @@ class ColorConfig(text: Int, configKey: String, default: String) : Config(text, (it.substring(2).toIntOrNull(16) ?: -1) in 0..0xffffff }) class BoolConfig(text: Int, configKey: String, default: String) : Config(text, configKey, default, { it.toBooleanStrictOrNull() != null}) -class NumberConfig(text: Int, configKey: String, default: String, validate: (String) -> Boolean) - : Config(text, configKey, default, validate) +class IntConfig(text: Int, configKey: String, default: String) + : Config(text, configKey, default, { it.toIntOrNull() != null }) +class ShortConfig(text: Int, configKey: String, default: String) + : Config(text, configKey, default, { it.toShortOrNull() != null }) class ThemeViewModel(val mvm: MainActivityState) : ViewModel() { val configs = listOf( ColorConfig(R.string.win_bg_color, "win_bg_color", "0x000000"), - NumberConfig(R.string.win_radius, "win_radius", "0") { it.toShortOrNull() != null }, - NumberConfig(R.string.win_border_size, "win_border_size", "0") { it.toShortOrNull() != null }, + ShortConfig(R.string.win_radius, "win_radius", "0"), + ShortConfig(R.string.win_border_size, "win_border_size", "0"), ColorConfig(R.string.win_border_color, "win_border_color", "0xffffff"), ColorConfig(R.string.list_bg_color, "list_bg_color", "0x000000"), - NumberConfig(R.string.list_radius, "list_radius", "0") { it.toShortOrNull() != null }, - NumberConfig(R.string.list_border_size, "list_border_size", "0") { it.toShortOrNull() != null }, + ShortConfig(R.string.list_radius, "list_radius", "0"), + ShortConfig(R.string.list_border_size, "list_border_size", "0"), ColorConfig(R.string.list_border_color, "list_border_color", "0xffffff"), - NumberConfig(R.string.global_font_size, "global_font_size", "0") { it.toIntOrNull() != null }, + IntConfig(R.string.global_font_size, "global_font_size", "0"), Config( R.string.global_font_name, "global_font_name", @@ -281,18 +283,18 @@ class ThemeViewModel(val mvm: MainActivityState) : ViewModel() { "button_unselected_text_color", "0xffffff" ), - NumberConfig( + ShortConfig( R.string.button_unselected_radius, "button_unselected_radius", "0" - ) { it.toShortOrNull() != null }, + ), ColorConfig(R.string.button_selected_color, "button_selected_color", "0xff9800"), ColorConfig(R.string.button_selected_text_color, "button_selected_text_color", "0x000000"), - NumberConfig( + ShortConfig( R.string.button_selected_radius, "button_selected_radius", "0" - ) { it.toShortOrNull() != null }, + ), BoolConfig( R.string.button_grow_default, "button_grow_default", @@ -303,21 +305,21 @@ class ThemeViewModel(val mvm: MainActivityState) : ViewModel() { "button_border_unselected_color", "0xffffff" ), - NumberConfig( + IntConfig( R.string.button_border_unselected_size, "button_border_unselected_size", "1" - ) { it.toIntOrNull() != null }, + ), ColorConfig( R.string.button_border_selected_color, "button_border_selected_color", "0xffffff" ), - NumberConfig( + IntConfig( R.string.button_border_selected_size, "button_border_selected_size", "1" - ) { it.toIntOrNull() != null } + ) ) } diff --git a/app/src/main/java/org/andbootmgr/app/util/StayAliveService.kt b/app/src/main/java/org/andbootmgr/app/util/StayAliveService.kt new file mode 100644 index 00000000..f1549575 --- /dev/null +++ b/app/src/main/java/org/andbootmgr/app/util/StayAliveService.kt @@ -0,0 +1,24 @@ +package org.andbootmgr.app.util + +import android.app.Service +import android.content.Intent +import android.os.Binder +import android.os.IBinder + +class StayAliveService : Service() { + override fun onCreate() { + super.onCreate() + startForeground(FG_SERVICE_ID, notif) // TODO + } + + override fun onBind(intent: Intent?): IBinder { + return object : Binder(), Provider { + override val service + get() = this@StayAliveService + } + } + + interface Provider { + val service: StayAliveService + } +} \ No newline at end of file diff --git a/app/src/main/java/org/andbootmgr/app/util/Toolkit.kt b/app/src/main/java/org/andbootmgr/app/util/Toolkit.kt index ab5b8dd9..4814c836 100644 --- a/app/src/main/java/org/andbootmgr/app/util/Toolkit.kt +++ b/app/src/main/java/org/andbootmgr/app/util/Toolkit.kt @@ -5,35 +5,34 @@ import android.content.res.AssetManager import android.util.Log import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell.FLAG_NON_ROOT_SHELL +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.* -import java.util.function.Consumer // Manage & extract Toolkit class Toolkit(private val ctx: Context) { private var fail = false private val targetPath = File(ctx.filesDir.parentFile, "assets") - fun copyAssets(uinf: Runnable, callback: Consumer) { + suspend fun copyAssets(uinf: suspend () -> Unit, callback: suspend (Boolean) -> Unit) { val shell = Shell.Builder.create().setFlags(FLAG_NON_ROOT_SHELL).setTimeout(30).setContext(ctx).build() - var input: InputStream - var b: ByteArray - try { - input = ctx.assets.open("cp/_ts") - b = input.readBytes() - input.close() + var b = try { + withContext(Dispatchers.IO) { + ctx.assets.open("cp/_ts").use { it.readBytes() } + } } catch (e: IOException) { e.printStackTrace() fail = true - callback.accept(true) + callback(true) return } val s = String(b).trim() - try { - input = FileInputStream(File(targetPath, "_ts")) - b = input.readBytes() - input.close() + b = try { + withContext(Dispatchers.IO) { + FileInputStream(File(targetPath, "_ts")).use { it.readBytes() } + } } catch (e: IOException) { - b = ByteArray(0) + ByteArray(0) } val s2 = String(b).trim() if (s != s2) { @@ -46,7 +45,7 @@ class Toolkit(private val ctx: Context) { copyAssets("cp", "") } shell.newJob().add("chmod -R +x " + targetPath.absolutePath).exec() - callback.accept(fail) + callback(fail) } private fun copyAssets(src: String, outp: String) {