Fix saving content (1/2)

This commit is contained in:
Nullptr 2022-02-20 14:30:18 +08:00
parent a23553e8ff
commit 0bd40ca4dd
3 changed files with 53 additions and 24 deletions

View File

@ -1,14 +1,21 @@
package org.lsposed.lspatch package org.lsposed.lspatch
import android.content.ContentValues
import android.content.Context
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.lsposed.patch.LSPatch import org.lsposed.patch.LSPatch
import org.lsposed.patch.util.Logger import org.lsposed.patch.util.Logger
import java.io.File
import java.nio.file.Files
import kotlin.io.path.absolutePathString
object Patcher { object Patcher {
class Options( class Options(
private val apkPaths: Array<String>, private val apkPaths: Array<String>,
private val outputPath: String,
private val debuggable: Boolean, private val debuggable: Boolean,
private val sigbypassLevel: Int, private val sigbypassLevel: Int,
private val v1: Boolean, private val v1: Boolean,
@ -19,9 +26,10 @@ object Patcher {
private val verbose: Boolean, private val verbose: Boolean,
private val embeddedModules: List<String> private val embeddedModules: List<String>
) { ) {
lateinit var outputPath: String
fun toStringArray(): Array<String> { fun toStringArray(): Array<String> {
return arrayListOf<String>().run { return arrayListOf<String>().run {
add("-f")
addAll(apkPaths) addAll(apkPaths)
add("-o"); add(outputPath) add("-o"); add(outputPath)
if (debuggable) add("-d") if (debuggable) add("-d")
@ -41,9 +49,31 @@ object Patcher {
} }
} }
suspend fun patch(logger: Logger, options: Options) { suspend fun patch(context: Context, logger: Logger, options: Options) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val download = "${Environment.DIRECTORY_DOWNLOADS}/LSPatch"
options.outputPath = Files.createTempDirectory("patch").absolutePathString()
LSPatch(logger, *options.toStringArray()).doCommandLine() 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) {
val contentDetails = ContentValues().apply {
put(MediaStore.Downloads.DISPLAY_NAME, it.name)
put(MediaStore.Downloads.RELATIVE_PATH, download)
}
val uri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentDetails)
?: throw IllegalStateException("Failed to save files to Download")
it.inputStream().use { input ->
context.contentResolver.openOutputStream(uri)!!.use { output ->
input.copyTo(output)
}
}
}
}
logger.i("Patched files are saved to $download")
} }
} }
} }

View File

