diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..dfdd7b6a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,103 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build (Serializer)", + "type": "shell", + "command": "flutter packages pub run build_runner build --delete-conflicting-outputs", + "problemMatcher": [] + }, + { + "label": "Build (Android)", + "type": "shell", + "command": "flutter build apk", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Install (Android)", + "type": "shell", + "command": "adb install build\\app\\outputs\\flutter-apk\\app-release.apk", + "problemMatcher": [] + }, + { + "label": "Clean (Flutter)", + "type": "shell", + "command": "flutter clean && flutter pub get", + "problemMatcher": [] + }, + { + "label": "Clean (Serializer)", + "type": "shell", + "command": "flutter packages pub run build_runner clean", + "problemMatcher": [] + }, + { + "label": "Build all (Android)", + "dependsOrder": "sequence", + "dependsOn": [ + "Build (Serializer)", + "Build (Android)" + ], + "problemMatcher": [] + }, + { + "label": "Clean all", + "dependsOrder": "sequence", + "dependsOn": [ + "Clean (Flutter)", + "Clean (Serializer)" + ], + "problemMatcher": [] + }, + { + "label": "Clean all & Build all (Android)", + "dependsOrder": "sequence", + "dependsOn": [ + "Clean all", + "Build all (Android)" + ], + "problemMatcher": [] + }, + { + "label": "Clean all & Install (Android)", + "dependsOrder": "sequence", + "dependsOn": [ + "Clean all", + "Build all (Android)", + "Install (Android)", + ], + "problemMatcher": [] + }, + { + "label": "Build & Install (Android)", + "dependsOrder": "sequence", + "dependsOn": [ + "Build (Android)", + "Install (Android)" + ], + "problemMatcher": [] + }, + { + "label": "Validate dependencies", + "type": "shell", + "command": "flutter pub pub run dependency_validator", + "problemMatcher": [] + }, + { + "label": "Show upgradable dependencies", + "type": "shell", + "command": "flutter pub outdated", + "problemMatcher": [] + }, + { + "label": "Validate translations", + "type": "shell", + "command": "flutter pub run flutter_i18n diff en.json pt.json", + "problemMatcher": [] + } + ] +} diff --git a/android/app/build.gradle b/android/app/build.gradle index d1216884..7f0fff27 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -43,10 +43,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "app.revanced.revanced_manager_flutter" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() @@ -55,8 +52,6 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } @@ -68,4 +63,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + // ReVanced + implementation "app.revanced:revanced-patcher:3.3.1" } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index fd9dcd6b..0480c468 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,8 +1,4 @@ - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 61043029..59ca22ac 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ @@ -17,10 +17,6 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - - diff --git a/android/app/src/main/kotlin/app/revanced/revanced_manager_flutter/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/revanced_manager_flutter/MainActivity.kt index b6784df9..a9ea2e68 100644 --- a/android/app/src/main/kotlin/app/revanced/revanced_manager_flutter/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/revanced_manager_flutter/MainActivity.kt @@ -1,6 +1,87 @@ package app.revanced.revanced_manager_flutter +import androidx.annotation.NonNull +import app.revanced.patcher.data.Data +import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages +import app.revanced.patcher.extensions.PatchExtensions.description +import app.revanced.patcher.extensions.PatchExtensions.patchName +import app.revanced.patcher.extensions.PatchExtensions.version +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.util.patch.implementation.DexPatchBundle +import dalvik.system.DexClassLoader import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel -class MainActivity: FlutterActivity() { +class MainActivity : FlutterActivity() { + private val CHANNEL = "app.revanced/patcher" + private var patches = mutableListOf>>() + + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> + when (call.method) { + "loadPatches" -> { + val pathBundlesPaths = call.argument>("pathBundlesPaths") + if (pathBundlesPaths != null) { + loadPatches(pathBundlesPaths) + result.success("OK") + } else { + result.notImplemented() + } + } + "getCompatiblePackages" -> result.success(getCompatiblePackages()) + "getFilteredPatches" -> { + val targetPackage = call.argument("targetPackage") + val targetVersion = call.argument("targetVersion") + val ignoreVersion = call.argument("ignoreVersion") + if (targetPackage != null && targetVersion != null && ignoreVersion != null) { + result.success(getFilteredPatches(targetPackage, targetVersion, ignoreVersion)) + } else { + result.notImplemented() + } + } + else -> result.notImplemented() + } + } + } + + fun loadPatches(pathBundlesPaths: List) { + pathBundlesPaths.forEach { path -> + patches.addAll(DexPatchBundle( + path, DexClassLoader( + path, + context.cacheDir.path, + null, + javaClass.classLoader + ) + ).loadPatches()) + } + } + + fun getCompatiblePackages(): List { + val filteredPackages = mutableListOf() + patches.forEach patch@{ patch -> + patch.compatiblePackages?.forEach { pkg -> + filteredPackages.add(pkg.name) + } + } + return filteredPackages.distinct() + } + + fun getFilteredPatches(targetPackage: String, targetVersion: String, ignoreVersion: Boolean): List> { + val filteredPatches = mutableListOf>() + patches.forEach patch@{ patch -> + patch.compatiblePackages?.forEach { pkg -> + if (pkg.name == targetPackage && (ignoreVersion || pkg.versions.isNotEmpty() || pkg.versions.contains(targetVersion))) { + var p = mutableMapOf(); + p.put("name", patch.patchName); + p.put("version", patch.version); + p.put("description", patch.description); + filteredPatches.add(p) + } + } + } + return filteredPatches + } } diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index fd9dcd6b..0480c468 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,8 +1,4 @@ - diff --git a/android/build.gradle b/android/build.gradle index 83ae2200..56665120 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,14 +15,19 @@ allprojects { repositories { google() mavenCentral() + maven { + url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") + credentials { + username = (project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")) as String + password = (project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")) as String + } + } } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { project.evaluationDependsOn(':app') } diff --git a/lib/models/patch.dart b/lib/models/patch.dart new file mode 100644 index 00000000..b9231198 --- /dev/null +++ b/lib/models/patch.dart @@ -0,0 +1,13 @@ +class Patch { + final String name; + final String simpleName; + final String version; + final String description; + + Patch({ + required this.name, + required this.simpleName, + required this.version, + required this.description, + }); +} diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index 5696be19..a28eefc2 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -8,7 +8,13 @@ class GithubAPI { Future latestRelease(String org, repoName) async { var latestRelease = await github.repositories .getLatestRelease(RepositorySlug(org, repoName)); - var dlurl = latestRelease.assets?.first.browserDownloadUrl; + var dlurl = latestRelease.assets + ?.firstWhere((asset) => + asset.name != null && + asset.name!.endsWith('.dex') && + !asset.name!.contains('-sources') && + !asset.name!.contains('-javadoc')) + .browserDownloadUrl; return dlurl; } diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart new file mode 100644 index 00000000..91630da4 --- /dev/null +++ b/lib/services/patcher_api.dart @@ -0,0 +1,100 @@ +import 'dart:io'; +import 'package:flutter/services.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:installed_apps/app_info.dart'; +import 'package:installed_apps/installed_apps.dart'; +import 'package:revanced_manager_flutter/models/patch.dart'; +import 'package:revanced_manager_flutter/services/github_api.dart'; +import 'package:revanced_manager_flutter/utils/string.dart'; + +class PatcherService { + File? _patchBundleFile; + final List _filteredPackages = []; + final Map> _filteredPatches = >{}; + final GithubAPI githubAPI = GithubAPI(); + static const platform = MethodChannel('app.revanced/patcher'); + static final PatcherService _instance = PatcherService.internal(); + factory PatcherService() => _instance; + PatcherService.internal(); + + Future loadPatches() async { + if (_patchBundleFile == null) { + String? dexFileUrl = + await githubAPI.latestRelease('revanced', 'revanced-patches'); + if (dexFileUrl != null) { + _patchBundleFile = + await DefaultCacheManager().getSingleFile(dexFileUrl); + try { + await platform.invokeMethod( + 'loadPatches', + { + 'pathBundlesPaths': [_patchBundleFile!.absolute.path], + }, + ); + } on PlatformException { + _patchBundleFile = null; + } + } + } + } + + Future> getFilteredInstalledApps() async { + if (_patchBundleFile != null && _filteredPackages.isEmpty) { + List all = await InstalledApps.getInstalledApps(false, true); + try { + List? patchesPackages = + await platform.invokeListMethod('getCompatiblePackages'); + if (patchesPackages != null) { + for (AppInfo app in all) { + if (patchesPackages.contains(app.packageName)) { + _filteredPackages.add(app); + } + } + } + } on Exception { + return List.empty(); + } + } + return _filteredPackages; + } + + Future?> getFilteredPatches(AppInfo? targetApp) async { + if (_patchBundleFile != null && targetApp != null) { + if (_filteredPatches[targetApp.packageName] == null || + _filteredPatches[targetApp.packageName]!.isEmpty) { + _filteredPatches[targetApp.packageName!] = []; + try { + var patches = await platform.invokeListMethod>( + 'getFilteredPatches', + { + 'targetPackage': targetApp.packageName, + 'targetVersion': targetApp.versionName, + 'ignoreVersion': true, + }, + ); + if (patches != null) { + for (var patch in patches) { + _filteredPatches[targetApp.packageName]!.add( + Patch( + name: patch['name'], + simpleName: (patch['name'] as String) + .replaceAll('-', ' ') + .split('-') + .join(' ') + .toTitleCase(), + version: patch['version'] ?? 'unknown', + description: patch['description'] ?? 'unknown', + ), + ); + } + } + } on Exception { + return List.empty(); + } + } + } else { + return List.empty(); + } + return _filteredPatches[targetApp.packageName]; + } +} diff --git a/lib/utils/string.dart b/lib/utils/string.dart new file mode 100644 index 00000000..47e54264 --- /dev/null +++ b/lib/utils/string.dart @@ -0,0 +1,8 @@ +extension StringCasingExtension on String { + String toCapitalized() => + length > 0 ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : ''; + String toTitleCase() => replaceAll(RegExp(' +'), ' ') + .split(' ') + .map((str) => str.toCapitalized()) + .join(' '); +} diff --git a/pubspec.lock b/pubspec.lock index 4f90dc51..af3a996a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -209,6 +209,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" flutter_lints: dependency: "direct dev" description: @@ -312,6 +319,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.4" + installed_apps: + dependency: "direct main" + description: + name: installed_apps + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" io: dependency: transitive description: @@ -327,12 +341,19 @@ packages: source: hosted version: "0.6.4" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation url: "https://pub.dartlang.org" source: hosted version: "4.6.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.3.1" lints: dependency: transitive description: @@ -466,6 +487,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" petitparser: dependency: transitive description: @@ -529,6 +557,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.5" shelf: dependency: transitive description: @@ -555,6 +590,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" source_span: dependency: transitive description: @@ -562,6 +604,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1+1" stack_trace: dependency: transitive description: @@ -618,6 +674,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+2" term_glyph: dependency: transitive description: @@ -653,6 +716,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.4" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 92da33f9..c96052d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,41 +1,23 @@ name: revanced_manager_flutter -description: A new Flutter project. +description: The unofficial ReVanced Manager based on Flutter. +homepage: https://github.com/Aunali321/revanced-manager -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.17.5 <3.0.0" -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + flutter_cache_manager: ^3.3.0 flutter_svg: ^1.1.1+1 google_fonts: ^3.0.1 + installed_apps: ^1.3.1 + json_annotation: ^4.6.0 #networking http: ^0.13.4 @@ -59,54 +41,12 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^2.0.1 build_runner: ^2.2.0 injectable_generator: ^1.5.4 + json_serializable: ^6.3.1 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: assets: - lib/assets/images/ - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Manrope - # fonts: - # - asset: lib/assets/fonts/Manrope.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages