Compare commits

...

35 Commits

Author SHA1 Message Date
Ushie
bbe5142ca9 build: bump version to v1.14.2 2023-10-26 17:21:25 +03:00
aAbed
e74ffac5b0 fix: unable to use custom API (#1435) 2023-10-26 17:20:34 +03:00
Ushie
cfb8980e3a chore: merge dev to main (#1425) 2023-10-25 11:42:54 +03:00
Ushie
e06e1bdcbe build: bump version to v1.14.1 2023-10-25 11:42:19 +03:00
Ushie
d60ced2f61 fix: remove codeblock from log export
Fixes: #1416
2023-10-25 11:40:30 +03:00
aAbed
e68689828e fix: Material You toggle not updating sometimes (#1421) 2023-10-25 11:37:03 +03:00
validcube
ba932758c8 build: bump version to v1.14.0 2023-10-21 23:00:51 +07:00
Pun Butrach
ee43fa6311 chore: merge dev to main (#1399) 2023-10-21 22:58:58 +07:00
Pun Butrach
ad6b164d51 feat: root status in export patch log (#1407)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-10-21 17:46:51 +07:00
validcube
4a5510acb2 refactor: remove the remaining of semantic release
Removal reason: not maintained
2023-10-21 15:37:23 +07:00
validcube
970dbc4428 refactor(social): use correct github organisation name
the correct name is "ReVanced" not "revanced", we changed for a while now.
2023-10-21 15:33:36 +07:00
validcube
f8f37325eb refactor(social): change Twitter to X 2023-10-21 15:32:13 +07:00
Benjamin
bb999019ef feat: show patch options in error log (#1394) 2023-10-17 13:21:59 +07:00
KobeW50
533b6a155a feat: clarify "Version compatibility check" description (#1397) 2023-10-17 13:20:46 +07:00
KobeW50
4cdc92388c refactor: fix patch log order to be consistent with settings order (#1398) 2023-10-17 13:19:18 +07:00
Pun Butrach
ccc6be1e71 refactor(accessibility): improve patch options (#1369)
Co-authored-by: Palm <palmpasuthorn@gmail.com>
2023-10-16 16:47:59 +07:00
Ushie
b355778a92 chore: merge dev to main (#1388) 2023-10-15 20:19:54 +03:00
Ushie
6a12e8f37a build: bump version to v1.13.1 2023-10-15 20:19:23 +03:00
Ushie
59adb91f5f fix(settings): inverted version compatibility switch 2023-10-15 20:16:58 +03:00
KobeW50
53677e2f39 fix: typo in reset patch selection dialog (#1387) 2023-10-15 20:14:19 +03:00
validcube
1c74f43b22 build: bump version to 1.13.0 2023-10-15 18:22:42 +07:00
Pun Butrach
b4801970e8 chore: merge dev to main (#1384)
ReVanced Manager 1.13.0!!
2023-10-15 18:18:09 +07:00
Pun Butrach
7a3a6b512f docs(building): use new highlight format 2023-10-15 18:04:19 +07:00
Pun Butrach
72ea33b6de fix(patch_selector): correct popup menu style
fix: #1372
2023-10-15 17:46:44 +07:00
Pun Butrach
d97192e0ee refactor(patch_options): disable card tap (#1368) 2023-10-15 16:56:43 +07:00
KobeW50
196d9fe4d2 refactor: reorganize and rename settings (#1307)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-10-15 16:56:02 +07:00
Benjamin
e960fcc303 feat: add user agent (#1380)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
2023-10-15 16:51:31 +07:00
Benjamin
f334da95ff feat: Remove full external storage access (#1381) 2023-10-15 16:44:11 +07:00
aAbed
5d5f311e36 fix(settings-view): list items jumping on scroll up (#1375) 2023-10-15 16:17:28 +07:00
aAbed
d577e97758 fix(app-bar): title not hiding completely (#1376) 2023-10-15 16:16:55 +07:00
aAbed
2dc92e26d3 fix(patches-selector): ignore punctuation marks when searching for patch (#1377) 2023-10-14 19:43:18 +07:00
oSumAtrIX
f4994a36a3 chore: Merge branch dev to main (#1366) 2023-10-13 00:22:02 +02:00
oSumAtrIX
7a785a8163 build: Bump version to v1.12.1 2023-10-13 00:14:14 +02:00
oSumAtrIX
6ad0d860c7 fix: Load patches from older versions of ReVanced Manager correctly 2023-10-13 00:12:27 +02:00
oSumAtrIX
38a2fa55df chore: Merge branch dev to main (#1350) 2023-10-12 21:58:29 +02:00
40 changed files with 595 additions and 7810 deletions

View File

@@ -1,75 +0,0 @@
{
"branches": [
"main",
{
"name": "dev",
"prerelease": true
}
],
"plugins": [
"semantic-release-export-data",
"@semantic-release/commit-analyzer",
[
"@semantic-release/release-notes-generator",
{
"presetConfig": {
"types": [
{
"type": "build",
"section": "Dependency Updates"
},
{
"type": "chore",
"section": "Other Changes",
"hidden": false
},
{
"type": "perf",
"section": "Performance Improvements",
"hidden": false
},
{
"type": "refactor",
"section": "Code Improvements",
"hidden": false
}
]
}
}
],
"@semantic-release/changelog",
"semantic-release-flutter-plugin",
[
"@semantic-release/git",
{
"assets": [
"CHANGELOG.md",
"pubspec.yaml"
]
}
],
[
"@semantic-release/github",
{
"assets": [
{
"path": "build/app/outputs/apk/release/revanced-manager-*.apk"
}
],
"successComment": false
}
],
[
"@saithodev/semantic-release-backmerge",
{
"backmergeBranches": [
{
"from": "main",
"to": "dev"
}
],
"clearWorkspace": true
}
]
]
}

View File

@@ -1 +0,0 @@

View File

@@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Cancel", "cancelButton": "Cancel",
"dismissButton": "Dismiss",
"quitButton": "Quit", "quitButton": "Quit",
"updateButton": "Update", "updateButton": "Update",
"enabledLabel": "Enabled", "enabledLabel": "Enabled",
@@ -128,14 +129,13 @@
"none": "None", "none": "None",
"noneTooltip": "Deselect all patches", "noneTooltip": "Deselect all patches",
"loadPatchesSelection": "Load patches selection", "loadPatchesSelection": "Load patch selection",
"noSavedPatches": "No saved patches for the selected app.\nPress Done to save current selection.", "noSavedPatches": "No saved patch selection for the selected app.\nPress Done to save the current selection.",
"noPatchesFound": "No patches found for the selected app", "noPatchesFound": "No patches found for the selected app",
"setRequiredOption": "Some patches require options to be set:\n\n{patches}\n\nPlease set them before continuing.", "setRequiredOption": "Some patches require options to be set:\n\n{patches}\n\nPlease set them before continuing."
"selectAllPatchesWarningContent": "You are about to select all patches, that includes non-suggested patches and can cause unwanted behavior."
}, },
"patchOptionsView": { "patchOptionsView": {
"resetOptionsTooltip": "Reset patch options",
"viewTitle": "Patch options", "viewTitle": "Patch options",
"saveOptions": "Save", "saveOptions": "Save",
@@ -152,10 +152,10 @@
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nSupported versions:\n{supportedVersions}", "unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nSupported versions:\n{supportedVersions}",
"unsupportedPatchVersion": "Patch is not supported for this app version. Enable the experimental toggle in settings to proceed.", "unsupportedPatchVersion": "Patch is not supported for this app version.",
"unsupportedRequiredOption": "This patch contains a required option that is not supported by this app", "unsupportedRequiredOption": "This patch contains a required option that is not supported by this app",
"patchesChangeWarningDialogText": "It is recommended to use the default selection of patches because changing it may cause unexpected issues.\n\nIf you know what you are doing, you can enable \"Enable changing selection\" in the settings.", "patchesChangeWarningDialogText": "It is recommended to use the default patch selection and options. Changing them may result in unexpected issues.\n\nYou'll need to turn on \"Allow changing patch selection\" in settings before changing any patch selection.",
"patchesChangeWarningDialogButton": "Use default selection" "patchesChangeWarningDialogButton": "Use default selection"
}, },
"installerView": { "installerView": {
@@ -187,10 +187,9 @@
"appearanceSectionTitle": "Appearance", "appearanceSectionTitle": "Appearance",
"teamSectionTitle": "Team", "teamSectionTitle": "Team",
"infoSectionTitle": "Info", "debugSectionTitle": "Debugging",
"advancedSectionTitle": "Advanced", "advancedSectionTitle": "Advanced",
"exportSectionTitle": "Import & export", "exportSectionTitle": "Import & export",
"logsSectionTitle": "Logs",
"themeModeLabel": "App theme", "themeModeLabel": "App theme",
"systemThemeLabel": "System", "systemThemeLabel": "System",
@@ -204,15 +203,15 @@
"englishOption": "English", "englishOption": "English",
"sourcesLabel": "Sources", "sourcesLabel": "Sources",
"sourcesLabelHint": "Configure your custom sources", "sourcesLabelHint": "Configure your sources",
"sourcesIntegrationsLabel": "Integrations source", "sourcesIntegrationsLabel": "Integrations source",
"sourcesResetDialogTitle": "Reset", "sourcesResetDialogTitle": "Reset",
"sourcesResetDialogText": "Are you sure you want to reset custom 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 API URL to its default value?", "apiURLResetDialogText": "Are you sure you want to reset your API URL to its default value?",
"sourcesUpdateNote": "Note: ReVanced Patches will be updated to the latest version automatically.\n\nThis will reveal your IP address to the server.", "sourcesUpdateNote": "Note: Patches will be updated to the latest version automatically.\n\nThis will reveal your IP address to the server.",
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Configure your custom API URL", "apiURLHint": "Configure your API URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"hostRepositoryLabel": "Repository API", "hostRepositoryLabel": "Repository API",
"orgPatchesLabel": "Patches organization", "orgPatchesLabel": "Patches organization",
@@ -222,21 +221,20 @@
"contributorsLabel": "Contributors", "contributorsLabel": "Contributors",
"contributorsHint": "A list of contributors of ReVanced", "contributorsHint": "A list of contributors of ReVanced",
"logsLabel": "Logs", "logsLabel": "Share logs",
"logsHint": "Share Manager's logs", "logsHint": "Share ReVanced Manager logs",
"enablePatchesSelectionLabel": "Enable changing selection", "enablePatchesSelectionLabel": "Allow changing patch selection",
"enablePatchesSelectionHint": "Enable changing the selection of patches.", "enablePatchesSelectionHint": "Allow changing the selection of patches",
"enablePatchesSelectionWarningText": "Changing the default selection of patches may cause unexpected issues.\n\nEnable anyways?", "enablePatchesSelectionWarningText": "Changing the selection of patches may cause unexpected issues.\n\nEnable anyways?",
"disablePatchesSelectionWarningText": "You are about to disable changing the selection of patches.\nThe default selection of patches will be restored.\n\nDisable anyways?", "disablePatchesSelectionWarningText": "You are about to disable changing the selection of patches.\nThe default selection of patches will be restored.\n\nDisable anyways?",
"autoUpdatePatchesLabel": "Auto update patches", "autoUpdatePatchesLabel": "Auto update patches",
"autoUpdatePatchesHint": "Automatically update ReVanced Patches to the latest version", "autoUpdatePatchesHint": "Automatically update patches to the latest version",
"experimentalUniversalPatchesLabel": "Experimental universal patches support", "universalPatchesLabel": "Show universal patches",
"experimentalUniversalPatchesHint": "Display all applications to use with universal patches, loading list of apps may be slower", "universalPatchesHint": "Display all apps and universal patches (may slow down the app list)",
"experimentalPatchesLabel": "Experimental patches support", "versionCompatibilityCheckLabel": "Version compatibility check",
"experimentalPatchesHint": "Enable usage of unsupported patches in any app version", "versionCompatibilityCheckHint": "Restricts patches to supported app versions",
"enabledExperimentalPatches": "Experimental patches support enabled",
"aboutLabel": "About", "aboutLabel": "About",
"snackbarMessage": "Copied to clipboard", "snackbarMessage": "Copied to clipboard",
@@ -246,54 +244,52 @@
"deleteTempDirHint": "Delete unused temporary files", "deleteTempDirHint": "Delete unused temporary files",
"deletedTempDir": "Temporary files deleted", "deletedTempDir": "Temporary files deleted",
"exportPatchesLabel": "Export patches selection", "exportPatchesLabel": "Export patch selection",
"exportPatchesHint": "Export patches selection to a JSON file", "exportPatchesHint": "Export patch selection to a JSON file",
"exportedPatches": "Patches selection exported", "exportedPatches": "Patch selection exported",
"noExportFileFound": "No patches selection to export", "noExportFileFound": "No patch selection to export",
"importPatchesLabel": "Import patches selection", "importPatchesLabel": "Import patch selection",
"importPatchesHint": "Import patches selection from a JSON file", "importPatchesHint": "Import patch selection from a JSON file",
"importedPatches": "Patches selection imported", "importedPatches": "Patch selection imported",
"resetStoredPatchesLabel": "Reset patches", "resetStoredPatchesLabel": "Reset patch selection",
"resetStoredPatchesHint": "Reset the stored patches selection", "resetStoredPatchesHint": "Reset the stored patch selection",
"resetStoredPatchesDialogTitle": "Reset patch selection?",
"resetStoredPatchesDialogText": "The default selection of patches will be restored.",
"resetStoredPatches": "Patch selection has been reset",
"resetStoredOptionsLabel": "Reset options", "resetStoredOptionsLabel": "Reset patch options",
"resetStoredOptionsHint": "Reset all patch options", "resetStoredOptionsHint": "Reset all patch options",
"resetStoredOptionsDialogTitle": "Reset patch options?",
"resetStoredPatchesDialogTitle": "Reset patches selection?", "resetStoredOptionsDialogText": "Resetting patch options will remove all saved options.",
"resetStoredPatchesDialogText": "Resetting patches selection will remove all selected patches.",
"resetStoredPatches": "Patches selection has been reset",
"resetStoredOptionsDialogTitle": "Reset options?",
"resetStoredOptionsDialogText": "Resetting options will remove all saved options.",
"resetStoredOptions": "Options have been reset", "resetStoredOptions": "Options have been reset",
"deleteLogsLabel": "Delete logs", "deleteLogsLabel": "Clear logs",
"deleteLogsHint": "Delete collected manager logs", "deleteLogsHint": "Delete collected ReVanced Manager logs",
"deletedLogs": "Logs deleted", "deletedLogs": "Logs deleted",
"regenerateKeystoreLabel": "Regenerate keystore", "regenerateKeystoreLabel": "Regenerate keystore",
"regenerateKeystoreHint": "Regenerate the keystore used to sign the app", "regenerateKeystoreHint": "Regenerate the keystore used to sign apps",
"regenerateKeystoreDialogTitle": "Regenerate keystore?", "regenerateKeystoreDialogTitle": "Regenerate keystore?",
"regenerateKeystoreDialogText": "Patched apps signed with the old keystore will no longer be able to update.", "regenerateKeystoreDialogText": "Patched apps signed with the old keystore will no longer be able to be updated.",
"regeneratedKeystore": "Keystore regenerated", "regeneratedKeystore": "Keystore regenerated",
"exportKeystoreLabel": "Export keystore", "exportKeystoreLabel": "Export keystore",
"exportKeystoreHint": "Export keystore used to sign apps", "exportKeystoreHint": "Export the keystore used to sign apps",
"exportedKeystore": "Keystore exported", "exportedKeystore": "Keystore exported",
"noKeystoreExportFileFound": "No keystore to export", "noKeystoreExportFileFound": "No keystore to export",
"importKeystoreLabel": "Import keystore", "importKeystoreLabel": "Import keystore",
"importKeystoreHint": "Import keystore used to sign apps", "importKeystoreHint": "Import a keystore used to sign apps",
"importedKeystore": "Keystore imported", "importedKeystore": "Keystore imported",
"selectKeystorePassword": "Keystore Password", "selectKeystorePassword": "Keystore password",
"selectKeystorePasswordHint": "Select keystore password used to sign the apk", "selectKeystorePasswordHint": "Select keystore password used to sign apps",
"jsonSelectorErrorMessage": "Unable to use selected JSON file", "jsonSelectorErrorMessage": "Unable to use selected JSON file",
"keystoreSelectorErrorMessage": "Unable to use selected KEYSTORE file" "keystoreSelectorErrorMessage": "Unable to use selected keystore file"
}, },
"appInfoView": { "appInfoView": {
"widgetTitle": "App info", "widgetTitle": "App info",

View File

@@ -2,37 +2,41 @@
ReVanced Manager has settings that can be configured to your liking. ReVanced Manager has settings that can be configured to your liking.
## Essential settings ## 🎛️ Essential settings
- ### 🔗 API URL - ### 🪛 Allow changing patch selection
API to use to fetch updates and ReVanced Patches from. Allows the user to change the patch selection from the default selection.
- ### 🔍 Version compatibility check
Constrains patches to supported app versions. Disable this to patch any version of an app.
> [!WARNING]
> Disabling this may cause issues if the patches are not compatible with the app version.
- ### 🧑‍🔬 Show universal patches
Reveals patches which can be applied to any app.
> [!WARNING]
> These patches may not work on all apps.
- ### 🧬 Sources - ### 🧬 Sources
Override the API and download ReVanced Patches from a different source. Override the API and download patches from a different source.
- ### 🧪 Experimental ReVanced Patches support - ### 🔗 API URL
Disable checking for the version of the app when applying ReVanced Patches. API to use to fetch updates and patches from.
> [!WARNING]
> This may cause issues if the ReVanced Patches are not compatible with the app version.
- ### 🧑‍🔬 Experimental universal support
This will show or hide ReVanced Patches, which are not meant for any app in particular but apply to all apps
> [!WARNING]
> Because the patches generalize the app, they may not work on all apps.
- ### 💾 Imports & Exports - ### 💾 Imports & Exports
You can import, export or reset the following settings: You can import, export or reset the following settings:
- 🔑 Keystore - 🔑 Keystore
- 📄 ReVanced Patches selection - 📄 Patch selection
- ⚙️ Options - ⚙️ Patch options
> [!NOTE] > [!NOTE]
> This is particularly useful if you want to backup or reset your settings. > This is particularly useful if you want to backup or reset your settings.
@@ -45,4 +49,4 @@ ReVanced Manager has settings that can be configured to your liking.
The next page will bring you back to the usage page. The next page will bring you back to the usage page.
Continue: [🛠️ Usage](2_usage.md) Continue: [🛠️ Usage](2_usage.md)

View File

@@ -31,7 +31,8 @@ This page will guide you through building ReVanced Manager from source.
flutter packages pub run build_runner build --delete-conflicting-outputs flutter packages pub run build_runner build --delete-conflicting-outputs
``` ```
> **Note**: Must be run every time you sync your local repository with the remote repository. > [!Note]
> Must be run every time you sync your local repository with the remote repository.
7. Build the APK 7. Build the APK

BIN
fonts/custom-icons.ttf Normal file

Binary file not shown.

View File

@@ -1,3 +1,4 @@
import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/patcher_api.dart';
@@ -41,6 +42,7 @@ import 'package:stacked_services/stacked_services.dart';
LazySingleton(classType: PatcherAPI), LazySingleton(classType: PatcherAPI),
LazySingleton(classType: RevancedAPI), LazySingleton(classType: RevancedAPI),
LazySingleton(classType: GithubAPI), LazySingleton(classType: GithubAPI),
LazySingleton(classType: DownloadManager),
LazySingleton(classType: Toast), LazySingleton(classType: Toast),
], ],
) )

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/revanced_api.dart';
@@ -19,6 +20,7 @@ Future main() async {
await setupLocator(); await setupLocator();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await locator<ManagerAPI>().initialize(); await locator<ManagerAPI>().initialize();
await locator<DownloadManager>().initialize();
final String apiUrl = locator<ManagerAPI>().getApiUrl(); final String apiUrl = locator<ManagerAPI>().getApiUrl();
await locator<RevancedAPI>().initialize(apiUrl); await locator<RevancedAPI>().initialize(apiUrl);
final String repoUrl = locator<ManagerAPI>().getRepoUrl(); final String repoUrl = locator<ManagerAPI>().getRepoUrl();

View File

@@ -12,7 +12,15 @@ class Patch {
required this.options, required this.options,
}); });
factory Patch.fromJson(Map<String, dynamic> json) => _$PatchFromJson(json); factory Patch.fromJson(Map<String, dynamic> json) {
// See: https://github.com/ReVanced/revanced-manager/issues/1364#issuecomment-1760414618
if (json['options'] == null) {
json['options'] = [];
}
return _$PatchFromJson(json);
}
final String name; final String name;
final String? description; final String? description;
final bool excluded; final bool excluded;

View File

@@ -0,0 +1,75 @@
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/file.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart';
@lazySingleton
class DownloadManager {
final ManagerAPI _managerAPI = locator<ManagerAPI>();
late final String _userAgent;
final _cacheOptions = CacheOptions(
store: MemCacheStore(),
maxStale: const Duration(days: 1),
priority: CachePriority.high,
);
Future<void> initialize() async {
_userAgent = 'ReVanced-Manager/${await _managerAPI.getCurrentManagerVersion()}';
}
Dio initDio(String url) {
var dio = Dio();
try {
dio = Dio(
BaseOptions(
baseUrl: url,
headers: {
'User-Agent': _userAgent,
},
),
);
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
return dio;
}
Future<void> clearAllCache() async {
try {
await _cacheOptions.store!.clean();
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
}
Future<File> getSingleFile(String url) async {
return DefaultCacheManager().getSingleFile(
url,
headers: {
'User-Agent': _userAgent,
},
);
}
Stream<FileResponse> getFileStream(String url) {
return DefaultCacheManager().getFileStream(
url,
withProgress: true,
headers: {
'User-Agent': _userAgent,
},
);
}
}

View File

@@ -1,48 +1,24 @@
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
@lazySingleton @lazySingleton
class GithubAPI { class GithubAPI {
late Dio _dio = Dio(); late final Dio _dio;
late final ManagerAPI _managerAPI = locator<ManagerAPI>(); late final ManagerAPI _managerAPI = locator<ManagerAPI>();
late final DownloadManager _downloadManager = locator<DownloadManager>();
final _cacheOptions = CacheOptions(
store: MemCacheStore(),
maxStale: const Duration(days: 1),
priority: CachePriority.high,
);
Future<void> initialize(String repoUrl) async { Future<void> initialize(String repoUrl) async {
try { _dio = _downloadManager.initDio(repoUrl);
_dio = Dio(
BaseOptions(
baseUrl: repoUrl,
),
);
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
} }
Future<void> clearAllCache() async { Future<void> clearAllCache() async {
try { await _downloadManager.clearAllCache();
await _cacheOptions.store!.clean();
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
} }
Future<Map<String, dynamic>?> getLatestRelease( Future<Map<String, dynamic>?> getLatestRelease(
@@ -104,7 +80,7 @@ class GithubAPI {
final Map<String, dynamic> releases = response.data[0]; final Map<String, dynamic> releases = response.data[0];
int updates = 0; int updates = 0;
final String currentVersion = final String currentVersion =
await ManagerAPI().getCurrentManagerVersion(); await _managerAPI.getCurrentManagerVersion();
while (response.data[updates]['tag_name'] != 'v$currentVersion') { while (response.data[updates]['tag_name'] != 'v$currentVersion') {
updates++; updates++;
} }
@@ -141,7 +117,7 @@ class GithubAPI {
(asset) => (asset['name'] as String).endsWith(extension), (asset) => (asset['name'] as String).endsWith(extension),
); );
if (asset != null) { if (asset != null) {
return await DefaultCacheManager().getSingleFile( return await _downloadManager.getSingleFile(
asset['browser_download_url'], asset['browser_download_url'],
); );
} }
@@ -162,7 +138,7 @@ class GithubAPI {
) async { ) async {
try { try {
if (url.isNotEmpty) { if (url.isNotEmpty) {
return await DefaultCacheManager().getSingleFile( return await _downloadManager.getSingleFile(
url, url,
); );
} }
@@ -180,7 +156,7 @@ class GithubAPI {
} else { } else {
_managerAPI.setPatchesDownloadURL(downloadUrl); _managerAPI.setPatchesDownloadURL(downloadUrl);
} }
return await DefaultCacheManager().getSingleFile( return await _downloadManager.getSingleFile(
downloadUrl, downloadUrl,
); );
} }

View File

@@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.dart';
@@ -33,6 +34,7 @@ class ManagerAPI {
Patch? selectedPatch; Patch? selectedPatch;
BuildContext? ctx; BuildContext? ctx;
bool isRooted = false; bool isRooted = false;
bool isDynamicThemeAvailable = false;
String storedPatchesFile = '/selected-patches.json'; String storedPatchesFile = '/selected-patches.json';
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';
@@ -59,10 +61,16 @@ class ManagerAPI {
Future<void> initialize() async { Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
isRooted = await _rootAPI.isRooted(); isRooted = await _rootAPI.isRooted();
isDynamicThemeAvailable = (await getSdkVersion()) >= 31; // ANDROID_12_SDK_VERSION = 31
storedPatchesFile = storedPatchesFile =
(await getApplicationDocumentsDirectory()).path + storedPatchesFile; (await getApplicationDocumentsDirectory()).path + storedPatchesFile;
} }
Future<int> getSdkVersion() async {
final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo;
return info.version.sdkInt;
}
String getApiUrl() { String getApiUrl() {
return _prefs.getString('apiUrl') ?? defaultApiUrl; return _prefs.getString('apiUrl') ?? defaultApiUrl;
} }
@@ -71,7 +79,6 @@ class ManagerAPI {
if (url.isEmpty || url == ' ') { if (url.isEmpty || url == ' ') {
url = defaultApiUrl; url = defaultApiUrl;
} }
await _revancedAPI.initialize(url);
await _revancedAPI.clearAllCache(); await _revancedAPI.clearAllCache();
await _prefs.setString('apiUrl', url); await _prefs.setString('apiUrl', url);
} }
@@ -244,12 +251,12 @@ class ManagerAPI {
await _prefs.setBool('universalPatchesEnabled', value); await _prefs.setBool('universalPatchesEnabled', value);
} }
bool areExperimentalPatchesEnabled() { bool isVersionCompatibilityCheckEnabled() {
return _prefs.getBool('experimentalPatchesEnabled') ?? false; return _prefs.getBool('versionCompatibilityCheckEnabled') ?? true;
} }
Future<void> enableExperimentalPatchesStatus(bool value) async { Future<void> enableVersionCompatibilityCheckStatus(bool value) async {
await _prefs.setBool('experimentalPatchesEnabled', value); await _prefs.setBool('versionCompatibilityCheckEnabled', value);
} }
Future<void> setKeystorePassword(String password) async { Future<void> setKeystorePassword(String password) async {
@@ -677,7 +684,7 @@ class ManagerAPI {
Future<List<String>> getDefaultPatches() async { Future<List<String>> getDefaultPatches() async {
final List<Patch> patches = await getPatches(); final List<Patch> patches = await getPatches();
final List<String> defaultPatches = []; final List<String> defaultPatches = [];
if (areExperimentalPatchesEnabled() == false) { if (isVersionCompatibilityCheckEnabled() == true) {
defaultPatches.addAll( defaultPatches.addAll(
patches patches
.where( .where(

View File

@@ -3,49 +3,27 @@ import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.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';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/download_manager.dart';
import 'package:synchronized/synchronized.dart'; import 'package:synchronized/synchronized.dart';
import 'package:timeago/timeago.dart'; import 'package:timeago/timeago.dart';
@lazySingleton @lazySingleton
class RevancedAPI { class RevancedAPI {
late Dio _dio = Dio(); late final Dio _dio;
late final DownloadManager _downloadManager = locator<DownloadManager>();
final Lock getToolsLock = Lock(); final Lock getToolsLock = Lock();
final _cacheOptions = CacheOptions( Future<void> initialize(String repoUrl) async {
store: MemCacheStore(), _dio = _downloadManager.initDio(repoUrl);
maxStale: const Duration(days: 1),
priority: CachePriority.high,
);
Future<void> initialize(String apiUrl) async {
try {
_dio = Dio(
BaseOptions(
baseUrl: apiUrl,
),
);
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
} }
Future<void> clearAllCache() async { Future<void> clearAllCache() async {
try { await _downloadManager.clearAllCache();
await _cacheOptions.store!.clean();
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
} }
Future<Map<String, List<dynamic>>> getContributors() async { Future<Map<String, List<dynamic>>> getContributors() async {
@@ -120,7 +98,7 @@ class RevancedAPI {
); );
if (release != null) { if (release != null) {
final String url = release['browser_download_url']; final String url = release['browser_download_url'];
return await DefaultCacheManager().getSingleFile(url); return await _downloadManager.getSingleFile(url);
} }
} on Exception catch (e) { } on Exception catch (e) {
if (kDebugMode) { if (kDebugMode) {
@@ -152,9 +130,8 @@ class RevancedAPI {
'revanced/revanced-manager', 'revanced/revanced-manager',
); );
File? outputFile; File? outputFile;
await for (final result in DefaultCacheManager().getFileStream( await for (final result in _downloadManager.getFileStream(
release!['browser_download_url'] as String, release!['browser_download_url'] as String,
withProgress: true,
)) { )) {
if (result is DownloadProgress) { if (result is DownloadProgress) {
final totalSize = result.totalSize ?? 10000000; final totalSize = result.totalSize ?? 10000000;

View File

@@ -38,13 +38,11 @@ class _AppSelectorViewState extends State<AppSelectorView> {
floating: true, floating: true,
title: I18nText( title: I18nText(
'appSelectorView.viewTitle', 'appSelectorView.viewTitle',
child: Text( ),
'', titleTextStyle: TextStyle(
style: TextStyle( fontSize: 22.0,
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.titleLarge!.color,
), ),
),
),
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back, Icons.arrow_back,

View File

@@ -139,7 +139,6 @@ class InstallerViewModel extends BaseViewModel {
} }
Future<void> runPatcher() async { Future<void> runPatcher() async {
try { try {
await _patcherAPI.runPatcher( await _patcherAPI.runPatcher(
_app.packageName, _app.packageName,
@@ -159,7 +158,7 @@ class InstallerViewModel extends BaseViewModel {
// Necessary to reset the state of patches so that they // Necessary to reset the state of patches so that they
// can be reloaded again. // can be reloaded again.
_managerAPI.patches.clear(); _managerAPI.patches.clear();
await _patcherAPI.loadPatches(); await _patcherAPI.loadPatches();
try { try {
@@ -184,29 +183,28 @@ class InstallerViewModel extends BaseViewModel {
final info = await AboutInfo.getInfo(); final info = await AboutInfo.getInfo();
final formattedLogs = [ final formattedLogs = [
'```', '- Device Info',
'~ Device Info',
'ReVanced Manager: ${info['version']}', 'ReVanced Manager: ${info['version']}',
'Build: ${info['flavor']}', 'Build: ${info['flavor']}',
'Model: ${info['model']}', 'Model: ${info['model']}',
'Android version: ${info['androidVersion']}', 'Android version: ${info['androidVersion']}',
'Supported architectures: ${info['supportedArch'].join(", ")}', 'Supported architectures: ${info['supportedArch'].join(", ")}',
'Root permissions: ${isRooted ? 'Yes' : 'No'}',
'\n~ Patch Info',
'\n- Patch Info',
'App: ${_app.packageName} v${_app.version}', 'App: ${_app.packageName} v${_app.version}',
'Patches version: ${_managerAPI.patchesVersion}', 'Patches version: ${_managerAPI.patchesVersion}',
'Patches: ${_patches.map((p) => p.name).toList().join(", ")}', 'Patches: ${_patches.map((p) => p.name + (p.options.isEmpty ? '' : ' [${p.options.map((o) => '${o.title}: ${o.value}').join(", ")}]')).toList().join(", ")}',
'\n~ Settings', '\n- Settings',
'Enabled changing patches: ${_managerAPI.isPatchesChangeEnabled()}', 'Allow changing patch selection: ${_managerAPI.isPatchesChangeEnabled()}',
'Enabled universal patches: ${_managerAPI.areUniversalPatchesEnabled()}', 'Version compatibility check: ${_managerAPI.isVersionCompatibilityCheckEnabled()}',
'Enabled experimental patches: ${_managerAPI.areExperimentalPatchesEnabled()}', 'Show universal patches: ${_managerAPI.areUniversalPatchesEnabled()}',
'Patches source: ${_managerAPI.getPatchesRepo()}', 'Patches source: ${_managerAPI.getPatchesRepo()}',
'Integration source: ${_managerAPI.getIntegrationsRepo()}', 'Integration source: ${_managerAPI.getIntegrationsRepo()}',
'\n~ Logs', '\n- Logs',
logs, logs,
'```',
]; ];
Clipboard.setData(ClipboardData(text: formattedLogs.join('\n'))); Clipboard.setData(ClipboardData(text: formattedLogs.join('\n')));

View File

@@ -19,7 +19,6 @@ class NavigationViewModel extends IndexTrackingViewModel {
Future<void> initialize(BuildContext context) async { Future<void> initialize(BuildContext context) async {
locator<Toast>().initialize(context); locator<Toast>().initialize(context);
final SharedPreferences prefs = await SharedPreferences.getInstance(); final SharedPreferences prefs = await SharedPreferences.getInstance();
await requestManageExternalStorage();
if (prefs.getBool('permissionsRequested') == null) { if (prefs.getBool('permissionsRequested') == null) {
await Permission.storage.request(); await Permission.storage.request();
@@ -60,17 +59,6 @@ class NavigationViewModel extends IndexTrackingViewModel {
); );
} }
Future<void> requestManageExternalStorage() async {
final manageExternalStorageStatus =
await Permission.manageExternalStorage.status;
if (manageExternalStorageStatus.isDenied) {
await Permission.manageExternalStorage.request();
}
if (manageExternalStorageStatus.isPermanentlyDenied) {
await openAppSettings();
}
}
Widget getViewForIndex(int index) { Widget getViewForIndex(int index) {
switch (index) { switch (index) {
case 0: case 0:

View File

@@ -48,6 +48,10 @@ class PatchOptionsView extends StatelessWidget {
icon: const Icon( icon: const Icon(
Icons.history, Icons.history,
), ),
tooltip: FlutterI18n.translate(
context,
'patchOptionsView.resetOptionsTooltip',
),
), ),
], ],
), ),

View File

@@ -147,7 +147,7 @@ class PatchOptionsViewModel extends BaseViewModel {
), ),
actions: [ actions: [
CustomMaterialButton( CustomMaterialButton(
label: I18nText('okButton'), label: I18nText('cancelButton'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
@@ -165,7 +165,7 @@ class PatchOptionsViewModel extends BaseViewModel {
.map((e) { .map((e) {
return CustomCard( return CustomCard(
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer, backgroundColor: Theme.of(context).colorScheme.surface,
onTap: () { onTap: () {
addOption(e); addOption(e);
Navigator.pop(context); Navigator.pop(context);
@@ -186,9 +186,9 @@ class PatchOptionsViewModel extends BaseViewModel {
e.description, e.description,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Theme.of(context).colorScheme.onSecondaryContainer, color: Theme.of(context).colorScheme.onSurface,
), ),
) ),
], ],
), ),
), ),

View File

@@ -80,7 +80,8 @@ class PatcherViewModel extends BaseViewModel {
} }
bool checkRequiredPatchOption(BuildContext context) { bool checkRequiredPatchOption(BuildContext context) {
if (getNullRequiredOptions(selectedPatches, selectedApp!.packageName).isNotEmpty) { if (getNullRequiredOptions(selectedPatches, selectedApp!.packageName)
.isNotEmpty) {
showRequiredOptionDialog(context); showRequiredOptionDialog(context);
return false; return false;
} }
@@ -190,7 +191,7 @@ class PatcherViewModel extends BaseViewModel {
this.selectedPatches.clear(); this.selectedPatches.clear();
this.selectedPatches.addAll(patches.where((patch) => !patch.excluded)); this.selectedPatches.addAll(patches.where((patch) => !patch.excluded));
} }
if (!_managerAPI.areExperimentalPatchesEnabled()) { if (_managerAPI.isVersionCompatibilityCheckEnabled()) {
this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch)); this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch));
} }
if (!_managerAPI.areUniversalPatchesEnabled()) { if (!_managerAPI.areUniversalPatchesEnabled()) {
@@ -199,11 +200,12 @@ class PatcherViewModel extends BaseViewModel {
.removeWhere((patch) => patch.compatiblePackages.isEmpty); .removeWhere((patch) => patch.compatiblePackages.isEmpty);
} }
final usedPatches = _managerAPI.getUsedPatches(selectedApp!.packageName); final usedPatches = _managerAPI.getUsedPatches(selectedApp!.packageName);
for (final patch in usedPatches){ for (final patch in usedPatches) {
if (!patches.any((p) => p.name == patch.name)){ if (!patches.any((p) => p.name == patch.name)) {
removedPatches.add('${patch.name}'); removedPatches.add('${patch.name}');
for (final option in patch.options) { for (final option in patch.options) {
_managerAPI.clearPatchOption(selectedApp!.packageName, patch.name, option.key); _managerAPI.clearPatchOption(
selectedApp!.packageName, patch.name, option.key);
} }
} }
} }

View File

@@ -4,7 +4,6 @@ import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/patchesSelectorView/patch_item.dart'; import 'package:revanced_manager/ui/widgets/patchesSelectorView/patch_item.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart';
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart'; import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart'; import 'package:revanced_manager/utils/check_for_supported_patch.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
@@ -62,12 +61,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
floating: true, floating: true,
title: I18nText( title: I18nText(
'patchesSelectorView.viewTitle', 'patchesSelectorView.viewTitle',
child: Text( ),
'', titleTextStyle: TextStyle(
style: TextStyle( fontSize: 22.0,
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.titleLarge!.color,
),
),
), ),
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
@@ -80,41 +77,34 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
}, },
), ),
actions: [ actions: [
FittedBox( Container(
fit: BoxFit.scaleDown, margin: const EdgeInsets.symmetric(vertical: 12),
child: Container( padding: const EdgeInsets.symmetric(horizontal: 6),
margin: const EdgeInsets.only(top: 12, bottom: 12), decoration: BoxDecoration(
padding: color:
const EdgeInsets.symmetric(horizontal: 6, vertical: 6), Theme.of(context).colorScheme.tertiary.withOpacity(0.5),
decoration: BoxDecoration( borderRadius: BorderRadius.circular(6),
color: Theme.of(context) ),
.colorScheme alignment: Alignment.center,
.tertiary child: Text(
.withOpacity(0.5), model.patchesVersion!,
borderRadius: BorderRadius.circular(6), style: TextStyle(
), color: Theme.of(context).textTheme.titleLarge!.color,
child: Text(
model.patchesVersion!,
style: TextStyle(
color: Theme.of(context).textTheme.titleLarge!.color,
),
), ),
), ),
), ),
CustomPopupMenu( PopupMenuButton(
onSelected: (value) => onSelected: (value) {
{model.onMenuSelection(value, context)}, model.onMenuSelection(value, context);
children: { },
0: I18nText( itemBuilder: (BuildContext context) => <PopupMenuEntry>[
'patchesSelectorView.loadPatchesSelection', PopupMenuItem(
child: const Text( value: 0,
'', child: I18nText(
style: TextStyle( 'patchesSelectorView.loadPatchesSelection',
fontWeight: FontWeight.bold,
),
), ),
), ),
}, ],
), ),
], ],
bottom: PreferredSize( bottom: PreferredSize(
@@ -294,7 +284,9 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
_managerAPI.isPatchesChangeEnabled(), _managerAPI.isPatchesChangeEnabled(),
hasUnsupportedPatchOption: hasUnsupportedPatchOption:
hasUnsupportedRequiredOption( hasUnsupportedRequiredOption(
patch.options, patch), patch.options,
patch,
),
options: patch.options, options: patch.options,
isSelected: model.isSelected(patch), isSelected: model.isSelected(patch),
navigateToOptions: (options) => navigateToOptions: (options) =>

View File

@@ -169,7 +169,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
.where( .where(
(element) => (element) =>
!element.excluded && !element.excluded &&
(_managerAPI.areExperimentalPatchesEnabled() || (!_managerAPI.isVersionCompatibilityCheckEnabled() ||
isPatchSupported(element)), isPatchSupported(element)),
), ),
); );
@@ -209,7 +209,10 @@ class PatchesSelectorViewModel extends BaseViewModel {
query.isEmpty || query.isEmpty ||
query.length < 2 || query.length < 2 ||
patch.name.toLowerCase().contains(query.toLowerCase()) || patch.name.toLowerCase().contains(query.toLowerCase()) ||
patch.getSimpleName().toLowerCase().contains(query.toLowerCase()), patch.name
.replaceAll(RegExp(r'[^\w\s]+'), '')
.toLowerCase()
.contains(query.toLowerCase()),
) )
.toList(); .toList();
if (_managerAPI.areUniversalPatchesEnabled()) { if (_managerAPI.areUniversalPatchesEnabled()) {
@@ -281,7 +284,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
this.selectedPatches.addAll( this.selectedPatches.addAll(
patches.where((patch) => selectedPatches.contains(patch.name)), patches.where((patch) => selectedPatches.contains(patch.name)),
); );
if (!_managerAPI.areExperimentalPatchesEnabled()) { if (_managerAPI.isVersionCompatibilityCheckEnabled()) {
this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch)); this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch));
} }
} else { } else {

View File

@@ -67,6 +67,7 @@ class SManageApiUrl extends BaseViewModel {
apiUrl = 'https://$apiUrl'; apiUrl = 'https://$apiUrl';
} }
_managerAPI.setApiUrl(apiUrl); _managerAPI.setApiUrl(apiUrl);
_toast.showBottom('settingsView.restartAppForChanges');
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),

View File

@@ -6,46 +6,98 @@ import 'package:flutter/services.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart';
final _settingViewModel = SettingsViewModel(); class SUpdateThemeUI extends StatefulWidget {
const SUpdateThemeUI({super.key});
// ignore: constant_identifier_names @override
const int ANDROID_12_SDK_VERSION = 31; State<SUpdateThemeUI> createState() => _SUpdateThemeUIState();
}
class SUpdateTheme extends BaseViewModel { class _SUpdateThemeUIState extends State<SUpdateThemeUI> {
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI managerAPI = locator<ManagerAPI>();
@override
Widget build(BuildContext context) {
return SettingsSection(
title: 'settingsView.appearanceSectionTitle',
children: <Widget>[
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.themeModeLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
trailing: CustomMaterialButton(
label: getThemeModeName(),
onPressed: () => {showThemeDialog(context)},
),
onTap: () => {showThemeDialog(context)},
),
if (managerAPI.isDynamicThemeAvailable)
SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.dynamicThemeLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.dynamicThemeHint'),
value: getDynamicThemeStatus(),
onChanged: (value) => {
setUseDynamicTheme(
context,
value,
),
},
),
],
);
}
bool getDynamicThemeStatus() { bool getDynamicThemeStatus() {
return _managerAPI.getUseDynamicTheme(); return managerAPI.getUseDynamicTheme();
} }
Future<void> setUseDynamicTheme(BuildContext context, bool value) async { Future<void> setUseDynamicTheme(BuildContext context, bool value) async {
await _managerAPI.setUseDynamicTheme(value); await managerAPI.setUseDynamicTheme(value);
final int currentTheme = (DynamicTheme.of(context)!.themeId ~/ 2) * 2; final int currentTheme = (DynamicTheme.of(context)!.themeId ~/ 2) * 2;
await DynamicTheme.of(context)!.setTheme(currentTheme + (value ? 1 : 0)); await DynamicTheme.of(context)!.setTheme(currentTheme + (value ? 1 : 0));
notifyListeners(); setState(() {});
} }
int getThemeMode() { int getThemeMode() {
return _managerAPI.getThemeMode(); return managerAPI.getThemeMode();
} }
Future<void> setThemeMode(BuildContext context, int value) async { Future<void> setThemeMode(BuildContext context, int value) async {
await _managerAPI.setThemeMode(value); await managerAPI.setThemeMode(value);
final bool isDynamicTheme = DynamicTheme.of(context)!.themeId.isEven; final bool isDynamicTheme = DynamicTheme.of(context)!.themeId.isEven;
await DynamicTheme.of(context)!.setTheme(value * 2 + (isDynamicTheme ? 0 : 1)); await DynamicTheme.of(context)!
final bool isLight = value != 2 && (value == 1 || DynamicTheme.of(context)!.theme.brightness == Brightness.light); .setTheme(value * 2 + (isDynamicTheme ? 0 : 1));
final bool isLight = value != 2 &&
(value == 1 ||
DynamicTheme.of(context)!.theme.brightness == Brightness.light);
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle( SystemUiOverlayStyle(
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness:
isLight ? Brightness.dark : Brightness.light, isLight ? Brightness.dark : Brightness.light,
), ),
); );
notifyListeners(); setState(() {});
} }
I18nText getThemeModeName() { I18nText getThemeModeName() {
@@ -131,63 +183,3 @@ class SUpdateTheme extends BaseViewModel {
); );
} }
} }
final sUpdateTheme = SUpdateTheme();
class SUpdateThemeUI extends StatelessWidget {
const SUpdateThemeUI({super.key});
@override
Widget build(BuildContext context) {
return SettingsSection(
title: 'settingsView.appearanceSectionTitle',
children: <Widget>[
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.themeModeLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
trailing: CustomMaterialButton(
label: sUpdateTheme.getThemeModeName(),
onPressed: () => { sUpdateTheme.showThemeDialog(context) },
),
onTap: () => { sUpdateTheme.showThemeDialog(context) },
),
FutureBuilder<int>(
future: _settingViewModel.getSdkVersion(),
builder: (context, snapshot) => Visibility(
visible:
snapshot.hasData && snapshot.data! >= ANDROID_12_SDK_VERSION,
child: SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.dynamicThemeLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.dynamicThemeHint'),
value: _settingViewModel.sUpdateTheme.getDynamicThemeStatus(),
onChanged: (value) => {
_settingViewModel.sUpdateTheme.setUseDynamicTheme(
context,
value,
),
},
),
),
),
],
);
}
}

View File

@@ -6,8 +6,8 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_theme.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_theme.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_advanced_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_advanced_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_debug_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_export_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_export_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_info_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_team_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_team_section.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
@@ -40,16 +40,24 @@ class SettingsView extends StatelessWidget {
SliverList( SliverList(
delegate: SliverChildListDelegate.fixed( delegate: SliverChildListDelegate.fixed(
<Widget>[ <Widget>[
SUpdateThemeUI(), ListView(
// SUpdateLanguageUI(), padding: EdgeInsets.zero,
// _settingsDivider, shrinkWrap: true,
STeamSection(), physics: NeverScrollableScrollPhysics(),
_settingsDivider, children: const [
SAdvancedSection(), SUpdateThemeUI(),
_settingsDivider, // _settingsDivider,
SExportSection(), // SUpdateLanguageUI(),
_settingsDivider, _settingsDivider,
SInfoSection(), SAdvancedSection(),
_settingsDivider,
SExportSection(),
_settingsDivider,
STeamSection(),
_settingsDivider,
SDebugSection(),
],
),
], ],
), ),
), ),

View File

@@ -1,6 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:cr_file_saver/file_saver.dart'; import 'package:cr_file_saver/file_saver.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -14,7 +13,6 @@ import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_language.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_language.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_theme.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:share_extend/share_extend.dart'; import 'package:share_extend/share_extend.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
@@ -29,7 +27,6 @@ class SettingsViewModel extends BaseViewModel {
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
final SUpdateLanguage sUpdateLanguage = SUpdateLanguage(); final SUpdateLanguage sUpdateLanguage = SUpdateLanguage();
final SUpdateTheme sUpdateTheme = SUpdateTheme();
void navigateToContributors() { void navigateToContributors() {
_navigationService.navigateTo(Routes.contributorsView); _navigationService.navigateTo(Routes.contributorsView);
@@ -135,12 +132,12 @@ class SettingsViewModel extends BaseViewModel {
notifyListeners(); notifyListeners();
} }
bool areExperimentalPatchesEnabled() { bool isVersionCompatibilityCheckEnabled() {
return _managerAPI.areExperimentalPatchesEnabled(); return _managerAPI.isVersionCompatibilityCheckEnabled();
} }
void useExperimentalPatches(bool value) { void useVersionCompatibilityCheck(bool value) {
_managerAPI.enableExperimentalPatchesStatus(value); _managerAPI.enableVersionCompatibilityCheckStatus(value);
notifyListeners(); notifyListeners();
} }
@@ -256,11 +253,6 @@ class SettingsViewModel extends BaseViewModel {
_toast.showBottom('settingsView.resetStoredPatches'); _toast.showBottom('settingsView.resetStoredPatches');
} }
Future<int> getSdkVersion() async {
final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo;
return info.version.sdkInt;
}
Future<void> deleteLogs() async { Future<void> deleteLogs() async {
final Directory appCacheDir = await getTemporaryDirectory(); final Directory appCacheDir = await getTemporaryDirectory();
final Directory logsDir = Directory('${appCacheDir.path}/logs'); final Directory logsDir = Directory('${appCacheDir.path}/logs');

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/file.dart'; import 'package:flutter_cache_manager/file.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -58,7 +58,7 @@ class _ContributorsCardState extends State<ContributorsCard> {
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
), ),
child: FutureBuilder<File?>( child: FutureBuilder<File?>(
future: DefaultCacheManager().getSingleFile( future: DownloadManager().getSingleFile(
widget.contributors[index]['avatar_url'], widget.contributors[index]['avatar_url'],
), ),
builder: (context, snapshot) => snapshot.hasData builder: (context, snapshot) => snapshot.hasData

View File

@@ -75,7 +75,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
const SizedBox(height: 16), const SizedBox(height: 16),
// ReVanced Patches // Patches
CustomCard( CustomCard(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -84,7 +84,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const Text('ReVanced Patches'), const Text('Patches'),
const SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
children: <Widget>[ children: <Widget>[

View File

@@ -48,13 +48,13 @@ class _PatchItemState extends State<PatchItem> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
widget.isSelected = widget.isSelected && widget.isSelected = widget.isSelected &&
(!widget.isUnsupported || (!widget.isUnsupported ||
widget._managerAPI.areExperimentalPatchesEnabled()) && !widget._managerAPI.isVersionCompatibilityCheckEnabled()) &&
!widget.hasUnsupportedPatchOption; !widget.hasUnsupportedPatchOption;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Opacity( child: Opacity(
opacity: widget.isUnsupported && opacity: widget.isUnsupported &&
widget._managerAPI.areExperimentalPatchesEnabled() == false widget._managerAPI.isVersionCompatibilityCheckEnabled() == true
? 0.5 ? 0.5
: 1, : 1,
child: CustomCard( child: CustomCard(
@@ -66,7 +66,7 @@ class _PatchItemState extends State<PatchItem> {
), ),
onTap: () { onTap: () {
if (widget.isUnsupported && if (widget.isUnsupported &&
!widget._managerAPI.areExperimentalPatchesEnabled()) { widget._managerAPI.isVersionCompatibilityCheckEnabled()) {
widget.isSelected = false; widget.isSelected = false;
widget.toast.showBottom('patchItem.unsupportedPatchVersion'); widget.toast.showBottom('patchItem.unsupportedPatchVersion');
} else if (widget.isChangeEnabled) { } else if (widget.isChangeEnabled) {
@@ -80,7 +80,7 @@ class _PatchItemState extends State<PatchItem> {
setState(() {}); setState(() {});
} }
if (!widget.isUnsupported || if (!widget.isUnsupported ||
widget._managerAPI.areExperimentalPatchesEnabled()) { !widget._managerAPI.isVersionCompatibilityCheckEnabled()) {
widget.onChanged(widget.isSelected); widget.onChanged(widget.isSelected);
} }
}, },
@@ -99,7 +99,8 @@ class _PatchItemState extends State<PatchItem> {
), ),
onChanged: (newValue) { onChanged: (newValue) {
if (widget.isUnsupported && if (widget.isUnsupported &&
!widget._managerAPI.areExperimentalPatchesEnabled()) { widget._managerAPI
.isVersionCompatibilityCheckEnabled()) {
widget.isSelected = false; widget.isSelected = false;
widget.toast.showBottom( widget.toast.showBottom(
'patchItem.unsupportedPatchVersion', 'patchItem.unsupportedPatchVersion',
@@ -115,7 +116,8 @@ class _PatchItemState extends State<PatchItem> {
setState(() {}); setState(() {});
} }
if (!widget.isUnsupported || if (!widget.isUnsupported ||
widget._managerAPI.areExperimentalPatchesEnabled()) { !widget._managerAPI
.isVersionCompatibilityCheckEnabled()) {
widget.onChanged(widget.isSelected); widget.onChanged(widget.isSelected);
} }
}, },
@@ -155,8 +157,8 @@ class _PatchItemState extends State<PatchItem> {
runSpacing: 4, runSpacing: 4,
children: [ children: [
if (widget.isUnsupported && if (widget.isUnsupported &&
widget._managerAPI !widget._managerAPI
.areExperimentalPatchesEnabled()) .isVersionCompatibilityCheckEnabled())
Padding( Padding(
padding: const EdgeInsets.only(top: 8), padding: const EdgeInsets.only(top: 8),
child: TextButton.icon( child: TextButton.icon(

View File

@@ -77,7 +77,7 @@ class IntAndStringPatchOption extends StatelessWidget {
if (patchOption.required && value == null) { if (patchOption.required && value == null) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children:[ children: [
const SizedBox(height: 8), const SizedBox(height: 8),
I18nText( I18nText(
'patchOptionsView.requiredOption', 'patchOptionsView.requiredOption',
@@ -139,11 +139,16 @@ class IntStringLongListPatchOption extends StatelessWidget {
value: e.toString(), value: e.toString(),
optionType: type, optionType: type,
onChanged: (newValue) { onChanged: (newValue) {
values[index] = type == 'StringListPatchOption' ? newValue : type == 'IntListPatchOption' ? int.parse(newValue) : num.parse(newValue); values[index] = type == 'StringListPatchOption'
? newValue
: type == 'IntListPatchOption'
? int.parse(newValue)
: num.parse(newValue);
onChanged(values, patchOption); onChanged(values, patchOption);
}, },
removeValue: (value) { removeValue: (value) {
patchOptionValue.value = List.from(patchOptionValue.value)..removeAt(index); patchOptionValue.value = List.from(patchOptionValue.value)
..removeAt(index);
values.removeAt(index); values.removeAt(index);
onChanged(values, patchOption); onChanged(values, patchOption);
}, },
@@ -154,35 +159,37 @@ class IntStringLongListPatchOption extends StatelessWidget {
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Align( Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: TextButton( child: TextButton(
onPressed: () { onPressed: () {
if (type == 'StringListPatchOption') { if (type == 'StringListPatchOption') {
patchOptionValue.value = List.from(patchOptionValue.value)..add(''); patchOptionValue.value = List.from(patchOptionValue.value)
values.add(''); ..add('');
} else { values.add('');
patchOptionValue.value = List.from(patchOptionValue.value)..add(0); } else {
values.add(0); patchOptionValue.value = List.from(patchOptionValue.value)
} ..add(0);
onChanged(values, patchOption); values.add(0);
}, }
child: Row( onChanged(values, patchOption);
mainAxisSize: MainAxisSize.min, },
children: [ child: Row(
const Icon(Icons.add, size: 20), mainAxisSize: MainAxisSize.min,
I18nText( children: [
'add', const Icon(Icons.add, size: 20),
child: const Text( I18nText(
'', 'add',
style: TextStyle( child: const Text(
fontSize: 14, '',
fontWeight: FontWeight.w600, style: TextStyle(
), fontSize: 14,
fontWeight: FontWeight.w600,
), ),
), ),
], ),
), ],
), ),
),
), ),
], ],
), ),
@@ -239,7 +246,6 @@ class PatchOption extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: CustomCard( child: CustomCard(
onTap: () {},
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(

View File

@@ -1,18 +1,14 @@
// ignore_for_file: prefer_const_constructors // ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_api_url.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_api_url.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_sources.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_sources.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_auto_update_patches.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_auto_update_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_enable_patches_selection.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_enable_patches_selection.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_universal_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_universal_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_version_compatibility_check.dart';
final _settingsViewModel = SettingsViewModel();
class SAdvancedSection extends StatelessWidget { class SAdvancedSection extends StatelessWidget {
const SAdvancedSection({super.key}); const SAdvancedSection({super.key});
@@ -21,85 +17,14 @@ class SAdvancedSection extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SettingsSection( return SettingsSection(
title: 'settingsView.advancedSectionTitle', title: 'settingsView.advancedSectionTitle',
children: <Widget>[ children: const <Widget>[
SManageApiUrlUI(),
SManageSourcesUI(),
// SManageKeystorePasswordUI(),
SAutoUpdatePatches(), SAutoUpdatePatches(),
SEnablePatchesSelection(), SEnablePatchesSelection(),
SExperimentalUniversalPatches(), SVersionCompatibilityCheck(),
SExperimentalPatches(), SUniversalPatches(),
ListTile( SManageSourcesUI(),
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), SManageApiUrlUI(),
title: I18nText(
'settingsView.regenerateKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.regenerateKeystoreHint'),
onTap: () => _showDeleteKeystoreDialog(context),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.deleteTempDirLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.deleteTempDirHint'),
onTap: () => _settingsViewModel.deleteTempDir(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.deleteLogsLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.deleteLogsHint'),
onTap: () => _settingsViewModel.deleteLogs(),
),
], ],
); );
} }
Future<void> _showDeleteKeystoreDialog(context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('settingsView.regenerateKeystoreDialogTitle'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('settingsView.regenerateKeystoreDialogText'),
actions: <Widget>[
CustomMaterialButton(
isFilled: false,
label: I18nText('noButton'),
onPressed: () => Navigator.of(context).pop(),
),
CustomMaterialButton(
label: I18nText('yesButton'),
onPressed: () => {
Navigator.of(context).pop(),
_settingsViewModel.deleteKeystore(),
},
),
],
),
);
}
} }

View File

@@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/about_widget.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
final _settingsViewModel = SettingsViewModel();
class SDebugSection extends StatelessWidget {
const SDebugSection({super.key});
@override
Widget build(BuildContext context) {
return SettingsSection(
title: 'settingsView.debugSectionTitle',
children: <Widget>[
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.logsLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.logsHint'),
onTap: () => _settingsViewModel.exportLogcatLogs(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.deleteLogsLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.deleteLogsHint'),
onTap: () => _settingsViewModel.deleteLogs(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.deleteTempDirLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.deleteTempDirHint'),
onTap: () => _settingsViewModel.deleteTempDir(),
),
const AboutWidget(
padding: EdgeInsets.symmetric(horizontal: 20.0),
),
],
);
}
}

View File

@@ -45,42 +45,6 @@ class SExportSection extends StatelessWidget {
subtitle: I18nText('settingsView.importPatchesHint'), subtitle: I18nText('settingsView.importPatchesHint'),
onTap: () => _settingsViewModel.importPatches(context), onTap: () => _settingsViewModel.importPatches(context),
), ),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.exportKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.exportKeystoreHint'),
onTap: () => _settingsViewModel.exportKeystore(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.importKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.importKeystoreHint'),
onTap: () async {
await _settingsViewModel.importKeystore();
final sManageKeystorePassword = SManageKeystorePassword();
if (context.mounted) {
sManageKeystorePassword.showKeystoreDialog(context);
}
},
),
ListTile( ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
@@ -121,6 +85,58 @@ class SExportSection extends StatelessWidget {
_settingsViewModel.resetAllOptions, _settingsViewModel.resetAllOptions,
), ),
), ),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.exportKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.exportKeystoreHint'),
onTap: () => _settingsViewModel.exportKeystore(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.importKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.importKeystoreHint'),
onTap: () async {
await _settingsViewModel.importKeystore();
final sManageKeystorePassword = SManageKeystorePassword();
if (context.mounted) {
sManageKeystorePassword.showKeystoreDialog(context);
}
},
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.regenerateKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.regenerateKeystoreHint'),
onTap: () => _showDeleteKeystoreDialog(context),
),
// SManageKeystorePasswordUI(),
], ],
); );
} }
@@ -154,4 +170,29 @@ class SExportSection extends StatelessWidget {
), ),
); );
} }
Future<void> _showDeleteKeystoreDialog(context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('settingsView.regenerateKeystoreDialogTitle'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('settingsView.regenerateKeystoreDialogText'),
actions: <Widget>[
CustomMaterialButton(
isFilled: false,
label: I18nText('noButton'),
onPressed: () => Navigator.of(context).pop(),
),
CustomMaterialButton(
label: I18nText('yesButton'),
onPressed: () => {
Navigator.of(context).pop(),
_settingsViewModel.deleteKeystore(),
},
),
],
),
);
}
} }

View File

@@ -1,38 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/about_widget.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
final _settingsViewModel = SettingsViewModel();
class SInfoSection extends StatelessWidget {
const SInfoSection({super.key});
@override
Widget build(BuildContext context) {
return SettingsSection(
title: 'settingsView.infoSectionTitle',
children: <Widget>[
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.logsLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.logsHint'),
onTap: () => _settingsViewModel.exportLogcatLogs(),
),
const AboutWidget(
padding: EdgeInsets.symmetric(horizontal: 20.0),
),
],
);
}
}

View File

@@ -4,26 +4,26 @@ import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
class SExperimentalUniversalPatches extends StatefulWidget { class SUniversalPatches extends StatefulWidget {
const SExperimentalUniversalPatches({super.key}); const SUniversalPatches({super.key});
@override @override
State<SExperimentalUniversalPatches> createState() => State<SUniversalPatches> createState() =>
_SExperimentalUniversalPatchesState(); _SUniversalPatchesState();
} }
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
final _patchesSelectorViewModel = PatchesSelectorViewModel(); final _patchesSelectorViewModel = PatchesSelectorViewModel();
final _patcherViewModel = PatcherViewModel(); final _patcherViewModel = PatcherViewModel();
class _SExperimentalUniversalPatchesState class _SUniversalPatchesState
extends State<SExperimentalUniversalPatches> { extends State<SUniversalPatches> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SwitchListTile( return SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.experimentalUniversalPatchesLabel', 'settingsView.universalPatchesLabel',
child: const Text( child: const Text(
'', '',
style: TextStyle( style: TextStyle(
@@ -32,7 +32,7 @@ class _SExperimentalUniversalPatchesState
), ),
), ),
), ),
subtitle: I18nText('settingsView.experimentalUniversalPatchesHint'), subtitle: I18nText('settingsView.universalPatchesHint'),
value: _settingsViewModel.areUniversalPatchesEnabled(), value: _settingsViewModel.areUniversalPatchesEnabled(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {

View File

@@ -5,24 +5,24 @@ import 'package:revanced_manager/ui/views/patches_selector/patches_selector_view
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart'; import 'package:revanced_manager/utils/check_for_supported_patch.dart';
class SExperimentalPatches extends StatefulWidget { class SVersionCompatibilityCheck extends StatefulWidget {
const SExperimentalPatches({super.key}); const SVersionCompatibilityCheck({super.key});
@override @override
State<SExperimentalPatches> createState() => _SExperimentalPatchesState(); State<SVersionCompatibilityCheck> createState() => _SVersionCompatibilityCheckState();
} }
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
final _patchesSelectorViewModel = PatchesSelectorViewModel(); final _patchesSelectorViewModel = PatchesSelectorViewModel();
final _patcherViewModel = PatcherViewModel(); final _patcherViewModel = PatcherViewModel();
class _SExperimentalPatchesState extends State<SExperimentalPatches> { class _SVersionCompatibilityCheckState extends State<SVersionCompatibilityCheck> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SwitchListTile( return SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.experimentalPatchesLabel', 'settingsView.versionCompatibilityCheckLabel',
child: const Text( child: const Text(
'', '',
style: TextStyle( style: TextStyle(
@@ -31,11 +31,11 @@ class _SExperimentalPatchesState extends State<SExperimentalPatches> {
), ),
), ),
), ),
subtitle: I18nText('settingsView.experimentalPatchesHint'), subtitle: I18nText('settingsView.versionCompatibilityCheckHint'),
value: _settingsViewModel.areExperimentalPatchesEnabled(), value: _settingsViewModel.isVersionCompatibilityCheckEnabled(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_settingsViewModel.useExperimentalPatches(value); _settingsViewModel.useVersionCompatibilityCheck(value);
}); });
if (!value) { if (!value) {
_patcherViewModel.selectedPatches _patcherViewModel.selectedPatches

View File

@@ -4,6 +4,7 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:revanced_manager/ui/widgets/settingsView/social_media_item.dart'; import 'package:revanced_manager/ui/widgets/settingsView/social_media_item.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_icon.dart';
class SocialMediaWidget extends StatelessWidget { class SocialMediaWidget extends StatelessWidget {
const SocialMediaWidget({ const SocialMediaWidget({
@@ -42,11 +43,17 @@ class SocialMediaWidget extends StatelessWidget {
child: const CustomCard( child: const CustomCard(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
SocialMediaItem(
icon: Icon(CustomIcon.revancedIcon),
title: Text('Website'),
subtitle: Text('revanced.app'),
url: 'https://revanced.app',
),
SocialMediaItem( SocialMediaItem(
icon: FaIcon(FontAwesomeIcons.github), icon: FaIcon(FontAwesomeIcons.github),
title: Text('GitHub'), title: Text('GitHub'),
subtitle: Text('github.com/revanced'), subtitle: Text('github.com/ReVanced'),
url: 'https://github.com/revanced', url: 'https://github.com/ReVanced',
), ),
SocialMediaItem( SocialMediaItem(
icon: FaIcon(FontAwesomeIcons.discord), icon: FaIcon(FontAwesomeIcons.discord),
@@ -67,10 +74,10 @@ class SocialMediaWidget extends StatelessWidget {
url: 'https://reddit.com/r/revancedapp', url: 'https://reddit.com/r/revancedapp',
), ),
SocialMediaItem( SocialMediaItem(
icon: FaIcon(FontAwesomeIcons.twitter), icon: FaIcon(FontAwesomeIcons.xTwitter),
title: Text('Twitter'), title: Text('X'),
subtitle: Text('@revancedapp'), subtitle: Text('@revancedapp'),
url: 'https://twitter.com/revancedapp', url: 'https://x.com/revancedapp',
), ),
SocialMediaItem( SocialMediaItem(
icon: FaIcon(FontAwesomeIcons.youtube), icon: FaIcon(FontAwesomeIcons.youtube),

View File

@@ -0,0 +1,9 @@
import 'package:flutter/widgets.dart';
class CustomIcon {
CustomIcon._();
static const _kFontFam = 'CustomIcon';
static const IconData revancedIcon = IconData(0xe800, fontFamily: _kFontFam);
}

7179
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
{
"devDependencies": {
"@saithodev/semantic-release-backmerge": "^3.1.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"semantic-release": "^21.0.1",
"semantic-release-export-data": "^1.0.1",
"semantic-release-flutter-plugin": "^1.1.2"
}
}

View File

@@ -4,7 +4,7 @@ homepage: https://github.com/revanced/revanced-manager
publish_to: 'none' publish_to: 'none'
version: 1.12.0+101200000 version: 1.14.2+101400200
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
@@ -41,7 +41,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_svg: ^2.0.4 flutter_svg: ^2.0.4
fluttertoast: ^8.2.1 fluttertoast: ^8.2.1
font_awesome_flutter: ^10.4.0 font_awesome_flutter: ^10.6.0
get_it: 7.2.0 get_it: 7.2.0
google_fonts: ^4.0.3 google_fonts: ^4.0.3
http: ^0.13.5 http: ^0.13.5
@@ -91,5 +91,9 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
fonts:
- family: CustomIcon
fonts:
- asset: fonts/custom-icons.ttf
assets: assets:
- assets/i18n/ - assets/i18n/