diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt b/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt index 57f0dfc9..09bdc02e 100644 --- a/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt +++ b/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt @@ -68,6 +68,7 @@ fun NewPatchScreen( ) { val viewModel = viewModel() 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 + ) + } + } + }) + } } } } diff --git a/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt b/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt index 12f7a7ba..5830b792 100644 --- a/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt +++ b/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt @@ -152,11 +152,12 @@ object LSPPackageManager { return Pair(status, message) } - suspend fun getAppInfoFromApks(apks: List): Result { + suspend fun getAppInfoFromApks(apks: List): Result> { return withContext(Dispatchers.IO) { runCatching { var primary: ApplicationInfo? = null - val splits = apks.mapNotNull { uri -> + val splits = mutableListOf() + 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) } } + + 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 = lspApp.packageManager.getPackageArchiveInfo(dst.absolutePath, 0)?.applicationInfo - primary?.let { - it.sourceDir = dst.absolutePath - return@mapNotNull null - } + primary = appInfo } - dst.absolutePath + 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) diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index 8fdd5840..605f9b39 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Repo Logs Off + Unknown error Some functions unavailable @@ -68,6 +69,7 @@ Due to different signatures, you need to uninstall the original app before installing the patched one.\nMake sure you have backed up personal data. Install successfully Install failed + No Xposed module(s) were found Select Apps