diff --git a/android/app/build.gradle b/android/app/build.gradle index 8663736a..1e32f0b7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -52,6 +52,8 @@ android { buildTypes { release { + shrinkResources false + minifyEnabled false resValue "string", "app_name", "ReVanced Manager" signingConfig signingConfigs.debug ndk { @@ -83,10 +85,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // ReVanced - implementation "app.revanced:revanced-patcher:11.0.4" + implementation "app.revanced:revanced-patcher:14.1.0" // Signing & aligning implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("com.android.tools.build:apksig:7.2.2") - } diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt index 2c8d7716..b94a582c 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt @@ -1,25 +1,30 @@ package app.revanced.manager.flutter -import android.os.Build import android.os.Handler import android.os.Looper -import androidx.annotation.NonNull import app.revanced.manager.flutter.utils.Aapt import app.revanced.manager.flutter.utils.aligning.ZipAligner import app.revanced.manager.flutter.utils.signing.Signer import app.revanced.manager.flutter.utils.zip.ZipFile import app.revanced.manager.flutter.utils.zip.structures.ZipEntry +import app.revanced.patcher.PatchBundleLoader import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages import app.revanced.patcher.extensions.PatchExtensions.patchName -import app.revanced.patcher.logging.Logger -import app.revanced.patcher.util.patch.PatchBundle -import dalvik.system.DexClassLoader +import app.revanced.patcher.patch.PatchResult import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel +import kotlinx.coroutines.cancel +import kotlinx.coroutines.runBlocking import java.io.File +import java.io.PrintWriter +import java.io.StringWriter +import java.util.logging.Level +import java.util.logging.LogRecord +import java.util.logging.Logger +import java.util.logging.SimpleFormatter private const val PATCHER_CHANNEL = "app.revanced.manager.flutter/patcher" private const val INSTALLER_CHANNEL = "app.revanced.manager.flutter/installer" @@ -30,10 +35,11 @@ class MainActivity : FlutterActivity() { private var cancel: Boolean = false private var stopResult: MethodChannel.Result? = null - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, PATCHER_CHANNEL) - installerChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, INSTALLER_CHANNEL) + installerChannel = + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, INSTALLER_CHANNEL) mainChannel.setMethodCallHandler { call, result -> when (call.method) { "runPatcher" -> { @@ -77,10 +83,12 @@ class MainActivity : FlutterActivity() { result.notImplemented() } } + "stopPatcher" -> { cancel = true stopResult = result } + else -> result.notImplemented() } } @@ -105,55 +113,79 @@ class MainActivity : FlutterActivity() { val outFile = File(outFilePath) val integrations = File(integrationsPath) val keyStoreFile = File(keyStoreFilePath) + val cacheDir = File(cacheDirPath) Thread { try { + Logger.getLogger("").apply { + handlers.forEach { + it.close() + removeHandler(it) + } + object : java.util.logging.Handler() { + override fun publish(record: LogRecord) = formatter.format(record).toByteArray().let { + if (record.level.intValue() > Level.INFO.intValue()) + System.err.write(it) + else + System.out.write(it) + } + + override fun flush() { + System.out.flush() + System.err.flush() + } + + override fun close() = flush() + }.also { + it.level = Level.ALL + it.formatter = SimpleFormatter() + }.let(::addHandler) + } handler.post { installerChannel.invokeMethod( "update", mapOf( "progress" to 0.1, "header" to "", - "log" to "Copying original apk" + "log" to "Copying original APK" ) ) } - if(cancel) { + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } originalFile.copyTo(inputFile, true) + if (cancel) { + handler.post { stopResult!!.success(null) } + return@Thread + } + handler.post { installerChannel.invokeMethod( "update", mapOf( "progress" to 0.2, - "header" to "Unpacking apk...", - "log" to "Unpacking input apk" + "header" to "Reading APK...", + "log" to "Reading input APK" ) ) } - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } - val patcher = Patcher( PatcherOptions( inputFile, - cacheDirPath, + cacheDir, Aapt.binary(applicationContext).absolutePath, - cacheDirPath, - logger = ManagerLogger() + cacheDir.path, ) ) - if(cancel) { + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } @@ -161,28 +193,19 @@ class MainActivity : FlutterActivity() { handler.post { installerChannel.invokeMethod( "update", - mapOf("progress" to 0.3, "header" to "", "log" to "") - ) - } - handler.post { - installerChannel.invokeMethod( - "update", - mapOf( - "progress" to 0.4, - "header" to "Merging integrations...", - "log" to "Merging integrations" - ) + mapOf("progress" to 0.3, "header" to "Loading patches...", "log" to "Loading patches") ) } - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } + val patches = + PatchBundleLoader.Dex( + File(patchBundleFilePath) + ).filter { patch -> + (patch.compatiblePackages?.any { it.name == patcher.context.packageMetadata.packageName } == true || patch.compatiblePackages.isNullOrEmpty()) && + selectedPatches.any { it == patch.patchName } + } - patcher.addIntegrations(listOf(integrations)) {} - - if(cancel) { + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } @@ -192,91 +215,75 @@ class MainActivity : FlutterActivity() { "update", mapOf( "progress" to 0.5, - "header" to "Applying patches...", + "header" to "Executing patches...", "log" to "" ) ) } - if(cancel) { + patcher.apply { + acceptIntegrations(listOf(integrations)) + acceptPatches(patches) + + runBlocking { + apply(false).collect { patchResult: PatchResult -> + patchResult.exception?.let { + if (cancel) { + handler.post { stopResult!!.success(null) } + this.cancel() + return@collect + } + StringWriter().use { writer -> + it.printStackTrace(PrintWriter(writer)) + handler.post { + installerChannel.invokeMethod( + "update", + mapOf("progress" to 0.5, "header" to "", "log" to "${patchResult.patchName} failed: $writer") + ) + } + } + } ?: run { + if (cancel) { + handler.post { stopResult!!.success(null) } + this.cancel() + return@collect + } + val msg = "${patchResult.patchName} succeeded" + handler.post { + installerChannel.invokeMethod( + "update", + mapOf( + "progress" to 0.5, + "header" to "", + "log" to msg + ) + ) + } + } + } + } + } + + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } - val patches = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) { - PatchBundle.Dex( - patchBundleFilePath, - DexClassLoader( - patchBundleFilePath, - cacheDirPath, - null, - javaClass.classLoader - ) - ).loadPatches().filter { patch -> - (patch.compatiblePackages?.any { it.name == patcher.context.packageMetadata.packageName } == true || patch.compatiblePackages.isNullOrEmpty()) && - selectedPatches.any { it == patch.patchName } - } - } else { - TODO("VERSION.SDK_INT < CUPCAKE") - } - - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } - - patcher.addPatches(patches) - patcher.executePatches().forEach { (patch, res) -> - if (res.isSuccess) { - val msg = "Applied $patch" - handler.post { - installerChannel.invokeMethod( - "update", - mapOf( - "progress" to 0.5, - "header" to "", - "log" to msg - ) - ) - } - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } - return@forEach - } - val msg = - "Failed to apply $patch: " + "${res.exceptionOrNull()!!.message ?: res.exceptionOrNull()!!.cause!!::class.simpleName}" - handler.post { - installerChannel.invokeMethod( - "update", - mapOf("progress" to 0.5, "header" to "", "log" to msg) - ) - } - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } - } - handler.post { installerChannel.invokeMethod( "update", mapOf( "progress" to 0.7, - "header" to "Repacking apk...", - "log" to "Repacking patched apk" + "header" to "Repacking APK...", + "log" to "" ) ) } - if(cancel) { - handler.post { stopResult!!.success(null) } - return@Thread - } - val res = patcher.save() + val res = patcher.get() + patcher.close() ZipFile(patchedFile).use { file -> res.dexFiles.forEach { - if(cancel) { + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } @@ -296,7 +303,7 @@ class MainActivity : FlutterActivity() { ZipAligner::getEntryAlignment ) } - if(cancel) { + if (cancel) { handler.post { stopResult!!.success(null) } return@Thread } @@ -305,7 +312,7 @@ class MainActivity : FlutterActivity() { "update", mapOf( "progress" to 0.9, - "header" to "Signing apk...", + "header" to "Signing APK...", "log" to "" ) ) @@ -319,7 +326,7 @@ class MainActivity : FlutterActivity() { ) } catch (e: Exception) { //log to console - print("Error signing apk: ${e.message}") + print("Error signing APK: ${e.message}") e.printStackTrace() } @@ -334,52 +341,54 @@ class MainActivity : FlutterActivity() { ) } } catch (ex: Throwable) { - val stack = ex.stackTraceToString() - handler.post { - installerChannel.invokeMethod( - "update", - mapOf( - "progress" to -100.0, - "header" to "Aborted...", - "log" to "An error occurred! Aborted\nError:\n$stack" + if (!cancel) { + val stack = ex.stackTraceToString() + handler.post { + installerChannel.invokeMethod( + "update", + mapOf( + "progress" to -100.0, + "header" to "Aborted...", + "log" to "An error occurred! Aborted\nError:\n$stack" + ) ) - ) + } } } handler.post { result.success(null) } }.start() } - inner class ManagerLogger : Logger { - override fun error(msg: String) { - handler.post { - installerChannel - .invokeMethod( - "update", - mapOf("progress" to -1.0, "header" to "", "log" to msg) - ) - } - } - - override fun warn(msg: String) { - handler.post { - installerChannel.invokeMethod( - "update", - mapOf("progress" to -1.0, "header" to "", "log" to msg) - ) - } - } - - override fun info(msg: String) { - handler.post { - installerChannel.invokeMethod( - "update", - mapOf("progress" to -1.0, "header" to "", "log" to msg) - ) - } - } - - override fun trace(_msg: String) { /* unused */ - } - } +// inner class ManagerLogger : Logger { +// override fun error(msg: String) { +// handler.post { +// installerChannel +// .invokeMethod( +// "update", +// mapOf("progress" to -1.0, "header" to "", "log" to msg) +// ) +// } +// } +// +// override fun warn(msg: String) { +// handler.post { +// installerChannel.invokeMethod( +// "update", +// mapOf("progress" to -1.0, "header" to "", "log" to msg) +// ) +// } +// } +// +// override fun info(msg: String) { +// handler.post { +// installerChannel.invokeMethod( +// "update", +// mapOf("progress" to -1.0, "header" to "", "log" to msg) +// ) +// } +// } +// +// override fun trace(_msg: String) { /* unused */ +// } +// } } diff --git a/android/build.gradle b/android/build.gradle index 88ac491c..bfe266a7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.0' repositories { google() mavenCentral()