mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-19 01:03:56 +00:00
Compare commits
9 Commits
v1.23.0-de
...
v1.23.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9552b2ebc5 | ||
|
|
7e3afe0cb2 | ||
|
|
7b7d91d661 | ||
|
|
44b8d4ceee | ||
|
|
aaa97ebb71 | ||
|
|
d99e5af384 | ||
|
|
c47c7c0a88 | ||
|
|
3e32c0fd90 | ||
|
|
a45d9598cc |
@@ -7,7 +7,7 @@ plugins {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "app.revanced.manager.flutter"
|
namespace = "app.revanced.manager.flutter"
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
ndkVersion = "27.0.12077973"
|
ndkVersion = "27.0.12077973"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -24,9 +24,19 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "app.revanced.manager.flutter"
|
applicationId = "app.revanced.manager.flutter"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 35
|
||||||
versionCode = flutter.versionCode
|
versionCode = flutter.versionCode
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
|
|
||||||
|
resValue("string", "app_name", "ReVanced Manager")
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationVariants.all {
|
||||||
|
outputs.all {
|
||||||
|
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
||||||
|
|
||||||
|
outputFileName = "revanced-manager-$versionName.apk"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -37,7 +47,6 @@ android {
|
|||||||
signingConfig = signingConfigs["debug"]
|
signingConfig = signingConfigs["debug"]
|
||||||
|
|
||||||
ndk.abiFilters += setOf("armeabi-v7a", "arm64-v8a", "x86_64")
|
ndk.abiFilters += setOf("armeabi-v7a", "arm64-v8a", "x86_64")
|
||||||
setProperty("archivesBaseName", "revanced-manager-v${flutter.versionName}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
release {
|
||||||
@@ -52,19 +61,21 @@ android {
|
|||||||
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
||||||
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resValue("string", "app_name", "ReVanced Manager")
|
||||||
} else {
|
} else {
|
||||||
resValue("string", "app_name", "ReVanced Manager (Debug)")
|
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
|
|
||||||
signingConfig = signingConfigs["debug"]
|
signingConfig = signingConfigs["debug"]
|
||||||
}
|
|
||||||
|
|
||||||
resValue("string", "app_name", "ReVanced Manager")
|
resValue("string", "app_name", "ReVanced Manager (Debug signed)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
resValue("string", "app_name", "ReVanced Manager (Debug)")
|
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
|
|
||||||
|
resValue("string", "app_name", "ReVanced Manager (Debug)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +91,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package app.revanced.manager.flutter
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageInfo
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
@@ -17,9 +16,8 @@ import java.security.MessageDigest
|
|||||||
class ExportSettingsActivity : Activity() {
|
class ExportSettingsActivity : Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val callingPackageName = getCallingPackage()!!
|
|
||||||
|
|
||||||
if (getFingerprint(callingPackageName) == getFingerprint(getPackageName())) {
|
if (getFingerprint(callingPackage!!) == getFingerprint(packageName)) {
|
||||||
// Create JSON Object
|
// Create JSON Object
|
||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
|
|
||||||
@@ -64,7 +62,7 @@ class ExportSettingsActivity : Activity() {
|
|||||||
fun getFingerprint(packageName: String): String {
|
fun getFingerprint(packageName: String): String {
|
||||||
// Get the signature of the app that matches the package name
|
// Get the signature of the app that matches the package name
|
||||||
val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
|
val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
|
||||||
val signature = packageInfo.signatures[0]
|
val signature = packageInfo.signatures!![0]
|
||||||
|
|
||||||
// Get the raw certificate data
|
// Get the raw certificate data
|
||||||
val rawCert = signature.toByteArray()
|
val rawCert = signature.toByteArray()
|
||||||
|
|||||||
@@ -283,7 +283,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
tmpDir,
|
tmpDir,
|
||||||
Aapt.binary(applicationContext).absolutePath,
|
Aapt.binary(applicationContext).absolutePath,
|
||||||
tmpDir.path,
|
tmpDir.path,
|
||||||
true // TODO: Add option to disable this
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import com.android.build.api.dsl.CommonExtension
|
import com.android.build.api.dsl.CommonExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
@@ -17,6 +18,14 @@ allprojects {
|
|||||||
|
|
||||||
layout.buildDirectory = File("../build")
|
layout.buildDirectory = File("../build")
|
||||||
|
|
||||||
|
project(":screenshot_callback") {
|
||||||
|
tasks.withType<KotlinCompile>().configureEach {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
extensions.findByName("android")?.let {
|
extensions.findByName("android")?.let {
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ android.useAndroidX=true
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
|
||||||
android.nonTransitiveRClass=false
|
android.nonTransitiveRClass=false
|
||||||
android.nonFinalResIds=false
|
android.nonFinalResIds=false
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
revanced-patcher = "20.0.2"
|
revanced-patcher = "21.0.0"
|
||||||
revanced-library = "3.0.1"
|
revanced-library = "3.0.2"
|
||||||
desugar_jdk_libs = "2.1.2"
|
desugar_jdk_libs = "2.1.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||||
|
|||||||
@@ -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.5.0" apply false
|
id("com.android.application") version "8.7.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
|
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,12 @@
|
|||||||
"deleteTempDirLabel": "Delete temporary files",
|
"deleteTempDirLabel": "Delete temporary files",
|
||||||
"deleteTempDirHint": "Delete unused temporary files",
|
"deleteTempDirHint": "Delete unused temporary files",
|
||||||
"deletedTempDir": "Temporary files deleted",
|
"deletedTempDir": "Temporary files deleted",
|
||||||
|
"exportSettingsLabel": "Export settings",
|
||||||
|
"exportSettingsHint": "Export settings to a JSON file",
|
||||||
|
"exportedSettings": "Settings exported",
|
||||||
|
"importSettingsLabel": "Import settings",
|
||||||
|
"importSettingsHint": "Import settings from a JSON file",
|
||||||
|
"importedSettings": "Settings imported",
|
||||||
"exportPatchesLabel": "Export patch selection",
|
"exportPatchesLabel": "Export patch selection",
|
||||||
"exportPatchesHint": "Export patch selection to a JSON file",
|
"exportPatchesHint": "Export patch selection to a JSON file",
|
||||||
"exportedPatches": "Patch selection exported",
|
"exportedPatches": "Patch selection exported",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ Learn how to configure ReVanced Manager.
|
|||||||
- 🔑 Keystore used to sign patched apps
|
- 🔑 Keystore used to sign patched apps
|
||||||
- 📄 Remembered selection of patches for each app
|
- 📄 Remembered selection of patches for each app
|
||||||
- ⚙️ Remembered patch options
|
- ⚙️ Remembered patch options
|
||||||
|
- 🛠️ Remembered settings
|
||||||
|
|
||||||
> ℹ️ Note
|
> ℹ️ Note
|
||||||
> These can be used to backup and restore or reset settings to default in case of issues.
|
> These can be used to backup and restore or reset settings to default in case of issues.
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class ManagerAPI {
|
|||||||
final String patcherRepo = 'revanced-patcher';
|
final String patcherRepo = 'revanced-patcher';
|
||||||
final String cliRepo = 'revanced-cli';
|
final String cliRepo = 'revanced-cli';
|
||||||
late SharedPreferences _prefs;
|
late SharedPreferences _prefs;
|
||||||
|
Map<String, List>? contributors;
|
||||||
List<Patch> patches = [];
|
List<Patch> patches = [];
|
||||||
List<Option> options = [];
|
List<Option> options = [];
|
||||||
Patch? selectedPatch;
|
Patch? selectedPatch;
|
||||||
@@ -44,7 +45,7 @@ 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/v3';
|
String defaultApiUrl = 'https://api.revanced.app/v4';
|
||||||
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';
|
||||||
@@ -66,16 +67,23 @@ class ManagerAPI {
|
|||||||
releaseBuild = !(await getCurrentManagerVersion()).contains('-dev');
|
releaseBuild = !(await getCurrentManagerVersion()).contains('-dev');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate to new API URL if not done yet as the old one is sunset.
|
final hasMigratedToNewMigrationSystem = _prefs.getBool('migratedToNewApiPrefSystem') ?? false;
|
||||||
final bool hasMigratedToLatestApi =
|
if (!hasMigratedToNewMigrationSystem) {
|
||||||
_prefs.getBool('migratedToLatestApiUrl') ?? false;
|
final apiUrl = getApiUrl().toLowerCase();
|
||||||
if (!hasMigratedToLatestApi) {
|
|
||||||
final String apiUrl = getApiUrl().toLowerCase();
|
final isReleases = apiUrl.contains('releases.revanced.app');
|
||||||
if (apiUrl.contains('releases.revanced.app') ||
|
final isV2 = apiUrl.contains('api.revanced.app/v2');
|
||||||
(apiUrl.contains('api.revanced.app') &&
|
final isV3 = apiUrl.contains('api.revanced.app/v3');
|
||||||
!apiUrl.contains('v3'))) {
|
|
||||||
await setApiUrl(''); // Reset to default.
|
if (isReleases || isV2 || isV3) {
|
||||||
_prefs.setBool('migratedToLatestApiUrl', true);
|
await resetApiUrl();
|
||||||
|
// At this point, the preference is removed.
|
||||||
|
// Now, no more migration is needed because:
|
||||||
|
// If the user touches the API URL,
|
||||||
|
// it will be remembered forever as intended.
|
||||||
|
// On the other hand, if the user resets it or sets it to the default,
|
||||||
|
// the URL will be updated whenever the app is updated.
|
||||||
|
_prefs.setBool('migratedToNewApiPrefSystem', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,12 +107,25 @@ class ManagerAPI {
|
|||||||
return _prefs.getString('apiUrl') ?? defaultApiUrl;
|
return _prefs.getString('apiUrl') ?? defaultApiUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setApiUrl(String url) async {
|
Future<void> resetApiUrl() async {
|
||||||
if (url.isEmpty || url == ' ') {
|
await _prefs.remove('apiUrl');
|
||||||
url = defaultApiUrl;
|
|
||||||
}
|
|
||||||
await _revancedAPI.clearAllCache();
|
await _revancedAPI.clearAllCache();
|
||||||
|
_toast.showBottom(t.settingsView.restartAppForChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setApiUrl(String url) async {
|
||||||
|
url = url.toLowerCase();
|
||||||
|
|
||||||
|
if (url == defaultApiUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url.startsWith('http')) {
|
||||||
|
url = 'https://$url';
|
||||||
|
}
|
||||||
|
|
||||||
await _prefs.setString('apiUrl', url);
|
await _prefs.setString('apiUrl', url);
|
||||||
|
await _revancedAPI.clearAllCache();
|
||||||
_toast.showBottom(t.settingsView.restartAppForChanges);
|
_toast.showBottom(t.settingsView.restartAppForChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +427,7 @@ class ManagerAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, List<dynamic>>> getContributors() async {
|
Future<Map<String, List<dynamic>>> getContributors() async {
|
||||||
return await _revancedAPI.getContributors();
|
return contributors ??= await _revancedAPI.getContributors();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Patch>> getPatches() async {
|
Future<List<Patch>> getPatches() async {
|
||||||
@@ -438,6 +459,10 @@ class ManagerAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<File?> downloadPatches() async {
|
Future<File?> downloadPatches() async {
|
||||||
|
if (!isUsingAlternativeSources()) {
|
||||||
|
return await _revancedAPI.getLatestReleaseFile('patches');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final String repoName = getPatchesRepo();
|
final String repoName = getPatchesRepo();
|
||||||
final String currentVersion = await getCurrentPatchesVersion();
|
final String currentVersion = await getCurrentPatchesVersion();
|
||||||
@@ -755,6 +780,36 @@ class ManagerAPI {
|
|||||||
return jsonDecode(string);
|
return jsonDecode(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String exportSettings() {
|
||||||
|
final Map<String, dynamic> settings = _prefs
|
||||||
|
.getKeys()
|
||||||
|
.fold<Map<String, dynamic>>({}, (Map<String, dynamic> map, String key) {
|
||||||
|
map[key] = _prefs.get(key);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
return jsonEncode(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> importSettings(String settings) async {
|
||||||
|
final Map<String, dynamic> settingsMap = jsonDecode(settings);
|
||||||
|
settingsMap.forEach((key, value) {
|
||||||
|
if (value is bool) {
|
||||||
|
_prefs.setBool(key, value);
|
||||||
|
} else if (value is int) {
|
||||||
|
_prefs.setInt(key, value);
|
||||||
|
} else if (value is double) {
|
||||||
|
_prefs.setDouble(key, value);
|
||||||
|
} else if (value is String) {
|
||||||
|
_prefs.setString(key, value);
|
||||||
|
} else if (value is List<dynamic>) {
|
||||||
|
_prefs.setStringList(
|
||||||
|
key,
|
||||||
|
value.map((a) => json.encode(a.toJson())).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void resetAllOptions() {
|
void resetAllOptions() {
|
||||||
_prefs.getKeys().where((key) => key.startsWith('patchOption-')).forEach(
|
_prefs.getKeys().where((key) => key.startsWith('patchOption-')).forEach(
|
||||||
(key) {
|
(key) {
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
|
|
||||||
@lazySingleton
|
@lazySingleton
|
||||||
class PatcherAPI {
|
class PatcherAPI {
|
||||||
static const patcherChannel =
|
static const patcherChannel = MethodChannel('app.revanced.manager.flutter/patcher');
|
||||||
MethodChannel('app.revanced.manager.flutter/patcher');
|
|
||||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
final RootAPI _rootAPI = RootAPI();
|
final RootAPI _rootAPI = RootAPI();
|
||||||
late Directory _dataDir;
|
late Directory _dataDir;
|
||||||
@@ -27,7 +26,7 @@ class PatcherAPI {
|
|||||||
late File _keyStoreFile;
|
late File _keyStoreFile;
|
||||||
List<Patch> _patches = [];
|
List<Patch> _patches = [];
|
||||||
List<Patch> _universalPatches = [];
|
List<Patch> _universalPatches = [];
|
||||||
List<String> _compatiblePackages = [];
|
Set<String> _compatiblePackages = {};
|
||||||
Map filteredPatches = <String, List<Patch>>{};
|
Map filteredPatches = <String, List<Patch>>{};
|
||||||
File? outFile;
|
File? outFile;
|
||||||
|
|
||||||
@@ -46,8 +45,8 @@ class PatcherAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getCompatiblePackages() {
|
Set<String> getCompatiblePackages() {
|
||||||
final List<String> compatiblePackages = [];
|
final Set<String> compatiblePackages = {};
|
||||||
for (final Patch patch in _patches) {
|
for (final Patch patch in _patches) {
|
||||||
for (final Package package in patch.compatiblePackages) {
|
for (final Package package in patch.compatiblePackages) {
|
||||||
if (!compatiblePackages.contains(package.name)) {
|
if (!compatiblePackages.contains(package.name)) {
|
||||||
@@ -66,16 +65,16 @@ class PatcherAPI {
|
|||||||
try {
|
try {
|
||||||
if (_patches.isEmpty) {
|
if (_patches.isEmpty) {
|
||||||
_patches = await _managerAPI.getPatches();
|
_patches = await _managerAPI.getPatches();
|
||||||
|
_universalPatches = getUniversalPatches();
|
||||||
|
_compatiblePackages = getCompatiblePackages();
|
||||||
}
|
}
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
_patches = List.empty();
|
_patches = List.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
_compatiblePackages = getCompatiblePackages();
|
|
||||||
_universalPatches = getUniversalPatches();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ApplicationWithIcon>> getFilteredInstalledApps(
|
Future<List<ApplicationWithIcon>> getFilteredInstalledApps(
|
||||||
@@ -84,6 +83,7 @@ class PatcherAPI {
|
|||||||
final List<ApplicationWithIcon> filteredApps = [];
|
final List<ApplicationWithIcon> filteredApps = [];
|
||||||
final bool allAppsIncluded =
|
final bool allAppsIncluded =
|
||||||
_universalPatches.isNotEmpty && showUniversalPatches;
|
_universalPatches.isNotEmpty && showUniversalPatches;
|
||||||
|
|
||||||
if (allAppsIncluded) {
|
if (allAppsIncluded) {
|
||||||
final appList = await DeviceApps.getInstalledApplications(
|
final appList = await DeviceApps.getInstalledApplications(
|
||||||
includeAppIcons: true,
|
includeAppIcons: true,
|
||||||
@@ -94,6 +94,7 @@ class PatcherAPI {
|
|||||||
filteredApps.add(app as ApplicationWithIcon);
|
filteredApps.add(app as ApplicationWithIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final packageName in _compatiblePackages) {
|
for (final packageName in _compatiblePackages) {
|
||||||
try {
|
try {
|
||||||
if (!filteredApps.any((app) => app.packageName == packageName)) {
|
if (!filteredApps.any((app) => app.packageName == packageName)) {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class RevancedAPI {
|
|||||||
}
|
}
|
||||||
return getToolsLock.synchronized(() async {
|
return getToolsLock.synchronized(() async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get('/$toolName/latest');
|
final response = await _dio.get('/$toolName');
|
||||||
return response.data;
|
return response.data;
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
@@ -89,7 +89,7 @@ class RevancedAPI {
|
|||||||
toolName,
|
toolName,
|
||||||
);
|
);
|
||||||
if (release != null) {
|
if (release != null) {
|
||||||
final String url = release['assets'][0]['download_url'];
|
final String url = release['download_url'];
|
||||||
return await _downloadManager.getSingleFile(url);
|
return await _downloadManager.getSingleFile(url);
|
||||||
}
|
}
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
|
|||||||
@@ -30,25 +30,13 @@ class ContributorsView extends StatelessWidget {
|
|||||||
sliver: SliverList(
|
sliver: SliverList(
|
||||||
delegate: SliverChildListDelegate.fixed(
|
delegate: SliverChildListDelegate.fixed(
|
||||||
<Widget>[
|
<Widget>[
|
||||||
ContributorsCard(
|
for (final String tool in model.contributors.keys) ...[
|
||||||
title: 'ReVanced Patcher',
|
ContributorsCard(
|
||||||
contributors: model.patcherContributors,
|
title: tool,
|
||||||
),
|
contributors: model.contributors[tool]!,
|
||||||
const SizedBox(height: 20),
|
),
|
||||||
ContributorsCard(
|
const SizedBox(height: 20),
|
||||||
title: 'ReVanced Patches',
|
],
|
||||||
contributors: model.patchesContributors,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
ContributorsCard(
|
|
||||||
title: 'ReVanced CLI',
|
|
||||||
contributors: model.cliContributors,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
ContributorsCard(
|
|
||||||
title: 'ReVanced Manager',
|
|
||||||
contributors: model.managerContributors,
|
|
||||||
),
|
|
||||||
SizedBox(height: MediaQuery.viewPaddingOf(context).bottom),
|
SizedBox(height: MediaQuery.viewPaddingOf(context).bottom),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,20 +4,10 @@ import 'package:stacked/stacked.dart';
|
|||||||
|
|
||||||
class ContributorsViewModel extends BaseViewModel {
|
class ContributorsViewModel extends BaseViewModel {
|
||||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
List<dynamic> patcherContributors = [];
|
Map<String, List<dynamic>> contributors = {};
|
||||||
List<dynamic> patchesContributors = [];
|
|
||||||
List<dynamic> cliContributors = [];
|
|
||||||
List<dynamic> managerContributors = [];
|
|
||||||
|
|
||||||
String repoName(String repo) => repo.split('/').last;
|
|
||||||
|
|
||||||
Future<void> getContributors() async {
|
Future<void> getContributors() async {
|
||||||
final Map<String, List<dynamic>> contributors =
|
contributors = await _managerAPI.getContributors();
|
||||||
await _managerAPI.getContributors();
|
|
||||||
patcherContributors = contributors[repoName(_managerAPI.defaultPatcherRepo)] ?? [];
|
|
||||||
patchesContributors = contributors[repoName(_managerAPI.defaultPatchesRepo)] ?? [];
|
|
||||||
cliContributors = contributors[repoName(_managerAPI.defaultCliRepo)] ?? [];
|
|
||||||
managerContributors = contributors[repoName(_managerAPI.defaultManagerRepo)] ?? [];
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ class SManageApiUrl extends BaseViewModel {
|
|||||||
final TextEditingController _apiUrlController = TextEditingController();
|
final TextEditingController _apiUrlController = TextEditingController();
|
||||||
|
|
||||||
Future<void> showApiUrlDialog(BuildContext context) async {
|
Future<void> showApiUrlDialog(BuildContext context) async {
|
||||||
final String apiUrl = _managerAPI.getApiUrl();
|
final apiUrl = _managerAPI.getApiUrl();
|
||||||
_apiUrlController.text = apiUrl.replaceAll('https://', '');
|
|
||||||
|
_apiUrlController.text = apiUrl;
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
@@ -60,11 +61,7 @@ class SManageApiUrl extends BaseViewModel {
|
|||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
String apiUrl = _apiUrlController.text;
|
_managerAPI.setApiUrl(_apiUrlController.text);
|
||||||
if (!apiUrl.startsWith('https')) {
|
|
||||||
apiUrl = 'https://$apiUrl';
|
|
||||||
}
|
|
||||||
_managerAPI.setApiUrl(apiUrl);
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(t.okButton),
|
child: Text(t.okButton),
|
||||||
@@ -87,7 +84,7 @@ class SManageApiUrl extends BaseViewModel {
|
|||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_managerAPI.setApiUrl('');
|
_managerAPI.resetApiUrl();
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
..pop()
|
..pop()
|
||||||
..pop();
|
..pop();
|
||||||
|
|||||||
@@ -222,6 +222,53 @@ class SettingsViewModel extends BaseViewModel {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> exportSettings() async {
|
||||||
|
try {
|
||||||
|
final String settings = _managerAPI.exportSettings();
|
||||||
|
final Directory tempDir = await getTemporaryDirectory();
|
||||||
|
final String filePath = '${tempDir.path}/manager_settings.json';
|
||||||
|
final File file = File(filePath);
|
||||||
|
await file.writeAsString(settings);
|
||||||
|
final String? result = await FlutterFileDialog.saveFile(
|
||||||
|
params: SaveFileDialogParams(
|
||||||
|
sourceFilePath: file.path,
|
||||||
|
fileName: 'manager_settings.json',
|
||||||
|
mimeTypesFilter: ['application/json'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
_toast.showBottom(t.settingsView.exportedSettings);
|
||||||
|
}
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> importSettings() async {
|
||||||
|
try {
|
||||||
|
final String? result = await FlutterFileDialog.pickFile(
|
||||||
|
params: const OpenFileDialogParams(
|
||||||
|
fileExtensionsFilter: ['json'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
final File inFile = File(result);
|
||||||
|
final String settings = inFile.readAsStringSync();
|
||||||
|
inFile.delete();
|
||||||
|
_managerAPI.importSettings(settings);
|
||||||
|
_toast.showBottom(t.settingsView.importedSettings);
|
||||||
|
_toast.showBottom(t.settingsView.restartAppForChanges);
|
||||||
|
}
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
_toast.showBottom(t.settingsView.jsonSelectorErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> exportPatches() async {
|
Future<void> exportPatches() async {
|
||||||
try {
|
try {
|
||||||
final File outFile = File(_managerAPI.storedPatchesFile);
|
final File outFile = File(_managerAPI.storedPatchesFile);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class _ContributorsCardState extends State<ContributorsCard> {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => launchUrl(
|
onTap: () => launchUrl(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
widget.contributors[index]['html_url'],
|
widget.contributors[index]['url'],
|
||||||
),
|
),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,6 +14,30 @@ class SExportSection extends StatelessWidget {
|
|||||||
return SettingsSection(
|
return SettingsSection(
|
||||||
title: t.settingsView.exportSectionTitle,
|
title: t.settingsView.exportSectionTitle,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
title: Text(
|
||||||
|
t.settingsView.exportSettingsLabel,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(t.settingsView.exportSettingsHint),
|
||||||
|
onTap: () => _settingsViewModel.exportSettings(),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
title: Text(
|
||||||
|
t.settingsView.importSettingsLabel,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(t.settingsView.importSettingsHint),
|
||||||
|
onTap: () => _settingsViewModel.importSettings(),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
title: Text(
|
title: Text(
|
||||||
@@ -114,7 +138,6 @@ class SExportSection extends StatelessWidget {
|
|||||||
subtitle: Text(t.settingsView.regenerateKeystoreHint),
|
subtitle: Text(t.settingsView.regenerateKeystoreHint),
|
||||||
onTap: () => _showDeleteKeystoreDialog(context),
|
onTap: () => _showDeleteKeystoreDialog(context),
|
||||||
),
|
),
|
||||||
// SManageKeystorePasswordUI(),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ homepage: https://revanced.app
|
|||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.23.0-dev.4+101800044
|
version: 1.23.0-dev.7+101800047
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.3
|
sdk: ^3.5.3
|
||||||
|
|||||||
Reference in New Issue
Block a user