feat: select module(s) from storage (#225)
This commit is contained in:
parent
b0fbc7341c
commit
8462043963
|
|
@ -68,6 +68,7 @@ fun NewPatchScreen(
|
|||
) {
|
||||
val viewModel = viewModel<NewPatchViewModel>()
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val errorUnknown = stringResource(R.string.error_unknown)
|
||||
val storageLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { apks ->
|
||||
if (apks.isEmpty()) {
|
||||
navigator.navigateUp()
|
||||
|
|
@ -76,15 +77,40 @@ fun NewPatchScreen(
|
|||
runBlocking {
|
||||
LSPPackageManager.getAppInfoFromApks(apks)
|
||||
.onSuccess {
|
||||
viewModel.dispatch(ViewAction.ConfigurePatch(it))
|
||||
viewModel.dispatch(ViewAction.ConfigurePatch(it.first()))
|
||||
}
|
||||
.onFailure {
|
||||
lspApp.globalScope.launch { snackbarHost.showSnackbar(it.message ?: "Unknown error") }
|
||||
lspApp.globalScope.launch { snackbarHost.showSnackbar(it.message ?: errorUnknown) }
|
||||
navigator.navigateUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var showSelectModuleDialog by remember { mutableStateOf(false) }
|
||||
val noXposedModules = stringResource(R.string.patch_no_xposed_module)
|
||||
val storageModuleLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { apks ->
|
||||
if (apks.isEmpty()) {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runBlocking {
|
||||
LSPPackageManager.getAppInfoFromApks(apks).onSuccess { it ->
|
||||
viewModel.embeddedModules = it.filter { it.isXposedModule }.ifEmpty {
|
||||
lspApp.globalScope.launch {
|
||||
snackbarHost.showSnackbar(noXposedModules)
|
||||
}
|
||||
return@onSuccess
|
||||
}
|
||||
}.onFailure {
|
||||
lspApp.globalScope.launch {
|
||||
snackbarHost.showSnackbar(
|
||||
it.message ?: errorUnknown
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "PatchState: ${viewModel.patchState}")
|
||||
when (viewModel.patchState) {
|
||||
PatchState.INIT -> {
|
||||
|
|
@ -128,7 +154,7 @@ fun NewPatchScreen(
|
|||
) { innerPadding ->
|
||||
if (viewModel.patchState == PatchState.CONFIGURING) {
|
||||
PatchOptionsBody(Modifier.padding(innerPadding)) {
|
||||
navigator.navigate(SelectAppsScreenDestination(true, viewModel.embeddedModules.mapTo(ArrayList()) { it.app.packageName }))
|
||||
showSelectModuleDialog = true
|
||||
}
|
||||
resultRecipient.onNavResult {
|
||||
if (it is NavResult.Value) {
|
||||
|
|
@ -140,6 +166,53 @@ fun NewPatchScreen(
|
|||
DoPatchBody(Modifier.padding(innerPadding), navigator)
|
||||
}
|
||||
}
|
||||
|
||||
if (showSelectModuleDialog) {
|
||||
AlertDialog(onDismissRequest = { showSelectModuleDialog = false },
|
||||
confirmButton = {},
|
||||
dismissButton = {
|
||||
TextButton(content = { Text(stringResource(android.R.string.cancel)) },
|
||||
onClick = { showSelectModuleDialog = false })
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.patch_embed_modules),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
TextButton(modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary),
|
||||
onClick = {
|
||||
storageModuleLauncher.launch(arrayOf("application/vnd.android.package-archive"))
|
||||
showSelectModuleDialog = false
|
||||
}) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
text = stringResource(R.string.patch_from_storage),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
}
|
||||
TextButton(modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary),
|
||||
onClick = {
|
||||
navigator.navigate(
|
||||
SelectAppsScreenDestination(true,
|
||||
viewModel.embeddedModules.mapTo(ArrayList()) { it.app.packageName })
|
||||
)
|
||||
showSelectModuleDialog = false
|
||||
}) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
text = stringResource(R.string.patch_from_applist),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,11 +152,12 @@ object LSPPackageManager {
|
|||
return Pair(status, message)
|
||||
}
|
||||
|
||||
suspend fun getAppInfoFromApks(apks: List<Uri>): Result<AppInfo> {
|
||||
suspend fun getAppInfoFromApks(apks: List<Uri>): Result<List<AppInfo>> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
var primary: ApplicationInfo? = null
|
||||
val splits = apks.mapNotNull { uri ->
|
||||
val splits = mutableListOf<String>()
|
||||
val appInfos = apks.mapNotNull { uri ->
|
||||
val src = DocumentFile.fromSingleUri(lspApp, uri)
|
||||
?: throw IOException("DocumentFile is null")
|
||||
val dst = lspApp.tmpApkDir.resolve(src.name!!)
|
||||
|
|
@ -167,21 +168,25 @@ object LSPPackageManager {
|
|||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
if (primary == null) {
|
||||
primary = lspApp.packageManager.getPackageArchiveInfo(dst.absolutePath, 0)?.applicationInfo
|
||||
primary?.let {
|
||||
it.sourceDir = dst.absolutePath
|
||||
return@mapNotNull null
|
||||
}
|
||||
}
|
||||
dst.absolutePath
|
||||
}
|
||||
|
||||
val appInfo = lspApp.packageManager.getPackageArchiveInfo(
|
||||
dst.absolutePath, PackageManager.GET_META_DATA
|
||||
)?.applicationInfo
|
||||
appInfo?.sourceDir = dst.absolutePath
|
||||
if (appInfo == null) {
|
||||
splits.add(dst.absolutePath)
|
||||
return@mapNotNull null
|
||||
}
|
||||
if (primary == null) {
|
||||
primary = appInfo
|
||||
}
|
||||
val label = lspApp.packageManager.getApplicationLabel(appInfo).toString()
|
||||
AppInfo(appInfo, label)
|
||||
}
|
||||
// TODO: Check selected apks are from the same app
|
||||
if (primary == null) throw IllegalArgumentException("No primary apk")
|
||||
val label = lspApp.packageManager.getApplicationLabel(primary!!).toString()
|
||||
if (splits.isNotEmpty()) primary!!.splitSourceDirs = splits.toTypedArray()
|
||||
AppInfo(primary!!, label)
|
||||
primary?.splitSourceDirs = splits.toTypedArray()
|
||||
if (appInfos.isEmpty()) throw IOException("No apks")
|
||||
appInfos
|
||||
}.recoverCatching { t ->
|
||||
cleanTmpApkDir()
|
||||
Log.e(TAG, "Failed to load apks", t)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<string name="screen_repo">Repo</string>
|
||||
<string name="screen_logs">Logs</string>
|
||||
<string name="off">Off</string>
|
||||
<string name="error_unknown">Unknown error</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_shizuku_warning">Some functions unavailable</string>
|
||||
|
|
@ -68,6 +69,7 @@
|
|||
<string name="patch_uninstall_text">Due to different signatures, you need to uninstall the original app before installing the patched one.\nMake sure you have backed up personal data.</string>
|
||||
<string name="patch_install_successfully">Install successfully</string>
|
||||
<string name="patch_install_failed">Install failed</string>
|
||||
<string name="patch_no_xposed_module">No Xposed module(s) were found</string>
|
||||
|
||||
<!-- Select Apps Screen -->
|
||||
<string name="screen_select_apps">Select Apps</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue