diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index 4ac69c2..586087d 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -82,17 +82,18 @@ dependencies { implementation(projects.patch) implementation("androidx.core:core-ktx:1.7.0") - implementation("androidx.activity:activity-compose:1.5.0-alpha01") - implementation("androidx.compose.material:material-icons-extended:1.0.5") - implementation("androidx.compose.material3:material3:1.0.0-alpha04") + implementation("androidx.activity:activity-compose:1.5.0-alpha02") + implementation("androidx.compose.material:material-icons-extended:1.1.0") + implementation("androidx.compose.material3:material3:1.0.0-alpha05") implementation("androidx.compose.runtime:runtime-livedata:$composeVersion") implementation("androidx.compose.ui:ui:$composeVersion") implementation("androidx.compose.ui:ui-tooling:$composeVersion") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha01") - implementation("androidx.navigation:navigation-compose:2.5.0-alpha01") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha02") + implementation("androidx.navigation:navigation-compose:2.5.0-alpha02") implementation("androidx.preference:preference:1.2.0") implementation("com.google.accompanist:accompanist-drawablepainter:0.24.2-alpha") implementation("com.google.accompanist:accompanist-navigation-animation:0.24.2-alpha") + implementation("com.google.accompanist:accompanist-permissions:0.24.2-alpha") implementation("com.google.accompanist:accompanist-swiperefresh:0.24.2-alpha") implementation("com.google.android.material:material:1.5.0") } diff --git a/manager/src/main/java/org/lsposed/lspatch/Patcher.kt b/manager/src/main/java/org/lsposed/lspatch/Patcher.kt index 8afb1a3..eaf3897 100644 --- a/manager/src/main/java/org/lsposed/lspatch/Patcher.kt +++ b/manager/src/main/java/org/lsposed/lspatch/Patcher.kt @@ -52,14 +52,23 @@ object Patcher { suspend fun patch(context: Context, logger: Logger, options: Options) { withContext(Dispatchers.IO) { val download = "${Environment.DIRECTORY_DOWNLOADS}/LSPatch" + val externalStorageDir = Environment.getExternalStoragePublicDirectory(download) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) externalStorageDir.mkdirs() options.outputPath = Files.createTempDirectory("patch").absolutePathString() + LSPatch(logger, *options.toStringArray()).doCommandLine() + File(options.outputPath) .walk() .filter { it.isFile } .forEach { - //FIXME: Android 9 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + it.inputStream().use { input -> + externalStorageDir.resolve(it.name).outputStream().use { output -> + input.copyTo(output) + } + } + } else { val contentDetails = ContentValues().apply { put(MediaStore.Downloads.DISPLAY_NAME, it.name) put(MediaStore.Downloads.RELATIVE_PATH, download) diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchPage.kt b/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchPage.kt index 3716f5e..ebdb44e 100644 --- a/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchPage.kt +++ b/manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchPage.kt @@ -1,5 +1,6 @@ package org.lsposed.lspatch.ui.page +import android.os.Build import android.util.Log import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring @@ -30,6 +31,9 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.PermissionStatus +import com.google.accompanist.permissions.rememberPermissionState import org.lsposed.lspatch.Patcher import org.lsposed.lspatch.R import org.lsposed.lspatch.TAG @@ -66,6 +70,7 @@ fun NewPatchFab() { } } +@OptIn(ExperimentalPermissionsApi::class) @Composable fun NewPatchPage() { val viewModel = viewModel() @@ -73,6 +78,28 @@ fun NewPatchPage() { val patchApp by navController.currentBackStackEntry!!.observeState("appInfo") if (viewModel.patchState == PatchState.SELECTING && patchApp != null) viewModel.patchState = PatchState.CONFIGURING + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val filePermissionState = rememberPermissionState(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (filePermissionState.status is PermissionStatus.Denied) { + AlertDialog( + onDismissRequest = {}, + confirmButton = { + TextButton(onClick = { filePermissionState.launchPermissionRequest() }) { + Text(stringResource(android.R.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = { navController.popBackStack() }) { + Text(stringResource(android.R.string.cancel)) + } + }, + title = { Text(stringResource(R.string.patch_permission_title)) }, + text = { Text(stringResource(R.string.patch_permission_text)) } + ) + return + } + } + Log.d(TAG, "NewPatchPage: ${viewModel.patchState}") when (viewModel.patchState) { PatchState.SELECTING -> navController.navigate(PageList.SelectApps.name + "/false") diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index 2e07c69..e72c798 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -10,6 +10,8 @@ New Patch + Permission + Please grant Write External Storage permission to allow saving patched apks to Download directory Patch Mode Local Patch an app without modules embedded.\nThe patched app need the manager running in background, and Xposed scope can be changed dynamically without re-patch.\nLocal patched apps can only run on the local device.