Skip to content

Commit 7acc806

Browse files
committed
Enhance installation and cleanup processes(#29)
1 parent b98eaf8 commit 7acc806

File tree

9 files changed

+168
-17
lines changed

9 files changed

+168
-17
lines changed

manager/src/main/AndroidManifest.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
android:name="android.permission.QUERY_ALL_PACKAGES"
77
tools:ignore="QueryAllPackagesPermission" />
88
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
9+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
910

1011
<application
1112
android:name=".LSPApplication"
@@ -44,6 +45,16 @@
4445
android:exported="true"
4546
android:multiprocess="false"
4647
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
48+
49+
<provider
50+
android:name="androidx.core.content.FileProvider"
51+
android:authorities="${applicationId}.fileprovider"
52+
android:exported="false"
53+
android:grantUriPermissions="true">
54+
<meta-data
55+
android:name="android.support.FILE_PROVIDER_PATHS"
56+
android:resource="@xml/file_paths" />
57+
</provider>
4758
</application>
4859

4960
</manifest>

manager/src/main/java/org/lsposed/lspatch/LSPApplication.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class LSPApplication : Application() {
1818

1919
lateinit var prefs: SharedPreferences
2020
lateinit var tmpApkDir: File
21+
lateinit var targetApkFile: File
2122

2223
val globalScope = CoroutineScope(Dispatchers.Default)
2324

manager/src/main/java/org/lsposed/lspatch/Patcher.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import org.lsposed.lspatch.share.Constants
1010
import org.lsposed.lspatch.share.PatchConfig
1111
import org.lsposed.patch.LSPatch
1212
import org.lsposed.patch.util.Logger
13+
import java.io.File
1314
import java.io.IOException
14-
import java.util.Collections.addAll
1515

1616
object Patcher {
1717

@@ -59,6 +59,9 @@ object Patcher {
5959
?: throw IOException("Failed to create output file")
6060
val output = lspApp.contentResolver.openOutputStream(file.uri)
6161
?: throw IOException("Failed to open output stream")
62+
val apkFile = File(lspApp.externalCacheDir, apk.name)
63+
apk.copyTo(apkFile, overwrite = true)
64+
lspApp.targetApkFile = apkFile
6265
output.use {
6366
apk.inputStream().use { input ->
6467
input.copyTo(output)

manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ import org.lsposed.lspatch.ui.component.settings.SettingsCheckBox
4848
import org.lsposed.lspatch.ui.component.settings.SettingsItem
4949
import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination
5050
import org.lsposed.lspatch.ui.util.LocalSnackbarHost
51+
import org.lsposed.lspatch.ui.util.checkIsApkFixedByLSP
52+
import org.lsposed.lspatch.ui.util.installApk
5153
import org.lsposed.lspatch.ui.util.isScrolledToEnd
5254
import org.lsposed.lspatch.ui.util.lastItemIndex
55+
import org.lsposed.lspatch.ui.util.uninstallApkByPackageName
5356
import org.lsposed.lspatch.ui.viewmodel.NewPatchViewModel
5457
import org.lsposed.lspatch.ui.viewmodel.NewPatchViewModel.PatchState
5558
import org.lsposed.lspatch.ui.viewmodel.NewPatchViewModel.ViewAction
@@ -121,7 +124,10 @@ fun NewPatchScreen(
121124
when (viewModel.patchState) {
122125
PatchState.INIT -> {
123126
LaunchedEffect(Unit) {
124-
LSPPackageManager.cleanTmpApkDir()
127+
LSPPackageManager.apply {
128+
cleanTmpApkDir()
129+
cleanExternalTmpApkDir()
130+
}
125131
when (id) {
126132
ACTION_STORAGE -> {
127133
storageLauncher.launch(arrayOf("application/vnd.android.package-archive"))
@@ -435,10 +441,10 @@ private fun DoPatchBody(modifier: Modifier, navigator: DestinationsNavigator) {
435441
val installSuccessfully = stringResource(R.string.patch_install_successfully)
436442
val installFailed = stringResource(R.string.patch_install_failed)
437443
val copyError = stringResource(R.string.copy_error)
438-
var installing by remember { mutableStateOf(false) }
439-
if (installing) InstallDialog(viewModel.patchApp) { status, message ->
444+
var installing by remember { mutableStateOf(0) }
445+
val onFinish: (Int, String?) -> Unit = { status, message ->
440446
scope.launch {
441-
installing = false
447+
installing = 0
442448
if (status == PackageInstaller.STATUS_SUCCESS) {
443449
lspApp.globalScope.launch { snackbarHost.showSnackbar(installSuccessfully) }
444450
navigator.navigateUp()
@@ -451,6 +457,7 @@ private fun DoPatchBody(modifier: Modifier, navigator: DestinationsNavigator) {
451457
}
452458
}
453459
}
460+
if (installing == 1) InstallDialog(viewModel.patchApp, onFinish) else if (installing == 2) InstallDialog2(viewModel.patchApp, onFinish)
454461
Row(Modifier.padding(top = 12.dp)) {
455462
Button(
456463
modifier = Modifier.weight(1f),
@@ -462,11 +469,9 @@ private fun DoPatchBody(modifier: Modifier, navigator: DestinationsNavigator) {
462469
modifier = Modifier.weight(1f),
463470
onClick = {
464471
if (!ShizukuApi.isPermissionGranted) {
465-
scope.launch {
466-
snackbarHost.showSnackbar(shizukuUnavailable)
467-
}
472+
installing = 2
468473
} else {
469-
installing = true
474+
installing = 1
470475
}
471476
},
472477
content = { Text(stringResource(R.string.install)) }
@@ -572,3 +577,71 @@ private fun InstallDialog(patchApp: AppInfo, onFinish: (Int, String?) -> Unit) {
572577
)
573578
}
574579
}
580+
581+
@Composable
582+
private fun InstallDialog2(patchApp: AppInfo, onFinish: (Int, String?) -> Unit) {
583+
val scope = rememberCoroutineScope()
584+
var uninstallFirst by remember {
585+
mutableStateOf(
586+
checkIsApkFixedByLSP(
587+
lspApp,
588+
patchApp.app.packageName
589+
)
590+
)
591+
}
592+
593+
fun doInstall() {
594+
Log.i(TAG, "Installing app ${patchApp.app.packageName}")
595+
installApk(lspApp, lspApp.targetApkFile)
596+
}
597+
598+
LaunchedEffect(Unit) {
599+
if (!uninstallFirst) {
600+
onFinish(LSPPackageManager.STATUS_USER_CANCELLED, "User cancelled")
601+
doInstall()
602+
}
603+
}
604+
605+
if (uninstallFirst) {
606+
AlertDialog(
607+
onDismissRequest = {
608+
onFinish(
609+
LSPPackageManager.STATUS_USER_CANCELLED,
610+
"User cancelled"
611+
)
612+
},
613+
confirmButton = {
614+
TextButton(
615+
onClick = {
616+
onFinish(LSPPackageManager.STATUS_USER_CANCELLED, "Reset")
617+
scope.launch {
618+
Log.i(TAG, "Uninstalling app ${patchApp.app.packageName}")
619+
uninstallApkByPackageName(lspApp, patchApp.app.packageName)
620+
uninstallFirst = false
621+
}
622+
},
623+
content = { Text(stringResource(android.R.string.ok)) }
624+
)
625+
},
626+
dismissButton = {
627+
TextButton(
628+
onClick = {
629+
onFinish(
630+
LSPPackageManager.STATUS_USER_CANCELLED,
631+
"User cancelled"
632+
)
633+
},
634+
content = { Text(stringResource(android.R.string.cancel)) }
635+
)
636+
},
637+
title = {
638+
Text(
639+
modifier = Modifier.fillMaxWidth(),
640+
text = stringResource(R.string.uninstall),
641+
textAlign = TextAlign.Center
642+
)
643+
},
644+
text = { Text(stringResource(R.string.patch_uninstall_text)) }
645+
)
646+
}
647+
}

manager/src/main/java/org/lsposed/lspatch/ui/page/manage/AppManagePage.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,7 @@ fun AppManageBody(
192192
onClick = {
193193
expanded = false
194194
scope.launch {
195-
if (!ShizukuApi.isPermissionGranted) {
196-
snackbarHost.showSnackbar(shizukuUnavailable)
197-
} else {
198-
viewModel.dispatch(AppManageViewModel.ViewAction.UpdateLoader(it.first, it.second))
199-
}
195+
viewModel.dispatch(AppManageViewModel.ViewAction.UpdateLoader(it.first, it.second))
200196
}
201197
}
202198
)

manager/src/main/java/org/lsposed/lspatch/ui/util/Utils.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package org.lsposed.lspatch.ui.util
22

3+
import android.content.Context
4+
import android.content.Intent
5+
import android.content.pm.PackageManager
6+
import android.util.Log
37
import androidx.compose.foundation.lazy.LazyListState
8+
import androidx.core.content.FileProvider
9+
import androidx.core.net.toUri
10+
import java.io.File
411

512
val LazyListState.lastVisibleItemIndex
613
get() = layoutInfo.visibleItemsInfo.lastOrNull()?.index
@@ -10,3 +17,44 @@ val LazyListState.lastItemIndex
1017

1118
val LazyListState.isScrolledToEnd
1219
get() = lastVisibleItemIndex == lastItemIndex
20+
21+
fun checkIsApkFixedByLSP(context: Context, packageName: String): Boolean {
22+
return try {
23+
val app =
24+
context.packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
25+
(app.metaData?.containsKey("lspatch") != true)
26+
} catch (_: PackageManager.NameNotFoundException) {
27+
Log.e("LSPatch", "Package not found: $packageName")
28+
false
29+
} catch (e: Exception) {
30+
Log.e("LSPatch", "Unexpected error in checkIsApkFixedByLSP", e)
31+
false
32+
}
33+
}
34+
35+
fun installApk(context: Context, apkFile: File) {
36+
try {
37+
val apkUri =
38+
FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", apkFile)
39+
40+
val intent = Intent(Intent.ACTION_VIEW).apply {
41+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
42+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
43+
addCategory("android.intent.category.DEFAULT")
44+
setDataAndType(apkUri, "application/vnd.android.package-archive")
45+
}
46+
context.startActivity(intent)
47+
} catch (e: Exception) {
48+
Log.e("LSPatch", "installApk", e)
49+
}
50+
}
51+
52+
fun uninstallApkByPackageName(context: Context, packageName: String) = try {
53+
val intent = Intent(Intent.ACTION_DELETE).apply {
54+
data = "package:$packageName".toUri()
55+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
56+
}
57+
context.startActivity(intent)
58+
} catch (e: Exception) {
59+
Log.e("LSPatch", "uninstallApkByPackageName", e)
60+
}

manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/manage/AppManageViewModel.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.lsposed.lspatch.Patcher
1717
import org.lsposed.lspatch.lspApp
1818
import org.lsposed.lspatch.share.Constants
1919
import org.lsposed.lspatch.share.PatchConfig
20+
import org.lsposed.lspatch.ui.util.installApk
2021
import org.lsposed.lspatch.ui.viewstate.ProcessingState
2122
import org.lsposed.lspatch.util.LSPPackageManager
2223
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
@@ -86,7 +87,10 @@ class AppManageViewModel : ViewModel() {
8687
updateLoaderState = ProcessingState.Processing
8788
val result = runCatching {
8889
withContext(Dispatchers.IO) {
89-
LSPPackageManager.cleanTmpApkDir()
90+
LSPPackageManager.apply {
91+
cleanTmpApkDir()
92+
cleanExternalTmpApkDir()
93+
}
9094
val apkPaths = listOf(appInfo.app.sourceDir) + (appInfo.app.splitSourceDirs ?: emptyArray())
9195
val patchPaths = mutableListOf<String>()
9296
val embeddedModulePaths = mutableListOf<String>()
@@ -118,8 +122,12 @@ class AppManageViewModel : ViewModel() {
118122
}
119123
}
120124
Patcher.patch(logger, Patcher.Options(false, config, patchPaths, embeddedModulePaths))
121-
val (status, message) = LSPPackageManager.install()
122-
if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message)
125+
if (!ShizukuApi.isPermissionGranted) {
126+
installApk(lspApp, lspApp.targetApkFile)
127+
} else {
128+
val (status, message) = LSPPackageManager.install()
129+
if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message)
130+
}
123131
}
124132
}
125133
updateLoaderState = ProcessingState.Done(result)

manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ object LSPPackageManager {
7979
}
8080
}
8181

82+
suspend fun cleanExternalTmpApkDir(){
83+
withContext(Dispatchers.IO) {
84+
lspApp.externalCacheDir?.listFiles()?.forEach(File::delete)
85+
}
86+
}
87+
8288
suspend fun install(): Pair<Int, String?> {
8389
Log.i(TAG, "Perform install patched apks")
8490
var status = PackageInstaller.STATUS_FAILURE
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<paths>
3+
<external-path name="external_files" path="." />
4+
<files-path name="files" path="." />
5+
</paths>

0 commit comments

Comments
 (0)