build(Needs bump): Bump ReVanced Patcher (#2242)

Co-authored-by: aAbed <aabedhkhan@gmail.com>
This commit is contained in:
oSumAtrIX 2024-10-26 17:41:49 +02:00 committed by GitHub
parent 4db4789a06
commit 8d0d782ab5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 135 additions and 291 deletions

View File

@ -9,14 +9,15 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import app.revanced.library.ApkUtils import app.revanced.library.ApkUtils
import app.revanced.library.ApkUtils.applyTo import app.revanced.library.ApkUtils.applyTo
import app.revanced.library.installation.installer.LocalInstaller
import app.revanced.manager.flutter.utils.Aapt import app.revanced.manager.flutter.utils.Aapt
import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver
import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import app.revanced.patcher.Patcher import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherConfig import app.revanced.patcher.PatcherConfig
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.loadPatchesFromDex
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
@ -37,7 +38,7 @@ class MainActivity : FlutterActivity() {
private var cancel: Boolean = false private var cancel: Boolean = false
private var stopResult: MethodChannel.Result? = null private var stopResult: MethodChannel.Result? = null
private lateinit var patches: PatchSet private lateinit var patches: Set<Patch<*>>
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine) super.configureFlutterEngine(flutterEngine)
@ -70,7 +71,6 @@ class MainActivity : FlutterActivity() {
"runPatcher" -> { "runPatcher" -> {
val inFilePath = call.argument<String>("inFilePath") val inFilePath = call.argument<String>("inFilePath")
val outFilePath = call.argument<String>("outFilePath") val outFilePath = call.argument<String>("outFilePath")
val integrationsPath = call.argument<String>("integrationsPath")
val selectedPatches = call.argument<List<String>>("selectedPatches") val selectedPatches = call.argument<List<String>>("selectedPatches")
val options = call.argument<Map<String, Map<String, Any>>>("options") val options = call.argument<Map<String, Map<String, Any>>>("options")
val tmpDirPath = call.argument<String>("tmpDirPath") val tmpDirPath = call.argument<String>("tmpDirPath")
@ -80,7 +80,6 @@ class MainActivity : FlutterActivity() {
if ( if (
inFilePath != null && inFilePath != null &&
outFilePath != null && outFilePath != null &&
integrationsPath != null &&
selectedPatches != null && selectedPatches != null &&
options != null && options != null &&
tmpDirPath != null && tmpDirPath != null &&
@ -92,14 +91,17 @@ class MainActivity : FlutterActivity() {
result, result,
inFilePath, inFilePath,
outFilePath, outFilePath,
integrationsPath,
selectedPatches, selectedPatches,
options, options,
tmpDirPath, tmpDirPath,
keyStoreFilePath, keyStoreFilePath,
keystorePassword keystorePassword
) )
} else result.notImplemented() } else result.error(
"INVALID_ARGUMENTS",
"Invalid arguments",
"One or more arguments are missing"
)
} }
"stopPatcher" -> { "stopPatcher" -> {
@ -113,14 +115,16 @@ class MainActivity : FlutterActivity() {
try { try {
val patchBundleFile = File(patchBundleFilePath) val patchBundleFile = File(patchBundleFilePath)
patchBundleFile.setWritable(false) patchBundleFile.setWritable(false)
patches = PatchBundleLoader.Dex( patches = loadPatchesFromDex(
patchBundleFile, setOf(patchBundleFile),
optimizedDexDirectory = codeCacheDir optimizedDexDirectory = codeCacheDir
) )
} catch (ex: Exception) { } catch (t: Throwable) {
return@setMethodCallHandler result.notImplemented() return@setMethodCallHandler result.error(
} catch (err: Error) { "PATCH_BUNDLE_ERROR",
return@setMethodCallHandler result.notImplemented() "Failed to load patch bundle",
t.stackTraceToString()
)
} }
JSONArray().apply { JSONArray().apply {
@ -130,13 +134,13 @@ class MainActivity : FlutterActivity() {
put("description", it.description) put("description", it.description)
put("excluded", !it.use) put("excluded", !it.use)
put("compatiblePackages", JSONArray().apply { put("compatiblePackages", JSONArray().apply {
it.compatiblePackages?.forEach { compatiblePackage -> it.compatiblePackages?.forEach { (name, versions) ->
val compatiblePackageJson = JSONObject().apply { val compatiblePackageJson = JSONObject().apply {
put("name", compatiblePackage.name) put("name", name)
put( put(
"versions", "versions",
JSONArray().apply { JSONArray().apply {
compatiblePackage.versions?.forEach { version -> versions?.forEach { version ->
put(version) put(version)
} }
}) })
@ -172,7 +176,7 @@ class MainActivity : FlutterActivity() {
} }
}) })
} ?: put("values", null) } ?: put("values", null)
put("valueType", option.valueType) put("type", option.type)
}.let(::put) }.let(::put)
} }
}) })
@ -211,7 +215,6 @@ class MainActivity : FlutterActivity() {
result: MethodChannel.Result, result: MethodChannel.Result,
inFilePath: String, inFilePath: String,
outFilePath: String, outFilePath: String,
integrationsPath: String,
selectedPatches: List<String>, selectedPatches: List<String>,
options: Map<String, Map<String, Any>>, options: Map<String, Map<String, Any>>,
tmpDirPath: String, tmpDirPath: String,
@ -223,7 +226,6 @@ class MainActivity : FlutterActivity() {
inFile.setWritable(true) inFile.setWritable(true)
inFile.setReadable(true) inFile.setReadable(true)
val outFile = File(outFilePath) val outFile = File(outFilePath)
val integrations = File(integrationsPath)
val keyStoreFile = File(keyStoreFilePath) val keyStoreFile = File(keyStoreFilePath)
val tmpDir = File(tmpDirPath) val tmpDir = File(tmpDirPath)
@ -289,8 +291,8 @@ class MainActivity : FlutterActivity() {
updateProgress(0.02, "Loading patches...", "Loading patches") updateProgress(0.02, "Loading patches...", "Loading patches")
val patches = patches.filter { patch -> val patches = patches.filter { patch ->
val isCompatible = patch.compatiblePackages?.any { val isCompatible = patch.compatiblePackages?.any { (name, _) ->
it.name == patcher.context.packageMetadata.packageName name == patcher.context.packageMetadata.packageName
} ?: false } ?: false
val compatibleOrUniversal = val compatibleOrUniversal =
@ -307,10 +309,7 @@ class MainActivity : FlutterActivity() {
updateProgress(0.05, "Executing...", "") updateProgress(0.05, "Executing...", "")
val patcherResult = patcher.use { val patcherResult = patcher.use {
patcher.apply { it += patches
acceptIntegrations(setOf(integrations))
acceptPatches(patches)
}
runBlocking { runBlocking {
// Update the progress bar every time a patch is executed from 0.15 to 0.7 // Update the progress bar every time a patch is executed from 0.15 to 0.7
@ -318,7 +317,7 @@ class MainActivity : FlutterActivity() {
val progressStep = 0.55 / totalPatchesCount val progressStep = 0.55 / totalPatchesCount
var progress = 0.05 var progress = 0.05
patcher.apply(false).collect(FlowCollector { patchResult: PatchResult -> patcher().collect(FlowCollector { patchResult: PatchResult ->
if (cancel(patcher::close)) return@FlowCollector if (cancel(patcher::close)) return@FlowCollector
val msg = patchResult.exception?.let { val msg = patchResult.exception?.let {
@ -346,10 +345,11 @@ class MainActivity : FlutterActivity() {
if (cancel(patcher::close)) return@Thread if (cancel(patcher::close)) return@Thread
ApkUtils.sign( ApkUtils.signApk(
inFile, inFile,
outFile, outFile,
ApkUtils.SigningOptions( "ReVanced",
ApkUtils.KeyStoreDetails(
keyStoreFile, keyStoreFile,
keystorePassword, keystorePassword,
"alias", "alias",

View File

@ -21,7 +21,8 @@ subprojects {
afterEvaluate { afterEvaluate {
extensions.findByName("android")?.let { extensions.findByName("android")?.let {
it as CommonExtension<*, *, *, *, *, *> it as CommonExtension<*, *, *, *, *, *>
it.compileSdk = 34 if (it.compileSdk != null && it.compileSdk!! < 31)
it.compileSdk = 34
} }
} }

View File

@ -1,6 +1,6 @@
[versions] [versions]
revanced-patcher = "19.3.1" # TODO: Update to non-dev revanced-patcher = "20.0.2"
revanced-library = "2.2.1" revanced-library = "3.0.1"
desugar_jdk_libs = "2.1.2" desugar_jdk_libs = "2.1.2"
[libraries] [libraries]

View File

@ -17,7 +17,7 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false id("com.android.application") version "8.5.0" apply false
id("org.jetbrains.kotlin.android") version "2.0.20" apply false id("org.jetbrains.kotlin.android") version "2.0.20" apply false
} }

View File

@ -158,20 +158,18 @@
"languageLabel": "Language", "languageLabel": "Language",
"languageUpdated": "Language updated", "languageUpdated": "Language updated",
"sourcesLabel": "Alternative sources", "sourcesLabel": "Alternative sources",
"sourcesLabelHint": "Configure the alternative sources for ReVanced Patches and ReVanced Integrations", "sourcesLabelHint": "Configure the alternative sources for ReVanced Patches",
"sourcesIntegrationsLabel": "Integrations source",
"useAlternativeSources": "Use alternative sources", "useAlternativeSources": "Use alternative sources",
"useAlternativeSourcesHint": "Use alternative sources for ReVanced Patches and ReVanced Integrations instead of the API", "useAlternativeSourcesHint": "Use alternative sources for ReVanced Patches instead of the API",
"sourcesResetDialogTitle": "Reset", "sourcesResetDialogTitle": "Reset",
"sourcesResetDialogText": "Are you sure you want to reset your sources to their default values?", "sourcesResetDialogText": "Are you sure you want to reset your sources to their default values?",
"apiURLResetDialogText": "Are you sure you want to reset your API URL to its default value?", "apiURLResetDialogText": "Are you sure you want to reset your API URL to its default value?",
"sourcesUpdateNote": "Note: This will automatically download ReVanced Patches and ReVanced Integrations from the alternative sources.\n\nThis will connect you to the alternative source.", "sourcesUpdateNote": "Note: This will automatically download ReVanced Patches from the alternative sources.\n\nThis will connect you to the alternative source.",
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Configure the API URL of ReVanced Manager", "apiURLHint": "Configure the API URL of ReVanced Manager",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"orgPatchesLabel": "Patches organization", "orgPatchesLabel": "Patches organization",
"sourcesPatchesLabel": "Patches source", "sourcesPatchesLabel": "Patches source",
"orgIntegrationsLabel": "Integrations organization",
"contributorsLabel": "Contributors", "contributorsLabel": "Contributors",
"contributorsHint": "A list of contributors of ReVanced", "contributorsHint": "A list of contributors of ReVanced",
"logsLabel": "Share logs", "logsLabel": "Share logs",

View File

@ -62,11 +62,12 @@ class Option {
required this.value, required this.value,
required this.values, required this.values,
required this.required, required this.required,
required this.valueType, required this.type,
}); });
factory Option.fromJson(Map<String, dynamic> json) { factory Option.fromJson(Map<String, dynamic> json) {
_migrateV17ToV19(json); _migrateV17ToV19(json);
_migrateV19ToV20(json);
return _$OptionFromJson(json); return _$OptionFromJson(json);
} }
@ -83,13 +84,25 @@ class Option {
} }
} }
static void _migrateV19ToV20(Map<String, dynamic> json) {
if (json['valueType'] != null) {
final String type = json['valueType'];
json['type'] = type.endsWith('Array')
? 'kotlin.collections.List<kotlin.${type.replaceAll('Array', '')}>'
: 'kotlin.$type';
json['valueType'] = null;
}
}
final String key; final String key;
final String title; final String title;
final String description; final String description;
final dynamic value; final dynamic value;
final Map<String, dynamic>? values; final Map<String, dynamic>? values;
final bool required; final bool required;
final String valueType; final String type;
Map toJson() => _$OptionToJson(this); Map toJson() => _$OptionToJson(this);
} }

View File

@ -111,11 +111,7 @@ class GithubAPI {
); );
if (asset != null) { if (asset != null) {
final String downloadUrl = asset['browser_download_url']; final String downloadUrl = asset['browser_download_url'];
if (extension == '.apk') { _managerAPI.setPatchesDownloadURL(downloadUrl);
_managerAPI.setIntegrationsDownloadURL(downloadUrl);
} else {
_managerAPI.setPatchesDownloadURL(downloadUrl);
}
return await _downloadManager.getSingleFile( return await _downloadManager.getSingleFile(
downloadUrl, downloadUrl,
); );

View File

@ -44,15 +44,13 @@ class ManagerAPI {
String keystoreFile = String keystoreFile =
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore'; '/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore';
String defaultKeystorePassword = 's3cur3p@ssw0rd'; String defaultKeystorePassword = 's3cur3p@ssw0rd';
String defaultApiUrl = 'https://api.revanced.app/'; String defaultApiUrl = 'https://api.revanced.app/v3';
String defaultRepoUrl = 'https://api.github.com'; String defaultRepoUrl = 'https://api.github.com';
String defaultPatcherRepo = 'revanced/revanced-patcher'; String defaultPatcherRepo = 'revanced/revanced-patcher';
String defaultPatchesRepo = 'revanced/revanced-patches'; String defaultPatchesRepo = 'revanced/revanced-patches';
String defaultIntegrationsRepo = 'revanced/revanced-integrations';
String defaultCliRepo = 'revanced/revanced-cli'; String defaultCliRepo = 'revanced/revanced-cli';
String defaultManagerRepo = 'revanced/revanced-manager'; String defaultManagerRepo = 'revanced/revanced-manager';
String? patchesVersion = ''; String? patchesVersion = '';
String? integrationsVersion = '';
Future<void> initialize() async { Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
@ -69,13 +67,15 @@ class ManagerAPI {
} }
// Migrate to new API URL if not done yet as the old one is sunset. // Migrate to new API URL if not done yet as the old one is sunset.
final bool hasMigratedToNewApi = final bool hasMigratedToLatestApi =
_prefs.getBool('migratedToNewApiUrl') ?? false; _prefs.getBool('migratedToLatestApiUrl') ?? false;
if (!hasMigratedToNewApi) { if (!hasMigratedToLatestApi) {
final String apiUrl = getApiUrl().toLowerCase(); final String apiUrl = getApiUrl().toLowerCase();
if (apiUrl.contains('releases.revanced.app')) { if (apiUrl.contains('releases.revanced.app') ||
(apiUrl.contains('api.revanced.app') &&
!apiUrl.contains('v3'))) {
await setApiUrl(''); // Reset to default. await setApiUrl(''); // Reset to default.
_prefs.setBool('migratedToNewApiUrl', true); _prefs.setBool('migratedToLatestApiUrl', true);
} }
} }
@ -83,10 +83,8 @@ class ManagerAPI {
_prefs.getBool('migratedToAlternativeSource') ?? false; _prefs.getBool('migratedToAlternativeSource') ?? false;
if (!hasMigratedToAlternativeSource) { if (!hasMigratedToAlternativeSource) {
final String patchesRepo = getPatchesRepo(); final String patchesRepo = getPatchesRepo();
final String integrationsRepo = getIntegrationsRepo();
final bool usingAlternativeSources = final bool usingAlternativeSources =
patchesRepo.toLowerCase() != defaultPatchesRepo || patchesRepo.toLowerCase() != defaultPatchesRepo;
integrationsRepo.toLowerCase() != defaultIntegrationsRepo;
_prefs.setBool('useAlternativeSources', usingAlternativeSources); _prefs.setBool('useAlternativeSources', usingAlternativeSources);
_prefs.setBool('migratedToAlternativeSource', true); _prefs.setBool('migratedToAlternativeSource', true);
} }
@ -200,14 +198,6 @@ class ManagerAPI {
await _prefs.setStringList('savedPatches-$packageName', patchesJson); await _prefs.setStringList('savedPatches-$packageName', patchesJson);
} }
String getIntegrationsDownloadURL() {
return _prefs.getString('integrationsDownloadURL') ?? '';
}
Future<void> setIntegrationsDownloadURL(String value) async {
await _prefs.setString('integrationsDownloadURL', value);
}
List<Patch> getUsedPatches(String packageName) { List<Patch> getUsedPatches(String packageName) {
final List<String> patchesJson = final List<String> patchesJson =
_prefs.getStringList('usedPatches-$packageName') ?? []; _prefs.getStringList('usedPatches-$packageName') ?? [];
@ -256,17 +246,6 @@ class ManagerAPI {
_prefs.remove('patchOption-$packageName-$patchName-$key'); _prefs.remove('patchOption-$packageName-$patchName-$key');
} }
String getIntegrationsRepo() {
return _prefs.getString('integrationsRepo') ?? defaultIntegrationsRepo;
}
Future<void> setIntegrationsRepo(String value) async {
if (value.isEmpty || value.startsWith('/') || value.endsWith('/')) {
value = defaultIntegrationsRepo;
}
await _prefs.setString('integrationsRepo', value);
}
bool getUseDynamicTheme() { bool getUseDynamicTheme() {
return _prefs.getBool('useDynamicTheme') ?? false; return _prefs.getBool('useDynamicTheme') ?? false;
} }
@ -464,28 +443,7 @@ class ManagerAPI {
final String currentVersion = await getCurrentPatchesVersion(); final String currentVersion = await getCurrentPatchesVersion();
final String url = getPatchesDownloadURL(); final String url = getPatchesDownloadURL();
return await _githubAPI.getReleaseFile( return await _githubAPI.getReleaseFile(
'.jar', '.rvp',
repoName,
currentVersion,
url,
);
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
return null;
}
}
Future<File?> downloadIntegrations() async {
try {
final String repoName = !isUsingAlternativeSources()
? defaultIntegrationsRepo
: getIntegrationsRepo();
final String currentVersion = await getCurrentIntegrationsVersion();
final String url = getIntegrationsDownloadURL();
return await _githubAPI.getReleaseFile(
'.apk',
repoName, repoName,
currentVersion, currentVersion,
url, url,
@ -499,18 +457,12 @@ class ManagerAPI {
} }
Future<File?> downloadManager() async { Future<File?> downloadManager() async {
return await _revancedAPI.getLatestReleaseFile( return await _revancedAPI.getLatestReleaseFile('manager');
'.apk',
defaultManagerRepo,
);
} }
Future<String?> getLatestPatchesReleaseTime() async { Future<String?> getLatestPatchesReleaseTime() async {
if (!isUsingAlternativeSources()) { if (!isUsingAlternativeSources()) {
return await _revancedAPI.getLatestReleaseTime( return await _revancedAPI.getLatestReleaseTime('patches');
'.json',
defaultPatchesRepo,
);
} else { } else {
final release = await _githubAPI.getLatestRelease(getPatchesRepo()); final release = await _githubAPI.getLatestRelease(getPatchesRepo());
if (release != null) { if (release != null) {
@ -525,39 +477,20 @@ class ManagerAPI {
Future<String?> getLatestManagerReleaseTime() async { Future<String?> getLatestManagerReleaseTime() async {
return await _revancedAPI.getLatestReleaseTime( return await _revancedAPI.getLatestReleaseTime(
'.apk', 'manager',
defaultManagerRepo,
); );
} }
Future<String?> getLatestManagerVersion() async { Future<String?> getLatestManagerVersion() async {
return await _revancedAPI.getLatestReleaseVersion( return await _revancedAPI.getLatestReleaseVersion(
'.apk', 'manager',
defaultManagerRepo,
); );
} }
Future<String?> getLatestIntegrationsVersion() async {
if (!isUsingAlternativeSources()) {
return await _revancedAPI.getLatestReleaseVersion(
'.apk',
defaultIntegrationsRepo,
);
} else {
final release = await _githubAPI.getLatestRelease(getIntegrationsRepo());
if (release != null) {
return release['tag_name'];
} else {
return null;
}
}
}
Future<String?> getLatestPatchesVersion() async { Future<String?> getLatestPatchesVersion() async {
if (!isUsingAlternativeSources()) { if (!isUsingAlternativeSources()) {
return await _revancedAPI.getLatestReleaseVersion( return await _revancedAPI.getLatestReleaseVersion(
'.json', 'patches',
defaultPatchesRepo,
); );
} else { } else {
final release = await _githubAPI.getLatestRelease(getPatchesRepo()); final release = await _githubAPI.getLatestRelease(getPatchesRepo());
@ -620,25 +553,6 @@ class ManagerAPI {
await downloadPatches(); await downloadPatches();
} }
Future<String> getCurrentIntegrationsVersion() async {
integrationsVersion = _prefs.getString('integrationsVersion') ?? '0.0.0';
if (integrationsVersion == '0.0.0' || isPatchesAutoUpdate()) {
final String newIntegrationsVersion =
await getLatestIntegrationsVersion() ?? '0.0.0';
if (integrationsVersion != newIntegrationsVersion &&
newIntegrationsVersion != '0.0.0') {
await setCurrentIntegrationsVersion(newIntegrationsVersion);
}
}
return integrationsVersion!;
}
Future<void> setCurrentIntegrationsVersion(String version) async {
await _prefs.setString('integrationsVersion', version);
await setIntegrationsDownloadURL('');
await downloadIntegrations();
}
Future<List<PatchedApplication>> getAppsToRemove( Future<List<PatchedApplication>> getAppsToRemove(
List<PatchedApplication> patchedApps, List<PatchedApplication> patchedApps,
) async { ) async {

View File

@ -33,7 +33,6 @@ class PatcherAPI {
Future<void> initialize() async { Future<void> initialize() async {
await loadPatches(); await loadPatches();
await _managerAPI.downloadIntegrations();
final Directory appCache = await getApplicationSupportDirectory(); final Directory appCache = await getApplicationSupportDirectory();
_dataDir = await getExternalStorageDirectory() ?? appCache; _dataDir = await getExternalStorageDirectory() ?? appCache;
_tmpDir = Directory('${appCache.path}/patcher'); _tmpDir = Directory('${appCache.path}/patcher');
@ -153,7 +152,6 @@ class PatcherAPI {
List<Patch> selectedPatches, List<Patch> selectedPatches,
bool isFromStorage, bool isFromStorage,
) async { ) async {
final File? integrationsFile = await _managerAPI.downloadIntegrations();
final Map<String, Map<String, dynamic>> options = {}; final Map<String, Map<String, dynamic>> options = {};
for (final patch in selectedPatches) { for (final patch in selectedPatches) {
if (patch.options.isNotEmpty) { if (patch.options.isNotEmpty) {
@ -169,44 +167,41 @@ class PatcherAPI {
} }
} }
if (integrationsFile != null) { _dataDir.createSync();
_dataDir.createSync(); _tmpDir.createSync();
_tmpDir.createSync(); final Directory workDir = await _tmpDir.createTemp('tmp-');
final Directory workDir = await _tmpDir.createTemp('tmp-');
final File inApkFile = File('${workDir.path}/in.apk'); final File inApkFile = File('${workDir.path}/in.apk');
await File(apkFilePath).copy(inApkFile.path); await File(apkFilePath).copy(inApkFile.path);
if (isFromStorage) { if (isFromStorage) {
// The selected apk was copied to cacheDir by the file picker, so it's not needed anymore. // The selected apk was copied to cacheDir by the file picker, so it's not needed anymore.
// rename() can't be used here, as Android system also counts the size of files moved out from cacheDir // rename() can't be used here, as Android system also counts the size of files moved out from cacheDir
// as part of the app's cache size. // as part of the app's cache size.
File(apkFilePath).delete(); File(apkFilePath).delete();
} }
outFile = File('${workDir.path}/out.apk'); outFile = File('${workDir.path}/out.apk');
final Directory tmpDir = final Directory tmpDir =
Directory('${workDir.path}/revanced-temporary-files'); Directory('${workDir.path}/revanced-temporary-files');
try { try {
await patcherChannel.invokeMethod( await patcherChannel.invokeMethod(
'runPatcher', 'runPatcher',
{ {
'inFilePath': inApkFile.path, 'inFilePath': inApkFile.path,
'outFilePath': outFile!.path, 'outFilePath': outFile!.path,
'integrationsPath': integrationsFile.path, 'selectedPatches': selectedPatches.map((p) => p.name).toList(),
'selectedPatches': selectedPatches.map((p) => p.name).toList(), 'options': options,
'options': options, 'tmpDirPath': tmpDir.path,
'tmpDirPath': tmpDir.path, 'keyStoreFilePath': _keyStoreFile.path,
'keyStoreFilePath': _keyStoreFile.path, 'keystorePassword': _managerAPI.getKeystorePassword(),
'keystorePassword': _managerAPI.getKeystorePassword(), },
}, );
); } on Exception catch (e) {
} on Exception catch (e) { if (kDebugMode) {
if (kDebugMode) { print(e);
print(e);
}
} }
} }
} }

View File

@ -1,7 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
@ -31,7 +29,7 @@ class RevancedAPI {
final Map<String, List<dynamic>> contributors = {}; final Map<String, List<dynamic>> contributors = {};
try { try {
final response = await _dio.get('/contributors'); final response = await _dio.get('/contributors');
final List<dynamic> repositories = response.data['repositories']; final List<dynamic> repositories = response.data;
for (final Map<String, dynamic> repo in repositories) { for (final Map<String, dynamic> repo in repositories) {
final String name = repo['name']; final String name = repo['name'];
contributors[name] = repo['contributors']; contributors[name] = repo['contributors'];
@ -46,21 +44,15 @@ class RevancedAPI {
} }
Future<Map<String, dynamic>?> _getLatestRelease( Future<Map<String, dynamic>?> _getLatestRelease(
String extension, String toolName,
String repoName,
) { ) {
if (!locator<ManagerAPI>().getDownloadConsent()) { if (!locator<ManagerAPI>().getDownloadConsent()) {
return Future(() => null); return Future(() => null);
} }
return getToolsLock.synchronized(() async { return getToolsLock.synchronized(() async {
try { try {
final response = await _dio.get('/tools'); final response = await _dio.get('/$toolName/latest');
final List<dynamic> tools = response.data['tools']; return response.data;
return tools.firstWhereOrNull(
(t) =>
(t['repository'] as String) == repoName &&
(t['name'] as String).endsWith(extension),
);
} on Exception catch (e) { } on Exception catch (e) {
if (kDebugMode) { if (kDebugMode) {
print(e); print(e);
@ -71,13 +63,11 @@ class RevancedAPI {
} }
Future<String?> getLatestReleaseVersion( Future<String?> getLatestReleaseVersion(
String extension, String toolName,
String repoName,
) async { ) async {
try { try {
final Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, toolName,
repoName,
); );
if (release != null) { if (release != null) {
return release['version']; return release['version'];
@ -92,16 +82,14 @@ class RevancedAPI {
} }
Future<File?> getLatestReleaseFile( Future<File?> getLatestReleaseFile(
String extension, String toolName,
String repoName,
) async { ) async {
try { try {
final Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, toolName,
repoName,
); );
if (release != null) { if (release != null) {
final String url = release['browser_download_url']; final String url = release['assets'][0]['download_url'];
return await _downloadManager.getSingleFile(url); return await _downloadManager.getSingleFile(url);
} }
} on Exception catch (e) { } on Exception catch (e) {
@ -129,13 +117,10 @@ class RevancedAPI {
} }
Future<File?> downloadManager() async { Future<File?> downloadManager() async {
final Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease('manager');
'.apk',
'revanced/revanced-manager',
);
File? outputFile; File? outputFile;
await for (final result in _downloadManager.getFileStream( await for (final result in _downloadManager.getFileStream(
release!['browser_download_url'] as String, release!['download_url'] as String,
)) { )) {
if (result is DownloadProgress) { if (result is DownloadProgress) {
final totalSize = result.totalSize ?? 10000000; final totalSize = result.totalSize ?? 10000000;
@ -152,17 +137,15 @@ class RevancedAPI {
} }
Future<String?> getLatestReleaseTime( Future<String?> getLatestReleaseTime(
String extension, String toolName,
String repoName,
) async { ) async {
try { try {
final Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, toolName,
repoName,
); );
if (release != null) { if (release != null) {
final DateTime timestamp = final DateTime timestamp =
DateTime.parse(release['timestamp'] as String); DateTime.parse(release['created_at'] as String);
return format(timestamp, locale: 'en_short'); return format(timestamp, locale: 'en_short');
} }
} on Exception catch (e) { } on Exception catch (e) {

View File

@ -40,11 +40,6 @@ class ContributorsView extends StatelessWidget {
contributors: model.patchesContributors, contributors: model.patchesContributors,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
ContributorsCard(
title: 'ReVanced Integrations',
contributors: model.integrationsContributors,
),
const SizedBox(height: 20),
ContributorsCard( ContributorsCard(
title: 'ReVanced CLI', title: 'ReVanced CLI',
contributors: model.cliContributors, contributors: model.cliContributors,

View File

@ -6,19 +6,18 @@ class ContributorsViewModel extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
List<dynamic> patcherContributors = []; List<dynamic> patcherContributors = [];
List<dynamic> patchesContributors = []; List<dynamic> patchesContributors = [];
List<dynamic> integrationsContributors = [];
List<dynamic> cliContributors = []; List<dynamic> cliContributors = [];
List<dynamic> managerContributors = []; List<dynamic> managerContributors = [];
String repoName(String repo) => repo.split('/').last;
Future<void> getContributors() async { Future<void> getContributors() async {
final Map<String, List<dynamic>> contributors = final Map<String, List<dynamic>> contributors =
await _managerAPI.getContributors(); await _managerAPI.getContributors();
patcherContributors = contributors[_managerAPI.defaultPatcherRepo] ?? []; patcherContributors = contributors[repoName(_managerAPI.defaultPatcherRepo)] ?? [];
patchesContributors = contributors[_managerAPI.defaultPatchesRepo] ?? []; patchesContributors = contributors[repoName(_managerAPI.defaultPatchesRepo)] ?? [];
integrationsContributors = cliContributors = contributors[repoName(_managerAPI.defaultCliRepo)] ?? [];
contributors[_managerAPI.defaultIntegrationsRepo] ?? []; managerContributors = contributors[repoName(_managerAPI.defaultManagerRepo)] ?? [];
cliContributors = contributors[_managerAPI.defaultCliRepo] ?? [];
managerContributors = contributors[_managerAPI.defaultManagerRepo] ?? [];
notifyListeners(); notifyListeners();
} }
} }

View File

@ -311,11 +311,8 @@ class HomeViewModel extends BaseViewModel {
_toast.showBottom(t.homeView.downloadingMessage); _toast.showBottom(t.homeView.downloadingMessage);
final String patchesVersion = final String patchesVersion =
await _managerAPI.getLatestPatchesVersion() ?? '0.0.0'; await _managerAPI.getLatestPatchesVersion() ?? '0.0.0';
final String integrationsVersion = if (patchesVersion != '0.0.0') {
await _managerAPI.getLatestIntegrationsVersion() ?? '0.0.0';
if (patchesVersion != '0.0.0' && integrationsVersion != '0.0.0') {
await _managerAPI.setCurrentPatchesVersion(patchesVersion); await _managerAPI.setCurrentPatchesVersion(patchesVersion);
await _managerAPI.setCurrentIntegrationsVersion(integrationsVersion);
_toast.showBottom(t.homeView.downloadedMessage); _toast.showBottom(t.homeView.downloadedMessage);
forceRefresh(context); forceRefresh(context);
} else { } else {

View File

@ -330,7 +330,6 @@ class InstallerViewModel extends BaseViewModel {
'Version compatibility check: ${_managerAPI.isVersionCompatibilityCheckEnabled()}', 'Version compatibility check: ${_managerAPI.isVersionCompatibilityCheckEnabled()}',
'Show universal patches: ${_managerAPI.areUniversalPatchesEnabled()}', 'Show universal patches: ${_managerAPI.areUniversalPatchesEnabled()}',
'Patches source: ${_managerAPI.getPatchesRepo()}', 'Patches source: ${_managerAPI.getPatchesRepo()}',
'Integration source: ${_managerAPI.getIntegrationsRepo()}', //
'\n- Logs', '\n- Logs',
logsTrimmed.join('\n'), logsTrimmed.join('\n'),

View File

@ -44,20 +44,20 @@ class PatchOptionsView extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
for (final Option option in model.modifiedOptions) for (final Option option in model.modifiedOptions)
if (option.valueType == 'String' || if (option.type == 'kotlin.String' ||
option.valueType == 'Int') option.type == 'kotlin.Int')
IntAndStringPatchOption( IntAndStringPatchOption(
patchOption: option, patchOption: option,
model: model, model: model,
) )
else if (option.valueType == 'Boolean') else if (option.type == 'kotlin.Boolean')
BooleanPatchOption( BooleanPatchOption(
patchOption: option, patchOption: option,
model: model, model: model,
) )
else if (option.valueType == 'StringArray' || else if (option.type == 'kotlin.collections.List<kotlin.String>' ||
option.valueType == 'IntArray' || option.type == 'kotlin.collections.List<kotlin.Int>' ||
option.valueType == 'LongArray') option.type == 'kotlin.collections.List<kotlin.Long>')
IntStringLongListPatchOption( IntStringLongListPatchOption(
patchOption: option, patchOption: option,
model: model, model: model,

View File

@ -74,7 +74,7 @@ class PatchOptionsViewModel extends BaseViewModel {
title: option.title, title: option.title,
description: option.description, description: option.description,
values: option.values, values: option.values,
valueType: option.valueType, type: option.type,
value: value, value: value,
required: option.required, required: option.required,
key: option.key, key: option.key,
@ -90,7 +90,7 @@ class PatchOptionsViewModel extends BaseViewModel {
title: option.title, title: option.title,
description: option.description, description: option.description,
values: option.values, values: option.values,
valueType: option.valueType, type: option.type,
value: option.value is List ? option.value.toList() : option.value, value: option.value is List ? option.value.toList() : option.value,
required: option.required, required: option.required,
key: option.key, key: option.key,

View File

@ -14,16 +14,11 @@ class SManageSources extends BaseViewModel {
final TextEditingController _orgPatSourceController = TextEditingController(); final TextEditingController _orgPatSourceController = TextEditingController();
final TextEditingController _patSourceController = TextEditingController(); final TextEditingController _patSourceController = TextEditingController();
final TextEditingController _orgIntSourceController = TextEditingController();
final TextEditingController _intSourceController = TextEditingController();
Future<void> showSourcesDialog(BuildContext context) async { Future<void> showSourcesDialog(BuildContext context) async {
final String patchesRepo = _managerAPI.getPatchesRepo(); final String patchesRepo = _managerAPI.getPatchesRepo();
final String integrationsRepo = _managerAPI.getIntegrationsRepo();
_orgPatSourceController.text = patchesRepo.split('/')[0]; _orgPatSourceController.text = patchesRepo.split('/')[0];
_patSourceController.text = patchesRepo.split('/')[1]; _patSourceController.text = patchesRepo.split('/')[1];
_orgIntSourceController.text = integrationsRepo.split('/')[0];
_intSourceController.text = integrationsRepo.split('/')[1];
return showDialog( return showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
@ -72,38 +67,6 @@ class SManageSources extends BaseViewModel {
hintText: patchesRepo.split('/')[1], hintText: patchesRepo.split('/')[1],
), ),
), ),
const SizedBox(height: 8),
// Integrations owner's name
TextField(
controller: _orgIntSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: Icon(
Icons.merge_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.orgIntegrationsLabel,
hintText: integrationsRepo.split('/')[0],
),
),
const SizedBox(height: 8),
// Integrations repository's name
TextField(
controller: _intSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: const Icon(
Icons.merge_outlined,
color: Colors.transparent,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.sourcesIntegrationsLabel,
hintText: integrationsRepo.split('/')[1],
),
),
const SizedBox(height: 20), const SizedBox(height: 20),
Text(t.settingsView.sourcesUpdateNote), Text(t.settingsView.sourcesUpdateNote),
], ],
@ -113,8 +76,6 @@ class SManageSources extends BaseViewModel {
onPressed: () { onPressed: () {
_orgPatSourceController.clear(); _orgPatSourceController.clear();
_patSourceController.clear(); _patSourceController.clear();
_orgIntSourceController.clear();
_intSourceController.clear();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text(t.cancelButton), child: Text(t.cancelButton),
@ -124,11 +85,7 @@ class SManageSources extends BaseViewModel {
_managerAPI.setPatchesRepo( _managerAPI.setPatchesRepo(
'${_orgPatSourceController.text.trim()}/${_patSourceController.text.trim()}', '${_orgPatSourceController.text.trim()}/${_patSourceController.text.trim()}',
); );
_managerAPI.setIntegrationsRepo(
'${_orgIntSourceController.text.trim()}/${_intSourceController.text.trim()}',
);
_managerAPI.setCurrentPatchesVersion('0.0.0'); _managerAPI.setCurrentPatchesVersion('0.0.0');
_managerAPI.setCurrentIntegrationsVersion('0.0.0');
_managerAPI.setLastUsedPatchesVersion(); _managerAPI.setLastUsedPatchesVersion();
_toast.showBottom(t.settingsView.restartAppForChanges); _toast.showBottom(t.settingsView.restartAppForChanges);
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -154,9 +111,7 @@ class SManageSources extends BaseViewModel {
FilledButton( FilledButton(
onPressed: () { onPressed: () {
_managerAPI.setPatchesRepo(''); _managerAPI.setPatchesRepo('');
_managerAPI.setIntegrationsRepo('');
_managerAPI.setCurrentPatchesVersion('0.0.0'); _managerAPI.setCurrentPatchesVersion('0.0.0');
_managerAPI.setCurrentIntegrationsVersion('0.0.0');
_toast.showBottom(t.settingsView.restartAppForChanges); _toast.showBottom(t.settingsView.restartAppForChanges);
Navigator.of(context) Navigator.of(context)
..pop() ..pop()

View File

@ -56,7 +56,6 @@ class SettingsViewModel extends BaseViewModel {
void useAlternativeSources(bool value) { void useAlternativeSources(bool value) {
_managerAPI.useAlternativeSources(value); _managerAPI.useAlternativeSources(value);
_managerAPI.setCurrentPatchesVersion('0.0.0'); _managerAPI.setCurrentPatchesVersion('0.0.0');
_managerAPI.setCurrentIntegrationsVersion('0.0.0');
_managerAPI.setLastUsedPatchesVersion(); _managerAPI.setLastUsedPatchesVersion();
notifyListeners(); notifyListeners();
} }

View File

@ -138,7 +138,7 @@ class IntStringLongListPatchOption extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<dynamic> values = List.from(patchOption.value ?? []); final List<dynamic> values = List.from(patchOption.value ?? []);
final ValueNotifier patchOptionValue = ValueNotifier(values); final ValueNotifier patchOptionValue = ValueNotifier(values);
final String type = patchOption.valueType; final String type = patchOption.type;
String getKey(dynamic value) { String getKey(dynamic value) {
if (value != null && patchOption.values != null) { if (value != null && patchOption.values != null) {
@ -408,12 +408,12 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isStringOption = widget.patchOption.valueType.contains('String'); final bool isStringOption = widget.patchOption.type.contains('String');
final bool isArrayOption = widget.patchOption.valueType.contains('Array'); final bool isListOption = widget.patchOption.type.contains('List');
selectedKey = selectedKey == '' ? selectedKey : widget.selectedKey; selectedKey = selectedKey == '' ? selectedKey : widget.selectedKey;
final bool isValueArray = widget.value?.startsWith('[') ?? false; final bool isValueArray = widget.value?.startsWith('[') ?? false;
final bool shouldResetValue = final bool shouldResetValue =
!isStringOption && isArrayOption && selectedKey == '' && isValueArray; !isStringOption && isListOption && selectedKey == '' && isValueArray;
controller.text = shouldResetValue ? '' : widget.value ?? ''; controller.text = shouldResetValue ? '' : widget.value ?? '';
defaultValue ??= controller.text; defaultValue ??= controller.text;
return Column( return Column(
@ -479,7 +479,7 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
} else { } else {
controller.text = widget.patchOption.values![value].toString(); controller.text = widget.patchOption.values![value].toString();
widget.onChanged( widget.onChanged(
isArrayOption isListOption
? widget.patchOption.values![value] ? widget.patchOption.values![value]
: controller.text, : controller.text,
); );
@ -492,9 +492,9 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
if (selectedKey == '') if (selectedKey == '')
TextFormField( TextFormField(
inputFormatters: [ inputFormatters: [
if (widget.patchOption.valueType.contains('Int')) if (widget.patchOption.type.contains('Int'))
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
if (widget.patchOption.valueType.contains('Long')) if (widget.patchOption.type.contains('Long'))
FilteringTextInputFormatter.allow(RegExp(r'^[0-9]*\.?[0-9]*')), FilteringTextInputFormatter.allow(RegExp(r'^[0-9]*\.?[0-9]*')),
], ],
controller: controller, controller: controller,
@ -505,7 +505,7 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
tooltip: t.patchOptionsView.tooltip, tooltip: t.patchOptionsView.tooltip,
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
return [ return [
if (isArrayOption) if (isListOption)
PopupMenuItem( PopupMenuItem(
value: 'remove', value: 'remove',
child: Text(t.remove), child: Text(t.remove),

View File

@ -33,7 +33,7 @@ bool hasUnsupportedRequiredOption(List<Option> options, Patch patch) {
option.key, option.key,
) == ) ==
null) { null) {
requiredOptionsType.add(option.valueType); requiredOptionsType.add(option.type);
} }
} }
for (final String optionType in requiredOptionsType) { for (final String optionType in requiredOptionsType) {