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 viewModel = viewModel<NewPatchViewModel>()
|
||||||
val snackbarHost = LocalSnackbarHost.current
|
val snackbarHost = LocalSnackbarHost.current
|
||||||
|
val errorUnknown = stringResource(R.string.error_unknown)
|
||||||
val storageLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { apks ->
|
val storageLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { apks ->
|
||||||
if (apks.isEmpty()) {
|
if (apks.isEmpty()) {
|
||||||
navigator.navigateUp()
|
navigator.navigateUp()
|
||||||
|
|
@ -76,15 +77,40 @@ fun NewPatchScreen(
|
||||||
runBlocking {
|
runBlocking {
|
||||||
LSPPackageManager.getAppInfoFromApks(apks)
|
LSPPackageManager.getAppInfoFromApks(apks)
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
viewModel.dispatch(ViewAction.ConfigurePatch(it))
|
viewModel.dispatch(ViewAction.ConfigurePatch(it.first()))
|
||||||
}
|
}
|
||||||
.onFailure {
|
.onFailure {
|
||||||
lspApp.globalScope.launch { snackbarHost.showSnackbar(it.message ?: "Unknown error") }
|
lspApp.globalScope.launch { snackbarHost.showSnackbar(it.message ?: errorUnknown) }
|
||||||
navigator.navigateUp()
|
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}")
|
Log.d(TAG, "PatchState: ${viewModel.patchState}")
|
||||||
when (viewModel.patchState) {
|
when (viewModel.patchState) {
|
||||||
PatchState.INIT -> {
|
PatchState.INIT -> {
|
||||||
|
|
@ -128,7 +154,7 @@ fun NewPatchScreen(
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
if (viewModel.patchState == PatchState.CONFIGURING) {
|
if (viewModel.patchState == PatchState.CONFIGURING) {
|
||||||
PatchOptionsBody(Modifier.padding(innerPadding)) {
|
PatchOptionsBody(Modifier.padding(innerPadding)) {
|
||||||
navigator.navigate(SelectAppsScreenDestination(true, viewModel.embeddedModules.mapTo(ArrayList()) { it.app.packageName }))
|
showSelectModuleDialog = true
|
||||||
}
|
}
|
||||||
resultRecipient.onNavResult {
|
resultRecipient.onNavResult {
|
||||||
if (it is NavResult.Value) {
|
if (it is NavResult.Value) {
|
||||||
|
|
@ -140,6 +166,53 @@ fun NewPatchScreen(
|
||||||
DoPatchBody(Modifier.padding(innerPadding), navigator)
|
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)
|
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) {
|
return withContext(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
var primary: ApplicationInfo? = null
|
var primary: ApplicationInfo? = null
|
||||||
val splits = apks.mapNotNull { uri ->
|
val splits = mutableListOf<String>()
|
||||||
|
val appInfos = apks.mapNotNull { uri ->
|
||||||
val src = DocumentFile.fromSingleUri(lspApp, uri)
|
val src = DocumentFile.fromSingleUri(lspApp, uri)
|
||||||
?: throw IOException("DocumentFile is null")
|
?: throw IOException("DocumentFile is null")
|
||||||
val dst = lspApp.tmpApkDir.resolve(src.name!!)
|
val dst = lspApp.tmpApkDir.resolve(src.name!!)
|
||||||
|
|
@ -167,21 +168,25 @@ object LSPPackageManager {
|
||||||
input.copyTo(output)
|
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
|
// TODO: Check selected apks are from the same app
|
||||||
if (primary == null) throw IllegalArgumentException("No primary apk")
|
primary?.splitSourceDirs = splits.toTypedArray()
|
||||||
val label = lspApp.packageManager.getApplicationLabel(primary!!).toString()
|
if (appInfos.isEmpty()) throw IOException("No apks")
|
||||||
if (splits.isNotEmpty()) primary!!.splitSourceDirs = splits.toTypedArray()
|
appInfos
|
||||||
AppInfo(primary!!, label)
|
|
||||||
}.recoverCatching { t ->
|
}.recoverCatching { t ->
|
||||||
cleanTmpApkDir()
|
cleanTmpApkDir()
|
||||||
Log.e(TAG, "Failed to load apks", t)
|
Log.e(TAG, "Failed to load apks", t)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
<string name="screen_repo">Repo</string>
|
<string name="screen_repo">Repo</string>
|
||||||
<string name="screen_logs">Logs</string>
|
<string name="screen_logs">Logs</string>
|
||||||
<string name="off">Off</string>
|
<string name="off">Off</string>
|
||||||
|
<string name="error_unknown">Unknown error</string>
|
||||||
|
|
||||||
<!-- Home Screen -->
|
<!-- Home Screen -->
|
||||||
<string name="home_shizuku_warning">Some functions unavailable</string>
|
<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_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_successfully">Install successfully</string>
|
||||||
<string name="patch_install_failed">Install failed</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 -->
|
<!-- Select Apps Screen -->
|
||||||
<string name="screen_select_apps">Select Apps</string>
|
<string name="screen_select_apps">Select Apps</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue