feat: working resource patching

This commit is contained in:
Alberto Ponces 2022-08-19 19:13:43 +01:00
parent e373aef2d9
commit 4b2806c519
8 changed files with 107 additions and 64 deletions

View file

@ -30,12 +30,12 @@ android {
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '11'
} }
sourceSets { sourceSets {
@ -67,7 +67,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced // ReVanced
implementation "app.revanced:revanced-patcher:3.3.1" implementation "app.revanced:revanced-patcher:3.3.3"
// Signing & aligning // Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("org.bouncycastle:bcpkix-jdk15on:1.70")

View file

@ -16,7 +16,7 @@ import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.extensions.PatchExtensions.version import app.revanced.patcher.extensions.PatchExtensions.version
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.implementation.DexPatchBundle import app.revanced.patcher.util.patch.impl.DexPatchBundle
import dalvik.system.DexClassLoader import dalvik.system.DexClassLoader
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
@ -41,9 +41,10 @@ class MainActivity : FlutterActivity() {
mainChannel.setMethodCallHandler { call, result -> mainChannel.setMethodCallHandler { call, result ->
when (call.method) { when (call.method) {
"loadPatches" -> { "loadPatches" -> {
val pathBundlesPaths = call.argument<List<String>>("pathBundlesPaths") val zipPatchBundlePath = call.argument<String>("zipPatchBundlePath")
if (pathBundlesPaths != null) { val cacheDirPath = call.argument<String>("cacheDirPath")
loadPatches(result, pathBundlesPaths) if (zipPatchBundlePath != null && cacheDirPath != null) {
loadPatches(result, zipPatchBundlePath, cacheDirPath)
} else { } else {
result.notImplemented() result.notImplemented()
} }
@ -100,23 +101,25 @@ class MainActivity : FlutterActivity() {
} }
} }
fun loadPatches(result: MethodChannel.Result, pathBundlesPaths: List<String>) { fun loadPatches(
result: MethodChannel.Result,
zipPatchBundlePath: String,
cacheDirPath: String
) {
Thread( Thread(
Runnable { Runnable {
pathBundlesPaths.forEach { path ->
patches.addAll( patches.addAll(
DexPatchBundle( DexPatchBundle(
path, zipPatchBundlePath,
DexClassLoader( DexClassLoader(
path, zipPatchBundlePath,
applicationContext.cacheDir.path, cacheDirPath,
null, null,
javaClass.classLoader javaClass.classLoader
) )
) )
.loadPatches() .loadPatches()
) )
}
handler.post { result.success(null) } handler.post { result.success(null) }
} }
) )
@ -185,7 +188,8 @@ class MainActivity : FlutterActivity() {
val patchedFile = File(patchedFilePath) val patchedFile = File(patchedFilePath)
val outFile = File(outFilePath) val outFile = File(outFilePath)
val integrations = File(integrationsPath) val integrations = File(integrationsPath)
val filteredPatches = patches.filter { patch -> selectedPatches.any { it == patch.patchName } } val filteredPatches =
patches.filter { patch -> selectedPatches.any { it == patch.patchName } }
Thread( Thread(
Runnable { Runnable {
@ -289,7 +293,7 @@ class MainActivity : FlutterActivity() {
res.dexFiles.forEach { res.dexFiles.forEach {
file.addEntryCompressData( file.addEntryCompressData(
ZipEntry.createWithName(it.name), ZipEntry.createWithName(it.name),
it.dexFileInputStream.readBytes() it.stream.readBytes()
) )
} }
res.resourceFile?.let { res.resourceFile?.let {

View file

@ -17,7 +17,11 @@ class GithubAPI {
} }
} }
Future<File?> latestReleaseFile(String org, repoName) async { Future<File?> latestReleaseFile(
String extension,
String org,
repoName,
) async {
try { try {
var latestRelease = await _github.repositories.getLatestRelease( var latestRelease = await _github.repositories.getLatestRelease(
RepositorySlug(org, repoName), RepositorySlug(org, repoName),
@ -25,7 +29,7 @@ class GithubAPI {
String? url = latestRelease.assets String? url = latestRelease.assets
?.firstWhere((asset) => ?.firstWhere((asset) =>
asset.name != null && asset.name != null &&
(asset.name!.endsWith('.dex') || asset.name!.endsWith('.apk')) && asset.name!.endsWith(extension) &&
!asset.name!.contains('-sources') && !asset.name!.contains('-sources') &&
!asset.name!.contains('-javadoc')) !asset.name!.contains('-javadoc'))
.browserDownloadUrl; .browserDownloadUrl;

View file

@ -6,30 +6,28 @@ import 'package:revanced_manager/services/github_api.dart';
class ManagerAPI { class ManagerAPI {
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
Future<File?> downloadPatches() async { Future<File?> downloadPatches(String extension) async {
return await _githubAPI.latestReleaseFile(ghOrg, patchesRepo); return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo);
} }
Future<File?> downloadIntegrations() async { Future<File?> downloadIntegrations(String extension) async {
return await _githubAPI.latestReleaseFile(ghOrg, integrationsRepo);
}
Future<File?> downloadManager() async {
return await _githubAPI.latestReleaseFile( return await _githubAPI.latestReleaseFile(
'Aunali321', extension,
'revanced-manager-flutter', ghOrg,
integrationsRepo,
); );
} }
Future<File?> downloadManager(String extension) async {
return await _githubAPI.latestReleaseFile(extension, ghOrg, managerRepo);
}
Future<String?> getLatestPatchesVersion() async { Future<String?> getLatestPatchesVersion() async {
return await _githubAPI.latestReleaseVersion(ghOrg, patchesRepo); return await _githubAPI.latestReleaseVersion(ghOrg, patchesRepo);
} }
Future<String?> getLatestManagerVersion() async { Future<String?> getLatestManagerVersion() async {
return await _githubAPI.latestReleaseVersion( return await _githubAPI.latestReleaseVersion(ghOrg, managerRepo);
'Aunali321',
'revanced-manager-flutter',
);
} }
Future<String> getCurrentManagerVersion() async { Future<String> getCurrentManagerVersion() async {

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:app_installer/app_installer.dart'; import 'package:app_installer/app_installer.dart';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_archive/flutter_archive.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
@ -21,29 +22,47 @@ class PatcherAPI {
Directory? _tmpDir; Directory? _tmpDir;
Directory? _workDir; Directory? _workDir;
Directory? _cacheDir; Directory? _cacheDir;
File? _patchBundleFile; File? _zipPatchBundleFile;
File? _integrations; File? _integrations;
File? _inputFile; File? _inputFile;
File? _patchedFile; File? _patchedFile;
File? _outFile; File? _outFile;
Future<void> initPatcher() async {
_tmpDir = await getTemporaryDirectory();
_workDir = _tmpDir!.createTempSync('tmp-');
_inputFile = File('${_workDir!.path}/base.apk');
_patchedFile = File('${_workDir!.path}/patched.apk');
_outFile = File('${_workDir!.path}/out.apk');
_cacheDir = Directory('${_workDir!.path}/cache');
_cacheDir!.createSync();
}
Future<void> loadPatches() async { Future<void> loadPatches() async {
if (_patchBundleFile == null) { if (_cacheDir == null) {
_patchBundleFile = await _managerAPI.downloadPatches(); await initPatcher();
if (_patchBundleFile != null) { }
if (_zipPatchBundleFile == null) {
File? patchBundleDexFile = await _managerAPI.downloadPatches('.dex');
File? patchBundleJarFile = await _managerAPI.downloadPatches('.jar');
if (patchBundleDexFile != null && patchBundleJarFile != null) {
await joinPatchBundleFiles(patchBundleDexFile, patchBundleJarFile);
if (_zipPatchBundleFile != null) {
await patcherChannel.invokeMethod<bool>( await patcherChannel.invokeMethod<bool>(
'loadPatches', 'loadPatches',
{ {
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path], 'zipPatchBundlePath': _zipPatchBundleFile!.path,
'cacheDirPath': _cacheDir!.path,
}, },
); );
} }
} }
} }
}
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async { Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
List<ApplicationWithIcon> filteredPackages = []; List<ApplicationWithIcon> filteredPackages = [];
if (_patchBundleFile != null) { if (_zipPatchBundleFile != null) {
try { try {
List<String>? patchesPackages = await patcherChannel List<String>? patchesPackages = await patcherChannel
.invokeListMethod<String>('getCompatiblePackages'); .invokeListMethod<String>('getCompatiblePackages');
@ -71,7 +90,7 @@ class PatcherAPI {
PatchedApplication? selectedApp, PatchedApplication? selectedApp,
) async { ) async {
List<Patch> filteredPatches = []; List<Patch> filteredPatches = [];
if (_patchBundleFile != null && selectedApp != null) { if (_zipPatchBundleFile != null && selectedApp != null) {
try { try {
var patches = var patches =
await patcherChannel.invokeListMethod<Map<dynamic, dynamic>>( await patcherChannel.invokeListMethod<Map<dynamic, dynamic>>(
@ -112,7 +131,7 @@ class PatcherAPI {
PatchedApplication? selectedApp, PatchedApplication? selectedApp,
) async { ) async {
List<Patch> appliedPatches = []; List<Patch> appliedPatches = [];
if (_patchBundleFile != null && selectedApp != null) { if (_zipPatchBundleFile != null && selectedApp != null) {
try { try {
var patches = var patches =
await patcherChannel.invokeListMethod<Map<dynamic, dynamic>>( await patcherChannel.invokeListMethod<Map<dynamic, dynamic>>(
@ -148,17 +167,12 @@ class PatcherAPI {
return appliedPatches; return appliedPatches;
} }
Future<void> initPatcher(bool mergeIntegrations) async { Future<void> mergeIntegrations(bool mergeIntegrations) async {
if (mergeIntegrations) { if (mergeIntegrations) {
_integrations = await _managerAPI.downloadIntegrations(); _integrations = await _managerAPI.downloadIntegrations('.apk');
} else {
_integrations = null;
} }
_tmpDir = await getTemporaryDirectory();
_workDir = _tmpDir!.createTempSync('tmp-');
_inputFile = File('${_workDir!.path}/base.apk');
_patchedFile = File('${_workDir!.path}/patched.apk');
_outFile = File('${_workDir!.path}/out.apk');
_cacheDir = Directory('${_workDir!.path}/cache');
_cacheDir!.createSync();
} }
Future<void> runPatcher( Future<void> runPatcher(
@ -234,4 +248,26 @@ class PatcherAPI {
await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath); await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
} }
} }
Future<void> joinPatchBundleFiles(
File patchBundleDexFile,
File patchBundleJarFile,
) async {
_zipPatchBundleFile = File('${_workDir!.path}/join.zip');
Directory joinDir = Directory('${_cacheDir!.path}/join');
try {
await ZipFile.extractToDirectory(
zipFile: patchBundleJarFile,
destinationDir: joinDir,
);
patchBundleDexFile.copySync('${joinDir.path}/classes.dex');
await ZipFile.createFromDirectory(
sourceDir: joinDir,
zipFile: _zipPatchBundleFile!,
recurseSubDirs: true,
);
} on Exception {
_zipPatchBundleFile = null;
}
}
} }

View file

@ -89,7 +89,7 @@ class HomeViewModel extends BaseViewModel {
toastLength: Toast.LENGTH_LONG, toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER, gravity: ToastGravity.CENTER,
); );
File? managerApk = await _managerAPI.downloadManager(); File? managerApk = await _managerAPI.downloadManager('.apk');
if (managerApk != null) { if (managerApk != null) {
flutterLocalNotificationsPlugin.show( flutterLocalNotificationsPlugin.show(
0, 0,

View file

@ -111,7 +111,7 @@ class InstallerViewModel extends BaseViewModel {
'com.google.android.apps.youtube.music') { 'com.google.android.apps.youtube.music') {
resourcePatching = true; resourcePatching = true;
} }
await _patcherAPI.initPatcher(mergeIntegrations); await _patcherAPI.mergeIntegrations(mergeIntegrations);
await _patcherAPI.runPatcher( await _patcherAPI.runPatcher(
apkFilePath, apkFilePath,
_patches, _patches,

View file

@ -21,6 +21,7 @@ dependencies:
file_picker: ^5.0.1 file_picker: ^5.0.1
flutter: flutter:
sdk: flutter sdk: flutter
flutter_archive: ^5.0.0
flutter_background: ^1.1.0 flutter_background: ^1.1.0
flutter_cache_manager: ^3.3.0 flutter_cache_manager: ^3.3.0
flutter_i18n: ^0.32.4 flutter_i18n: ^0.32.4