diff --git a/build.gradle.kts b/build.gradle.kts index 3916d5c..9ce0ba1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ buildscript { } dependencies { classpath("org.eclipse.jgit:org.eclipse.jgit:6.0.0.202111291000-r") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") + classpath(kotlin("gradle-plugin", version = "1.7.0")) } } diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index 9b8ae56..1b71a54 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -38,7 +38,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.2.0-beta02" + kotlinCompilerExtensionVersion = "1.2.0" } sourceSets["main"].assets.srcDirs(rootProject.projectDir.resolve("out/assets")) @@ -78,22 +78,22 @@ dependencies { annotationProcessor("androidx.room:room-compiler:$roomVersion") compileOnly("dev.rikka.hidden:stub:2.3.1") implementation("dev.rikka.hidden:compat:2.3.1") - implementation("androidx.core:core-ktx:1.7.0") - implementation("androidx.activity:activity-compose:1.6.0-alpha03") - implementation("androidx.compose.material:material-icons-extended:1.2.0-beta02") - implementation("androidx.compose.material3:material3:1.0.0-alpha12") - implementation("androidx.compose.runtime:runtime-livedata:1.2.0-beta02") - implementation("androidx.compose.ui:ui:1.2.0-beta02") - implementation("androidx.compose.ui:ui-tooling:1.2.0-beta02") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-rc01") - implementation("androidx.navigation:navigation-compose:2.5.0-rc01") + implementation("androidx.core:core-ktx:1.8.0") + implementation("androidx.activity:activity-compose:1.6.0-alpha05") + implementation("androidx.compose.material:material-icons-extended:1.3.0-alpha01") + implementation("androidx.compose.material3:material3:1.0.0-alpha14") + implementation("androidx.compose.runtime:runtime-livedata:1.3.0-alpha01") + implementation("androidx.compose.ui:ui:1.3.0-alpha01") + implementation("androidx.compose.ui:ui-tooling:1.3.0-alpha01") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0") + implementation("androidx.navigation:navigation-compose:2.5.0") implementation("androidx.preference:preference:1.2.0") implementation("androidx.room:room-ktx:$roomVersion") implementation("androidx.room:room-runtime:$roomVersion") - implementation("com.google.accompanist:accompanist-drawablepainter:0.24.9-beta") - implementation("com.google.accompanist:accompanist-navigation-animation:0.24.9-beta") - implementation("com.google.accompanist:accompanist-swiperefresh:0.24.9-beta") - implementation("com.google.android.material:material:1.6.0") + implementation("com.google.accompanist:accompanist-drawablepainter:0.24.11-rc") + implementation("com.google.accompanist:accompanist-navigation-animation:0.24.11-rc") + implementation("com.google.accompanist:accompanist-swiperefresh:0.24.11-rc") + implementation("com.google.android.material:material:1.6.1") implementation("com.google.code.gson:gson:2.9.0") implementation("dev.rikka.shizuku:api:12.1.0") implementation("dev.rikka.shizuku:provider:12.1.0") diff --git a/manager/proguard-rules.pro b/manager/proguard-rules.pro index ef58967..811ea6c 100644 --- a/manager/proguard-rules.pro +++ b/manager/proguard-rules.pro @@ -1,5 +1,6 @@ -keep class com.beust.jcommander.** { *; } -keep class org.lsposed.lspatch.Patcher$Options { *; } +-keep class org.lsposed.lspatch.share.LSPConfig { *; } -keep class org.lsposed.lspatch.share.PatchConfig { *; } -keepclassmembers class org.lsposed.patch.LSPatch { private ; diff --git a/manager/src/main/java/org/lsposed/lspatch/Patcher.kt b/manager/src/main/java/org/lsposed/lspatch/Patcher.kt index 964f466..b48c998 100644 --- a/manager/src/main/java/org/lsposed/lspatch/Patcher.kt +++ b/manager/src/main/java/org/lsposed/lspatch/Patcher.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.withContext import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX import org.lsposed.lspatch.Constants.PREFS_STORAGE_DIRECTORY import org.lsposed.lspatch.config.MyKeyStore +import org.lsposed.lspatch.share.PatchConfig import org.lsposed.patch.LSPatch import org.lsposed.patch.util.Logger import java.io.IOException @@ -14,26 +15,21 @@ import java.io.IOException object Patcher { class Options( - private val apkPaths: List, - private val debuggable: Boolean, - private val sigbypassLevel: Int, - private val v1: Boolean, - private val v2: Boolean, - private val useManager: Boolean, - private val overrideVersionCode: Boolean, private val verbose: Boolean, + private val config: PatchConfig, + private val apkPaths: List, private val embeddedModules: List? ) { fun toStringArray(): Array { return buildList { addAll(apkPaths) add("-o"); add(lspApp.tmpApkDir.absolutePath) - if (debuggable) add("-d") - add("-l"); add(sigbypassLevel.toString()) - add("--v1"); add(v1.toString()) - add("--v2"); add(v2.toString()) - if (useManager) add("--manager") - if (overrideVersionCode) add("-r") + if (config.debuggable) add("-d") + add("-l"); add(config.sigBypassLevel.toString()) + add("--v1"); add(config.v1.toString()) + add("--v2"); add(config.v2.toString()) + if (config.useManager) add("--manager") + if (config.overrideVersionCode) add("-r") if (verbose) add("-v") embeddedModules?.forEach { add("-m"); add(it) diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/component/LoadingDialog.kt b/manager/src/main/java/org/lsposed/lspatch/ui/component/LoadingDialog.kt new file mode 100644 index 0000000..002780b --- /dev/null +++ b/manager/src/main/java/org/lsposed/lspatch/ui/component/LoadingDialog.kt @@ -0,0 +1,32 @@ +package org.lsposed.lspatch.ui.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Preview +@Composable +fun LoadingDialog() { + Dialog( + onDismissRequest = {}, + properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false) + ) { + Box( + modifier = Modifier + .size(100.dp) + .background(Color.White, shape = RoundedCornerShape(8.dp)), + contentAlignment = Alignment.Center, + content = { CircularProgressIndicator() } + ) + } +} diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/component/settings/Switch.kt b/manager/src/main/java/org/lsposed/lspatch/ui/component/settings/Switch.kt new file mode 100644 index 0000000..48a790a --- /dev/null +++ b/manager/src/main/java/org/lsposed/lspatch/ui/component/settings/Switch.kt @@ -0,0 +1,49 @@ +package org.lsposed.lspatch.ui.component.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Api +import androidx.compose.material3.Switch +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview + +@Composable +fun SettingsSwitch( + modifier: Modifier = Modifier, + checked: Boolean, + enabled: Boolean = true, + onClick: () -> Unit, + icon: ImageVector? = null, + title: String, + desc: String? = null, + extraContent: (@Composable ColumnScope.() -> Unit)? = null +) { + SettingsSlot(modifier, enabled, onClick, icon, title, desc, extraContent) { + Switch(checked = checked, onCheckedChange = { onClick() }) + } +} + +@Preview +@Composable +private fun SettingsCheckBoxPreview() { + var checked1 by remember { mutableStateOf(false) } + var checked2 by remember { mutableStateOf(false) } + Column { + SettingsSwitch( + checked = checked1, + onClick = { checked1 = !checked1 }, + title = "Title", + desc = "Description" + ) + SettingsSwitch( + checked = checked2, + onClick = { checked2 = !checked2 }, + icon = Icons.Outlined.Api, + title = "Title", + desc = "Description" + ) + } +} diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/page/ManagePage.kt b/manager/src/main/java/org/lsposed/lspatch/ui/page/ManagePage.kt index 1a07f5d..4e5a34e 100644 --- a/manager/src/main/java/org/lsposed/lspatch/ui/page/ManagePage.kt +++ b/manager/src/main/java/org/lsposed/lspatch/ui/page/ManagePage.kt @@ -1,6 +1,9 @@ package org.lsposed.lspatch.ui.page import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context import android.content.Intent import android.net.Uri import android.util.Log @@ -38,12 +41,15 @@ import org.lsposed.lspatch.R import org.lsposed.lspatch.config.ConfigManager import org.lsposed.lspatch.database.entity.Module import org.lsposed.lspatch.lspApp +import org.lsposed.lspatch.share.LSPConfig import org.lsposed.lspatch.ui.component.AppItem +import org.lsposed.lspatch.ui.component.LoadingDialog import org.lsposed.lspatch.ui.util.LocalNavController import org.lsposed.lspatch.ui.util.LocalSnackbarHost import org.lsposed.lspatch.ui.util.observeState import org.lsposed.lspatch.ui.util.setState import org.lsposed.lspatch.ui.viewmodel.ManageViewModel +import org.lsposed.lspatch.ui.viewmodel.ManageViewModel.ViewAction import org.lsposed.lspatch.util.LSPPackageManager import org.lsposed.lspatch.util.LSPPackageManager.AppInfo import org.lsposed.lspatch.util.ShizukuApi @@ -240,6 +246,27 @@ private fun Body() { navController.currentBackStackEntry!!.setState("isCancelled", null) } } + + if (viewModel.processingUpdate) LoadingDialog() + viewModel.updateLoaderResult?.let { + val updateSuccessfully = stringResource(R.string.manage_update_loader_successfully) + val updateFailed = stringResource(R.string.manage_update_loader_failed) + val copyError = stringResource(R.string.copy_error) + LaunchedEffect(Unit) { + it.onSuccess { + LSPPackageManager.fetchAppList() + snackbarHost.showSnackbar(updateSuccessfully) + }.onFailure { + val result = snackbarHost.showSnackbar(updateFailed, copyError) + if (result == SnackbarResult.ActionPerformed) { + val cm = lspApp.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + cm.setPrimaryClip(ClipData.newPlainText("LSPatch", it.toString())) + } + } + viewModel.dispatch(ViewAction.ClearUpdateLoaderResult) + } + } + LazyColumn { items( items = viewModel.appList, @@ -272,6 +299,22 @@ private fun Body() { } ) DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + val shizukuUnavailable = stringResource(R.string.shizuku_unavailable) + if (it.second.lspConfig.VERSION_CODE >= 319 && it.second.lspConfig.VERSION_CODE < LSPConfig.instance.VERSION_CODE) { + DropdownMenuItem( + text = { Text(stringResource(R.string.manage_update_loader)) }, + onClick = { + expanded = false + scope.launch { + if (!ShizukuApi.isPermissionGranted) { + snackbarHost.showSnackbar(shizukuUnavailable) + } else { + viewModel.dispatch(ViewAction.UpdateLoader(it.first, it.second)) + } + } + } + ) + } if (it.second.useManager) { DropdownMenuItem( text = { Text(stringResource(R.string.manage_module_scope)) }, @@ -291,19 +334,16 @@ private fun Body() { } ) } - val shizukuUnavailable = stringResource(R.string.shizuku_unavailable) val optimizeSucceed = stringResource(R.string.manage_optimize_successfully) val optimizeFailed = stringResource(R.string.manage_optimize_failed) DropdownMenuItem( text = { Text(stringResource(R.string.manage_optimize)) }, onClick = { expanded = false - if (!ShizukuApi.isPermissionGranted) { - scope.launch { + scope.launch { + if (!ShizukuApi.isPermissionGranted) { snackbarHost.showSnackbar(shizukuUnavailable) - } - } else { - scope.launch { + } else { val result = ShizukuApi.performDexOptMode(it.first.app.packageName) snackbarHost.showSnackbar(if (result) optimizeSucceed else optimizeFailed) } 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 cf3a9a4..1bc4244 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 @@ -81,7 +81,7 @@ fun NewPatchPage(from: String, entry: NavBackStackEntry) { } } LaunchedEffect(Unit) { - lspApp.tmpApkDir.listFiles()?.forEach(File::delete) + LSPPackageManager.cleanTmpApkDir() if (isCancelled == true) navController.popBackStack() else when (from) { "storage" -> storageLauncher.launch(arrayOf("application/vnd.android.package-archive")) @@ -323,7 +323,7 @@ private fun DoPatchBody(modifier: Modifier) { val shizukuUnavailable = stringResource(R.string.shizuku_unavailable) val installSuccessfully = stringResource(R.string.patch_install_successfully) val installFailed = stringResource(R.string.patch_install_failed) - val copyError = stringResource(R.string.patch_copy_error) + val copyError = stringResource(R.string.copy_error) var installing by remember { mutableStateOf(false) } if (installing) InstallDialog(viewModel.patchApp) { status, message -> scope.launch { @@ -377,7 +377,7 @@ private fun DoPatchBody(modifier: Modifier) { val cm = lspApp.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager cm.setPrimaryClip(ClipData.newPlainText("LSPatch", viewModel.logs.joinToString { it.second + "\n" })) }, - content = { Text(stringResource(R.string.patch_copy_error)) } + content = { Text(stringResource(R.string.copy_error)) } ) } } diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/ManageViewModel.kt b/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/ManageViewModel.kt index 623c87b..3447553 100644 --- a/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/ManageViewModel.kt +++ b/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/ManageViewModel.kt @@ -1,27 +1,113 @@ package org.lsposed.lspatch.ui.viewmodel +import android.content.pm.PackageInstaller import android.util.Base64 import android.util.Log import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.google.gson.Gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.lsposed.lspatch.Patcher +import org.lsposed.lspatch.lspApp +import org.lsposed.lspatch.share.Constants import org.lsposed.lspatch.share.PatchConfig import org.lsposed.lspatch.util.LSPPackageManager import org.lsposed.lspatch.util.LSPPackageManager.AppInfo +import org.lsposed.patch.util.Logger +import java.io.FileNotFoundException +import java.util.zip.ZipFile private const val TAG = "ManageViewModel" class ManageViewModel : ViewModel() { + sealed class ViewAction { + data class UpdateLoader(val appInfo: AppInfo, val config: PatchConfig) : ViewAction() + object ClearUpdateLoaderResult : ViewAction() + } + val appList: List> by derivedStateOf { LSPPackageManager.appList.mapNotNull { appInfo -> appInfo.app.metaData?.getString("lspatch")?.let { val json = Base64.decode(it, Base64.DEFAULT).toString(Charsets.UTF_8) + Log.d(TAG, "Read patched config: $json") appInfo to Gson().fromJson(json, PatchConfig::class.java) } }.also { Log.d(TAG, "Loaded ${it.size} patched apps") } } + + var processingUpdate by mutableStateOf(false) + private set + var updateLoaderResult: Result? by mutableStateOf(null) + private set + + private val logger = object : Logger() { + override fun d(msg: String) { + if (verbose) Log.d(TAG, msg) + } + + override fun i(msg: String) { + Log.i(TAG, msg) + } + + override fun e(msg: String) { + Log.e(TAG, msg) + } + } + + fun dispatch(action: ViewAction) { + when (action) { + is ViewAction.UpdateLoader -> updateLoader(action.appInfo, action.config) + is ViewAction.ClearUpdateLoaderResult -> updateLoaderResult = null + } + } + + private fun updateLoader(appInfo: AppInfo, config: PatchConfig) { + Log.i(TAG, "Update loader for ${appInfo.app.packageName}") + viewModelScope.launch { + processingUpdate = true + val result = runCatching { + withContext(Dispatchers.IO) { + LSPPackageManager.cleanTmpApkDir() + val apkPaths = listOf(appInfo.app.sourceDir) + (appInfo.app.splitSourceDirs ?: emptyArray()) + val patchPaths = mutableListOf() + val embeddedModulePaths = if (config.useManager) emptyList() else null + for (apk in apkPaths) { + ZipFile(apk).use { zip -> + var entry = zip.getEntry(Constants.ORIGINAL_APK_ASSET_PATH) + if (entry == null) entry = zip.getEntry("assets/lspatch/origin_apk.bin") + if (entry == null) throw FileNotFoundException("Original apk entry not found for $apk") + zip.getInputStream(entry).use { input -> + val dst = lspApp.tmpApkDir.resolve(apk.substringAfterLast('/')) + patchPaths.add(dst.absolutePath) + dst.outputStream().use { output -> + input.copyTo(output) + } + } + } + } + Patcher.patch( + logger, Patcher.Options( + verbose = true, + config = config, + apkPaths = patchPaths, + embeddedModules = embeddedModulePaths + ) + ) + val (status, message) = LSPPackageManager.install() + if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message) + } + } + processingUpdate = false + updateLoaderResult = result + } + } } diff --git a/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/NewPatchViewModel.kt b/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/NewPatchViewModel.kt index 98c2f4d..700f29c 100644 --- a/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/NewPatchViewModel.kt +++ b/manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/NewPatchViewModel.kt @@ -10,10 +10,10 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import org.lsposed.lspatch.Patcher -import org.lsposed.lspatch.lspApp +import org.lsposed.lspatch.share.PatchConfig +import org.lsposed.lspatch.util.LSPPackageManager import org.lsposed.lspatch.util.LSPPackageManager.AppInfo import org.lsposed.patch.util.Logger -import java.io.File private const val TAG = "NewPatchViewModel" @@ -82,13 +82,9 @@ class NewPatchViewModel : ViewModel() { Log.d(TAG, "Submit patch") if (useManager) embeddedModules.clear() patchOptions = Patcher.Options( - apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()), - debuggable = debuggable, - sigbypassLevel = sigBypassLevel, - v1 = sign[0], v2 = sign[1], - useManager = useManager, - overrideVersionCode = overrideVersionCode, verbose = true, + config = PatchConfig(useManager, debuggable, overrideVersionCode, sign[0], sign[1], sigBypassLevel, null, null), + apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()), embeddedModules = embeddedModules.flatMap { listOf(it.app.sourceDir) + (it.app.splitSourceDirs ?: emptyArray()) } ) patchState = PatchState.PATCHING @@ -105,7 +101,7 @@ class NewPatchViewModel : ViewModel() { logger.e(t.stackTraceToString()) PatchState.ERROR } finally { - lspApp.tmpApkDir.listFiles()?.forEach(File::delete) + LSPPackageManager.cleanTmpApkDir() } } } 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 b1d13cd..f53bd14 100644 --- a/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt +++ b/manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt @@ -68,7 +68,14 @@ object LSPPackageManager { fun getIcon(appInfo: AppInfo) = appIcon[appInfo.app.packageName]!! + suspend fun cleanTmpApkDir() { + withContext(Dispatchers.IO) { + lspApp.tmpApkDir.listFiles()?.forEach(File::delete) + } + } + suspend fun install(): Pair { + Log.i(TAG, "Perform install patched apks") var status = PackageInstaller.STATUS_FAILURE var message: String? = null withContext(Dispatchers.IO) { @@ -85,6 +92,7 @@ object LSPPackageManager { ?: throw IOException("DocumentFile is null") root.listFiles().forEach { file -> if (file.name?.endsWith(PATCH_FILE_SUFFIX) != true) return@forEach + Log.d(TAG, "Add ${file.name}") val input = lspApp.contentResolver.openInputStream(file.uri) ?: throw IOException("Cannot open input stream") input.use { @@ -180,7 +188,7 @@ object LSPPackageManager { } AppInfo(app, app.packageName) }.recoverCatching { t -> - lspApp.tmpApkDir.listFiles()?.forEach(File::delete) + cleanTmpApkDir() Log.e(TAG, "Failed to load apks", t) throw t } diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index c121e63..7a5b9ef 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -4,6 +4,7 @@ Installing Uninstall Uninstalling + Copy error Shizuku service available Shizuku service not connected Repo @@ -26,6 +27,9 @@ Manage Loading No patched apps yet + Update loader + Update successfully + Update failed Module scope Optimize Optimize successfully @@ -58,7 +62,6 @@ 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 - Copy error Select Apps diff --git a/patch/src/main/java/org/lsposed/patch/LSPatch.java b/patch/src/main/java/org/lsposed/patch/LSPatch.java index 9b5e6d5..234bff6 100644 --- a/patch/src/main/java/org/lsposed/patch/LSPatch.java +++ b/patch/src/main/java/org/lsposed/patch/LSPatch.java @@ -216,16 +216,11 @@ public class LSPatch { throw new PatchError("Failed to register signer", e); } - String originalSignature = null; - if (sigbypassLevel > 0) { - // save the apk original signature info, to support crack signature. - originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath()); - if (originalSignature == null || originalSignature.isEmpty()) { - throw new PatchError("get original signature failed"); - } - - logger.d("Original signature\n" + originalSignature); + final String originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath()); + if (originalSignature == null || originalSignature.isEmpty()) { + throw new PatchError("get original signature failed"); } + logger.d("Original signature\n" + originalSignature); // copy out manifest file from zlib var manifestEntry = srcZFile.get(ANDROID_MANIFEST_XML); @@ -233,7 +228,7 @@ public class LSPatch { throw new PatchError("Provided file is not a valid apk"); // parse the app appComponentFactory full name from the manifest file - String appComponentFactory; + final String appComponentFactory; try (var is = manifestEntry.open()) { var pair = ManifestParser.parseManifestFile(is); if (pair == null) @@ -244,9 +239,9 @@ public class LSPatch { logger.i("Patching apk..."); // modify manifest - var config = new PatchConfig(useManager, sigbypassLevel, null, appComponentFactory); - var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8); - var metadata = Base64.getEncoder().encodeToString(configBytes); + final var config = new PatchConfig(useManager, debuggableFlag, overrideVersionCode, v1, v2, sigbypassLevel, originalSignature, appComponentFactory); + final var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8); + final var metadata = Base64.getEncoder().encodeToString(configBytes); try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open(), metadata))) { dstZFile.add(ANDROID_MANIFEST_XML, is); } catch (Throwable e) { @@ -283,8 +278,6 @@ public class LSPatch { } // save lspatch config to asset.. - config = new PatchConfig(useManager, sigbypassLevel, originalSignature, appComponentFactory); - configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8); try (var is = new ByteArrayInputStream(configBytes)) { dstZFile.add(CONFIG_ASSET_PATH, is); } catch (Throwable e) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 079a398..deb9b79 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ pluginManagement { plugins { id("com.android.library") version agpVersion id("com.android.application") version agpVersion - id("com.google.devtools.ksp") version "1.6.21-1.0.5" + id("com.google.devtools.ksp") version "1.7.0-1.0.6" id("dev.rikka.tools.refine") version "3.1.1" } } diff --git a/share/java/src/main/java/org/lsposed/lspatch/share/PatchConfig.java b/share/java/src/main/java/org/lsposed/lspatch/share/PatchConfig.java index 60cb892..3119f68 100644 --- a/share/java/src/main/java/org/lsposed/lspatch/share/PatchConfig.java +++ b/share/java/src/main/java/org/lsposed/lspatch/share/PatchConfig.java @@ -3,13 +3,30 @@ package org.lsposed.lspatch.share; public class PatchConfig { public final boolean useManager; + public final boolean debuggable; + public final boolean overrideVersionCode; + public final boolean v1; + public final boolean v2; public final int sigBypassLevel; public final String originalSignature; public final String appComponentFactory; public final LSPConfig lspConfig; - public PatchConfig(boolean useManager, int sigBypassLevel, String originalSignature, String appComponentFactory) { + public PatchConfig( + boolean useManager, + boolean debuggable, + boolean overrideVersionCode, + boolean v1, + boolean v2, + int sigBypassLevel, + String originalSignature, + String appComponentFactory + ) { this.useManager = useManager; + this.debuggable = debuggable; + this.overrideVersionCode = overrideVersionCode; + this.v1 = v1; + this.v2 = v2; this.sigBypassLevel = sigBypassLevel; this.originalSignature = originalSignature; this.appComponentFactory = appComponentFactory;