diff --git a/android/app/build.gradle b/android/app/build.gradle index 7854f0c1..7f8b88f9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -85,7 +85,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // ReVanced - implementation "app.revanced:revanced-patcher:15.0.3" + implementation "app.revanced:revanced-patcher:16.0.0" // Signing & aligning implementation("org.bouncycastle:bcpkix-jdk15on:1.70") 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 691267b3..b88abb82 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 @@ -8,6 +8,7 @@ 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.PatchSet import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions import app.revanced.patcher.patch.PatchResult @@ -31,6 +32,8 @@ class MainActivity : FlutterActivity() { private var cancel: Boolean = false private var stopResult: MethodChannel.Result? = null + private lateinit var patches: PatchSet + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) @@ -46,7 +49,6 @@ class MainActivity : FlutterActivity() { mainChannel.setMethodCallHandler { call, result -> when (call.method) { "runPatcher" -> { - val patchBundleFilePath = call.argument("patchBundleFilePath") val originalFilePath = call.argument("originalFilePath") val inputFilePath = call.argument("inputFilePath") val patchedFilePath = call.argument("patchedFilePath") @@ -57,7 +59,7 @@ class MainActivity : FlutterActivity() { val keyStoreFilePath = call.argument("keyStoreFilePath") val keystorePassword = call.argument("keystorePassword") - if (patchBundleFilePath != null && + if ( originalFilePath != null && inputFilePath != null && patchedFilePath != null && @@ -71,7 +73,6 @@ class MainActivity : FlutterActivity() { cancel = false runPatcher( result, - patchBundleFilePath, originalFilePath, inputFilePath, patchedFilePath, @@ -94,17 +95,19 @@ class MainActivity : FlutterActivity() { val patchBundleFilePath = call.argument("patchBundleFilePath")!! val cacheDirPath = call.argument("cacheDirPath")!! + try { + patches = PatchBundleLoader.Dex( + File(patchBundleFilePath), + optimizedDexDirectory = File(cacheDirPath) + ) + } catch (ex: Exception) { + return@setMethodCallHandler result.notImplemented() + } catch (err: Error) { + return@setMethodCallHandler result.notImplemented() + } + JSONArray().apply { - try { - PatchBundleLoader.Dex( - File(patchBundleFilePath), - optimizedDexDirectory = File(cacheDirPath) - ) - } catch (ex: Exception) { - return@setMethodCallHandler result.notImplemented() - } catch (err: Error) { - return@setMethodCallHandler result.notImplemented() - }.forEach { + patches.forEach { JSONObject().apply { put("name", it.name) put("description", it.description) @@ -136,7 +139,6 @@ class MainActivity : FlutterActivity() { private fun runPatcher( result: MethodChannel.Result, - patchBundleFilePath: String, originalFilePath: String, inputFilePath: String, patchedFilePath: String, @@ -223,10 +225,7 @@ class MainActivity : FlutterActivity() { updateProgress(0.1, "Loading patches...", "Loading patches") - val patches = PatchBundleLoader.Dex( - File(patchBundleFilePath), - optimizedDexDirectory = cacheDir - ).filter { patch -> + val patches = patches.filter { patch -> val isCompatible = patch.compatiblePackages?.any { it.name == patcher.context.packageMetadata.packageName } ?: false @@ -331,7 +330,7 @@ class MainActivity : FlutterActivity() { val stack = ex.stackTraceToString() updateProgress( -100.0, - "Aborted", + "Failed", "An error occurred:\n$stack" ) } diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 9a094bec..dc4ffabd 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -31,7 +31,7 @@ class PatcherAPI { File? outFile; Future initialize() async { - await _loadPatches(); + await loadPatches(); await _managerAPI.downloadIntegrations(); final Directory appCache = await getTemporaryDirectory(); _dataDir = await getExternalStorageDirectory() ?? appCache; @@ -59,12 +59,10 @@ class PatcherAPI { } List getUniversalPatches() { - return _patches - .where((patch) => patch.compatiblePackages.isEmpty) - .toList(); + return _patches.where((patch) => patch.compatiblePackages.isEmpty).toList(); } - Future _loadPatches() async { + Future loadPatches() async { try { if (_patches.isEmpty) { _patches = await _managerAPI.getPatches(); @@ -85,15 +83,14 @@ class PatcherAPI { ) async { final List filteredApps = []; final bool allAppsIncluded = - _universalPatches.isNotEmpty && - showUniversalPatches; + _universalPatches.isNotEmpty && showUniversalPatches; if (allAppsIncluded) { final appList = await DeviceApps.getInstalledApplications( includeAppIcons: true, onlyAppsWithLaunchIntent: true, ); - for(final app in appList) { + for (final app in appList) { filteredApps.add(app as ApplicationWithIcon); } } @@ -154,9 +151,9 @@ class PatcherAPI { String apkFilePath, List selectedPatches, ) async { - final File? patchBundleFile = await _managerAPI.downloadPatches(); final File? integrationsFile = await _managerAPI.downloadIntegrations(); - if (patchBundleFile != null) { + + if (integrationsFile != null) { _dataDir.createSync(); _tmpDir.createSync(); final Directory workDir = _tmpDir.createTempSync('tmp-'); @@ -170,12 +167,11 @@ class PatcherAPI { await patcherChannel.invokeMethod( 'runPatcher', { - 'patchBundleFilePath': patchBundleFile.path, 'originalFilePath': originalFilePath, 'inputFilePath': inputFile.path, 'patchedFilePath': patchedFile.path, 'outFilePath': outFile!.path, - 'integrationsPath': integrationsFile!.path, + 'integrationsPath': integrationsFile.path, 'selectedPatches': selectedPatches.map((p) => p.name).toList(), 'cacheDirPath': cacheDir.path, 'keyStoreFilePath': _keyStoreFile.path, diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 137d2269..8d54987d 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -130,28 +130,28 @@ class InstallerViewModel extends BaseViewModel { Future runPatcher() async { try { - update(0.0, 'Initializing...', 'Initializing installer'); - if (_patches.isNotEmpty) { - try { - update(0.1, '', 'Creating working directory'); - await _patcherAPI.runPatcher( - _app.packageName, - _app.apkFilePath, - _patches, - ); - } on Exception catch (e) { - update( - -100.0, - 'Aborted...', - 'An error occurred! Aborted\nError:\n$e', - ); - if (kDebugMode) { - print(e); - } - } - } else { - update(-100.0, 'Aborted...', 'No app or patches selected! Aborted'); + await _patcherAPI.runPatcher( + _app.packageName, + _app.apkFilePath, + _patches, + ); + } on Exception catch (e) { + update( + -100.0, + 'Failed...', + 'Something went wrong:\n$e', + ); + if (kDebugMode) { + print(e); } + } + + // Necessary to reset the state of patches by reloading them + // in a later patching process. + _managerAPI.patches.clear(); + await _patcherAPI.loadPatches(); + + try { if (FlutterBackground.isBackgroundExecutionEnabled) { try { FlutterBackground.disableBackgroundExecution(); @@ -209,7 +209,8 @@ class InstallerViewModel extends BaseViewModel { ), RadioListTile( title: I18nText('installerView.installNonRootType'), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16), value: 0, groupValue: value, onChanged: (selected) { @@ -218,7 +219,8 @@ class InstallerViewModel extends BaseViewModel { ), RadioListTile( title: I18nText('installerView.installRootType'), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16), value: 1, groupValue: value, onChanged: (selected) { @@ -256,9 +258,9 @@ class InstallerViewModel extends BaseViewModel { Future stopPatcher() async { try { isCanceled = true; - update(0.5, 'Aborting...', 'Canceling patching process'); + update(0.5, 'Canceling...', 'Canceling patching process'); await _patcherAPI.stopPatcher(); - update(-100.0, 'Aborted...', 'Press back to exit'); + update(-100.0, 'Canceled...', 'Press back to exit'); } on Exception catch (e) { if (kDebugMode) { print(e); @@ -269,33 +271,34 @@ class InstallerViewModel extends BaseViewModel { Future installResult(BuildContext context, bool installAsRoot) async { try { _app.isRooted = installAsRoot; - update( - 1.0, - 'Installing...', - _app.isRooted - ? 'Installing patched file using root method' - : 'Installing patched file using nonroot method', - ); - isInstalled = await _patcherAPI.installPatchedFile(_app); - if (isInstalled) { - _app.isFromStorage = false; - _app.patchDate = DateTime.now(); - _app.appliedPatches = _patches.map((p) => p.name).toList(); + update( + 1.0, + 'Installing...', + _app.isRooted + ? 'Installing patched file using root method' + : 'Installing patched file using nonroot method', + ); + isInstalled = await _patcherAPI.installPatchedFile(_app); + if (isInstalled) { + _app.isFromStorage = false; + _app.patchDate = DateTime.now(); + _app.appliedPatches = _patches.map((p) => p.name).toList(); - // In case a patch changed the app name or package name, - // update the app info. - final app = await DeviceApps.getAppFromStorage(_patcherAPI.outFile!.path); - if (app != null) { - _app.name = app.appName; - _app.packageName = app.packageName; - } - - await _managerAPI.savePatchedApp(_app); - - update(1.0, 'Installed!', 'Installed!'); - } else { - // TODO(aabed): Show error message. + // In case a patch changed the app name or package name, + // update the app info. + final app = + await DeviceApps.getAppFromStorage(_patcherAPI.outFile!.path); + if (app != null) { + _app.name = app.appName; + _app.packageName = app.packageName; } + + await _managerAPI.savePatchedApp(_app); + + update(1.0, 'Installed!', 'Installed!'); + } else { + // TODO(aabed): Show error message. + } } on Exception catch (e) { if (kDebugMode) { print(e);