@ -1,6 +1,5 @@
package org.lsposed.lspatch.ui.page package org.lsposed.lspatch.ui.page
import android.os.Environment
import android.util.Log import android.util.Log
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring import androidx.compose.animation.core.Spring
@ -25,6 +24,7 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -43,9 +43,10 @@ import org.lsposed.lspatch.ui.util.lastItemIndex
import org.lsposed.lspatch.ui.util.observeState import org.lsposed.lspatch.ui.util.observeState
import org.lsposed.lspatch.ui.viewmodel.AppInfo import org.lsposed.lspatch.ui.viewmodel.AppInfo
import org.lsposed.patch.util.Logger import org.lsposed.patch.util.Logger
import java.io.File
enum class PatchState { enum class PatchState {
SELECTING, CONFIGURING, SUBMITTING, PATCHING, FINISHED SELECTING, CONFIGURING, SUBMITTING, PATCHING, FINISHED, ERROR
} }
class NewPatchPageViewModel : ViewModel() { class NewPatchPageViewModel : ViewModel() {
@ -76,7 +77,7 @@ fun NewPatchPage() {
when (viewModel.patchState) { when (viewModel.patchState) {
PatchState.SELECTING -> navController.navigate(PageList.SelectApps.name + "/false") PatchState.SELECTING -> navController.navigate(PageList.SelectApps.name + "/false")
PatchState.CONFIGURING, PatchState.SUBMITTING -> PatchOptionsPage(patchApp!!) PatchState.CONFIGURING, PatchState.SUBMITTING -> PatchOptionsPage(patchApp!!)
PatchState.PATCHING, PatchState.FINISHED -> DoPatchPage(viewModel.patchOptions!!) PatchState.PATCHING, PatchState.FINISHED, PatchState.ERROR -> DoPatchPage(viewModel.patchOptions!!)
} }
} }
@ -95,11 +96,9 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
.savedStateHandle.getLiveData<SnapshotStateList<AppInfo>>("selected", SnapshotStateList()) .savedStateHandle.getLiveData<SnapshotStateList<AppInfo>>("selected", SnapshotStateList())
if (viewModel.patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) { if (viewModel.patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) {
val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
if (useManager) embeddedModules.value?.clear() if (useManager) embeddedModules.value?.clear()
viewModel.patchOptions = Patcher.Options( viewModel.patchOptions = Patcher.Options(
apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk
outputPath = downloadDir,
debuggable = debuggable, debuggable = debuggable,
sigbypassLevel = sigBypassLevel, sigbypassLevel = sigBypassLevel,
v1 = v1, v2 = v2, v3 = v3, v1 = v1, v2 = v2, v3 = v3,
@ -194,6 +193,7 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
@Composable @Composable
private fun DoPatchPage(patcherOptions: Patcher.Options) { private fun DoPatchPage(patcherOptions: Patcher.Options) {
val context = LocalContext.current
val viewModel = viewModel<NewPatchPageViewModel>() val viewModel = viewModel<NewPatchPageViewModel>()
val navController = LocalNavController.current val navController = LocalNavController.current
val logs = remember { mutableStateListOf<Pair<Int, String>>() } val logs = remember { mutableStateListOf<Pair<Int, String>>() }
@ -219,8 +219,16 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
} }
LaunchedEffect(patcherOptions) { LaunchedEffect(patcherOptions) {
Patcher.patch(logger, patcherOptions) try {
Patcher.patch(context, logger, patcherOptions)
viewModel.patchState = PatchState.FINISHED viewModel.patchState = PatchState.FINISHED
} catch (t: Throwable) {
logger.e(t.message.orEmpty())
logger.e(t.stackTraceToString())
viewModel.patchState = PatchState.ERROR
} finally {
File(patcherOptions.outputPath).deleteRecursively()
}
} }
Column( Column(
@ -230,8 +238,7 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
.wrapContentHeight() .wrapContentHeight()
.animateContentSize(spring(stiffness = Spring.StiffnessLow)) .animateContentSize(spring(stiffness = Spring.StiffnessLow))
) { ) {
val patching by remember { derivedStateOf { viewModel.patchState == PatchState.PATCHING } } ShimmerAnimation(enabled = viewModel.patchState == PatchState.PATCHING) {
ShimmerAnimation(enabled = patching) {
CompositionLocalProvider( CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace) LocalTextStyle provides MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace)
) { ) {
@ -262,7 +269,7 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
} }
} }
if (!patching) { if (viewModel.patchState == PatchState.FINISHED) {
Row(Modifier.padding(top = 12.dp)) { Row(Modifier.padding(top = 12.dp)) {
Button( Button(
onClick = { navController.popBackStack() }, onClick = { navController.popBackStack() },

View File

@ -173,14 +173,13 @@ public class LSPatch {
if (!srcApkFile.exists()) if (!srcApkFile.exists())
throw new PatchError("The source apk file does not exit. Please provide a correct path."); throw new PatchError("The source apk file does not exit. Please provide a correct path.");
File tmpApk = Files.createTempFile(srcApkFile.getName(), "unsigned").toFile(); outputFile.delete();
tmpApk.delete();
logger.d("apk path: " + srcApkFile); logger.d("apk path: " + srcApkFile);
logger.i("Parsing original apk..."); logger.i("Parsing original apk...");
try (var dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS); try (var dstZFile = ZFile.openReadWrite(outputFile, Z_FILE_OPTIONS);
var srcZFile = dstZFile.addNestedZip((ignore) -> ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) { var srcZFile = dstZFile.addNestedZip((ignore) -> ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) {
// sign apk // sign apk
@ -313,15 +312,8 @@ public class LSPatch {
dstZFile.realign(); dstZFile.realign();
logger.i("Writing apk..."); logger.i("Writing apk...");
} finally { }
try {
outputFile.delete();
FileUtils.moveFile(tmpApk, outputFile);
logger.i("Done. Output APK: " + outputFile.getAbsolutePath()); logger.i("Done. Output APK: " + outputFile.getAbsolutePath());
} catch (Throwable e) {
throw new PatchError("Error writing apk", e);
}
}
} }
private void embedModules(ZFile zFile) { private void embedModules(ZFile zFile) {