Fix UI bugs
This commit is contained in:
parent
a9b27b1427
commit
d7b3b4cb6b
|
|
@ -10,6 +10,7 @@ import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
|
@ -18,7 +19,7 @@ import org.lsposed.lspatch.ui.page.PageList
|
||||||
import org.lsposed.lspatch.ui.theme.LSPTheme
|
import org.lsposed.lspatch.ui.theme.LSPTheme
|
||||||
import org.lsposed.lspatch.ui.util.LocalNavController
|
import org.lsposed.lspatch.ui.util.LocalNavController
|
||||||
import org.lsposed.lspatch.ui.util.LocalSnackbarHost
|
import org.lsposed.lspatch.ui.util.LocalSnackbarHost
|
||||||
import org.lsposed.lspatch.ui.util.currentRoute
|
import org.lsposed.lspatch.ui.util.navigateWithState
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
|
@ -27,7 +28,6 @@ class MainActivity : ComponentActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
val navController = rememberAnimatedNavController()
|
val navController = rememberAnimatedNavController()
|
||||||
val currentRoute = navController.currentRoute
|
|
||||||
var mainPage by rememberSaveable { mutableStateOf(PageList.Home) }
|
var mainPage by rememberSaveable { mutableStateOf(PageList.Home) }
|
||||||
|
|
||||||
LSPTheme {
|
LSPTheme {
|
||||||
|
|
@ -39,12 +39,9 @@ class MainActivity : ComponentActivity() {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
MainNavigationBar(mainPage) {
|
MainNavigationBar(mainPage) {
|
||||||
|
if (mainPage == it) return@MainNavigationBar
|
||||||
mainPage = it
|
mainPage = it
|
||||||
navController.navigate(it.name) {
|
navController.navigateWithState(it.name)
|
||||||
currentRoute?.let { route ->
|
|
||||||
popUpTo(route) { inclusive = true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||||
|
|
@ -64,12 +61,8 @@ private fun MainNavHost(navController: NavHostController, modifier: Modifier) {
|
||||||
startDestination = PageList.Home.name,
|
startDestination = PageList.Home.name,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
PageList.values().forEach { page ->
|
for (page in PageList.values()) {
|
||||||
val sb = StringBuilder(page.name)
|
composable(route = page.route, arguments = page.arguments, content = page.body)
|
||||||
if (page.arguments.isNotEmpty()) {
|
|
||||||
sb.append(page.arguments.joinToString(",", "?") { "${it.name}={${it.name}}" })
|
|
||||||
}
|
|
||||||
composable(route = sb.toString(), arguments = page.arguments, content = page.body)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,10 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.*
|
import androidx.compose.material.icons.outlined.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
|
@ -38,7 +36,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.lsposed.lspatch.Patcher
|
|
||||||
import org.lsposed.lspatch.R
|
import org.lsposed.lspatch.R
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
import org.lsposed.lspatch.ui.component.SelectionColumn
|
import org.lsposed.lspatch.ui.component.SelectionColumn
|
||||||
|
|
@ -51,7 +48,6 @@ import org.lsposed.lspatch.ui.viewmodel.NewPatchViewModel.PatchState
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager
|
import org.lsposed.lspatch.util.LSPPackageManager
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
||||||
import org.lsposed.lspatch.util.ShizukuApi
|
import org.lsposed.lspatch.util.ShizukuApi
|
||||||
import org.lsposed.patch.util.Logger
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private const val TAG = "NewPatchPage"
|
private const val TAG = "NewPatchPage"
|
||||||
|
|
@ -64,12 +60,6 @@ fun NewPatchPage(from: String, entry: NavBackStackEntry) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
val isCancelled by entry.observeState<Boolean>("isCancelled")
|
val isCancelled by entry.observeState<Boolean>("isCancelled")
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
lspApp.tmpApkDir.listFiles()?.forEach(File::delete)
|
|
||||||
entry.savedStateHandle.getLiveData<AppInfo>("appInfo").observe(lifecycleOwner) {
|
|
||||||
viewModel.configurePatch(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "PatchState: ${viewModel.patchState}")
|
Log.d(TAG, "PatchState: ${viewModel.patchState}")
|
||||||
if (viewModel.patchState == PatchState.SELECTING) {
|
if (viewModel.patchState == PatchState.SELECTING) {
|
||||||
|
|
@ -90,10 +80,16 @@ fun NewPatchPage(from: String, entry: NavBackStackEntry) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
lspApp.tmpApkDir.listFiles()?.forEach(File::delete)
|
||||||
if (isCancelled == true) navController.popBackStack()
|
if (isCancelled == true) navController.popBackStack()
|
||||||
else when (from) {
|
else when (from) {
|
||||||
"storage" -> storageLauncher.launch(arrayOf("application/vnd.android.package-archive"))
|
"storage" -> storageLauncher.launch(arrayOf("application/vnd.android.package-archive"))
|
||||||
"applist" -> navController.navigate(PageList.SelectApps.name + "?multiSelect=false")
|
"applist" -> {
|
||||||
|
entry.savedStateHandle.getLiveData<AppInfo>("appInfo").observe(lifecycleOwner) {
|
||||||
|
viewModel.configurePatch(it)
|
||||||
|
}
|
||||||
|
navController.navigate(PageList.SelectApps.name + "?multiSelect=false")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -268,44 +264,16 @@ private fun PatchOptionsBody(modifier: Modifier) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PatchLogger(private val logs: MutableList<Pair<Int, String>>) : Logger() {
|
|
||||||
override fun d(msg: String) {
|
|
||||||
if (verbose) {
|
|
||||||
Log.d(TAG, msg)
|
|
||||||
logs += Log.DEBUG to msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun i(msg: String) {
|
|
||||||
Log.i(TAG, msg)
|
|
||||||
logs += Log.INFO to msg
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun e(msg: String) {
|
|
||||||
Log.e(TAG, msg)
|
|
||||||
logs += Log.ERROR to msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DoPatchBody(modifier: Modifier) {
|
private fun DoPatchBody(modifier: Modifier) {
|
||||||
val viewModel = viewModel<NewPatchViewModel>()
|
val viewModel = viewModel<NewPatchViewModel>()
|
||||||
val snackbarHost = LocalSnackbarHost.current
|
val snackbarHost = LocalSnackbarHost.current
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val logs = remember { mutableStateListOf<Pair<Int, String>>() }
|
|
||||||
val logger = remember { PatchLogger(logs) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
try {
|
if (viewModel.logs.isEmpty()) {
|
||||||
Patcher.patch(logger, viewModel.patchOptions)
|
viewModel.launchPatch()
|
||||||
viewModel.finishPatch()
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
logger.e(t.message.orEmpty())
|
|
||||||
logger.e(t.stackTraceToString())
|
|
||||||
viewModel.failPatch()
|
|
||||||
} finally {
|
|
||||||
lspApp.tmpApkDir.listFiles()?.forEach(File::delete)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +301,7 @@ private fun DoPatchBody(modifier: Modifier) {
|
||||||
.background(brush)
|
.background(brush)
|
||||||
.padding(horizontal = 24.dp, vertical = 18.dp)
|
.padding(horizontal = 24.dp, vertical = 18.dp)
|
||||||
) {
|
) {
|
||||||
items(logs) {
|
items(viewModel.logs) {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
Log.DEBUG -> Text(text = it.second)
|
Log.DEBUG -> Text(text = it.second)
|
||||||
Log.INFO -> Text(text = it.second)
|
Log.INFO -> Text(text = it.second)
|
||||||
|
|
@ -357,7 +325,7 @@ private fun DoPatchBody(modifier: Modifier) {
|
||||||
val installSuccessfully = stringResource(R.string.patch_install_successfully)
|
val installSuccessfully = stringResource(R.string.patch_install_successfully)
|
||||||
val installFailed = stringResource(R.string.patch_install_failed)
|
val installFailed = stringResource(R.string.patch_install_failed)
|
||||||
val copyError = stringResource(R.string.patch_copy_error)
|
val copyError = stringResource(R.string.patch_copy_error)
|
||||||
var installing by rememberSaveable { mutableStateOf(false) }
|
var installing by remember { mutableStateOf(false) }
|
||||||
if (installing) InstallDialog(viewModel.patchApp) { status, message ->
|
if (installing) InstallDialog(viewModel.patchApp) { status, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
LSPPackageManager.fetchAppList()
|
LSPPackageManager.fetchAppList()
|
||||||
|
|
@ -408,7 +376,7 @@ private fun DoPatchBody(modifier: Modifier) {
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
onClick = {
|
onClick = {
|
||||||
val cm = lspApp.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val cm = lspApp.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
cm.setPrimaryClip(ClipData.newPlainText("LSPatch", logs.joinToString { it.second + "\n" }))
|
cm.setPrimaryClip(ClipData.newPlainText("LSPatch", viewModel.logs.joinToString { it.second + "\n" }))
|
||||||
},
|
},
|
||||||
content = { Text(stringResource(R.string.patch_copy_error)) }
|
content = { Text(stringResource(R.string.patch_copy_error)) }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,11 @@ enum class PageList(
|
||||||
NewPatch -> stringResource(R.string.page_new_patch)
|
NewPatch -> stringResource(R.string.page_new_patch)
|
||||||
SelectApps -> stringResource(R.string.page_select_apps)
|
SelectApps -> stringResource(R.string.page_select_apps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val route = buildString {
|
||||||
|
append(name)
|
||||||
|
if (arguments.isNotEmpty()) {
|
||||||
|
append(arguments.joinToString(",", "?") { "${it.name}={${it.name}}" })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,27 +5,18 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
||||||
|
|
||||||
val NavController.currentRoute: String?
|
|
||||||
@Composable get() = currentBackStackEntryAsState().value?.destination?.route
|
|
||||||
|
|
||||||
val NavController.startRoute: String?
|
|
||||||
get() = graph.findStartDestination().route
|
|
||||||
|
|
||||||
fun <T> NavBackStackEntry.setState(key: String, value: T?) {
|
fun <T> NavBackStackEntry.setState(key: String, value: T?) {
|
||||||
savedStateHandle.getLiveData<T>(key).value = value
|
savedStateHandle.getLiveData<T>(key).value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> NavBackStackEntry.observeState(key: String, initial: T? = null) = savedStateHandle.getLiveData(key, initial).observeAsState()
|
fun <T> NavBackStackEntry.observeState(key: String, initial: T? = null) =
|
||||||
|
savedStateHandle.getLiveData(key, initial).observeAsState()
|
||||||
|
|
||||||
@Composable
|
fun NavController.navigateWithState(route: String) {
|
||||||
fun NavController.isAtStartRoute(): Boolean = currentRoute == startRoute
|
navigate(route) {
|
||||||
|
popUpTo(graph.findStartDestination().id) {
|
||||||
fun NavController.navigateWithState(route: String?) {
|
|
||||||
navigate(route.toString()) {
|
|
||||||
popUpTo(startRoute.toString()) {
|
|
||||||
saveState = true
|
saveState = true
|
||||||
}
|
}
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
package org.lsposed.lspatch.ui.viewmodel
|
package org.lsposed.lspatch.ui.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.lsposed.lspatch.Patcher
|
import org.lsposed.lspatch.Patcher
|
||||||
|
import org.lsposed.lspatch.lspApp
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
||||||
|
import org.lsposed.patch.util.Logger
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private const val TAG = "NewPatchViewModel"
|
||||||
|
|
||||||
class NewPatchViewModel : ViewModel() {
|
class NewPatchViewModel : ViewModel() {
|
||||||
|
|
||||||
|
|
@ -21,7 +29,7 @@ class NewPatchViewModel : ViewModel() {
|
||||||
var useManager by mutableStateOf(true)
|
var useManager by mutableStateOf(true)
|
||||||
var debuggable by mutableStateOf(false)
|
var debuggable by mutableStateOf(false)
|
||||||
var overrideVersionCode by mutableStateOf(false)
|
var overrideVersionCode by mutableStateOf(false)
|
||||||
var sign = mutableStateListOf(false, true)
|
val sign = mutableStateListOf(false, true)
|
||||||
var sigBypassLevel by mutableStateOf(2)
|
var sigBypassLevel by mutableStateOf(2)
|
||||||
|
|
||||||
lateinit var patchApp: AppInfo
|
lateinit var patchApp: AppInfo
|
||||||
|
|
@ -30,12 +38,34 @@ class NewPatchViewModel : ViewModel() {
|
||||||
lateinit var patchOptions: Patcher.Options
|
lateinit var patchOptions: Patcher.Options
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
val logs = mutableStateListOf<Pair<Int, String>>()
|
||||||
|
private val logger = object : Logger() {
|
||||||
|
override fun d(msg: String) {
|
||||||
|
if (verbose) {
|
||||||
|
Log.d(TAG, msg)
|
||||||
|
logs += Log.DEBUG to msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun i(msg: String) {
|
||||||
|
Log.i(TAG, msg)
|
||||||
|
logs += Log.INFO to msg
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun e(msg: String) {
|
||||||
|
Log.e(TAG, msg)
|
||||||
|
logs += Log.ERROR to msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun configurePatch(app: AppInfo) {
|
fun configurePatch(app: AppInfo) {
|
||||||
|
Log.d(TAG, "Configuring patch for ${app.app.packageName}")
|
||||||
patchApp = app
|
patchApp = app
|
||||||
patchState = PatchState.CONFIGURING
|
patchState = PatchState.CONFIGURING
|
||||||
}
|
}
|
||||||
|
|
||||||
fun submitPatch() {
|
fun submitPatch() {
|
||||||
|
Log.d(TAG, "Submit patch")
|
||||||
if (useManager) embeddedModules.clear()
|
if (useManager) embeddedModules.clear()
|
||||||
patchOptions = Patcher.Options(
|
patchOptions = Patcher.Options(
|
||||||
apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()),
|
apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()),
|
||||||
|
|
@ -50,11 +80,19 @@ class NewPatchViewModel : ViewModel() {
|
||||||
patchState = PatchState.PATCHING
|
patchState = PatchState.PATCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finishPatch() {
|
fun launchPatch() {
|
||||||
patchState = PatchState.FINISHED
|
logger.i("Launch patch")
|
||||||
}
|
viewModelScope.launch {
|
||||||
|
patchState = try {
|
||||||
fun failPatch() {
|
Patcher.patch(logger, patchOptions)
|
||||||
patchState = PatchState.ERROR
|
PatchState.FINISHED
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logger.e(t.message.orEmpty())
|
||||||
|
logger.e(t.stackTraceToString())
|
||||||
|
PatchState.ERROR
|
||||||
|
} finally {
|
||||||
|
lspApp.tmpApkDir.listFiles()?.forEach(File::delete)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue