From b6ee63c1ea324dad4f96e62ce477ea267df4c479 Mon Sep 17 00:00:00 2001 From: Pun Butrach Date: Mon, 4 Sep 2023 11:15:17 +0700 Subject: [PATCH 01/13] ci(pr-build): sign apk with keystore (#1231) --- .github/workflows/pr-build.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 0bb9bdb1..2d4127a4 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -37,9 +37,26 @@ jobs: - name: Build with Flutter env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} + SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} + SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }} run: flutter build apk --debug + - name: Sign APK + id: sign_apk + uses: ilharp/sign-android-release@v1 + with: + releaseDir: build/app/outputs/apk/debug + signingKey: ${{ secrets.SIGNING_KEYSTORE }} + keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }} + keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }} + keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }} + - name: Get SHA short + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Add version to APK + run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-debug-${{ steps.vars.outputs.sha_short }}.apk - name: Upload build uses: actions/upload-artifact@v3 with: name: revanced-manager - path: build/app/outputs/flutter-apk/app-debug.apk + path: revanced-manager-debug-${{ steps.vars.outputs.sha_short }}.apk From 2bf6a03d563450409be71b4076e5d26982d29b6c Mon Sep 17 00:00:00 2001 From: aAbed <39409020+TheAabedKhan@users.noreply.github.com> Date: Mon, 4 Sep 2023 10:33:50 +0545 Subject: [PATCH 02/13] fix: back button closing the app from any page --- android/app/src/main/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 85483241..08e9e77c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -25,8 +25,7 @@ android:icon="@mipmap/ic_launcher" android:largeHeap="true" android:requestLegacyExternalStorage="true" - android:extractNativeLibs="true" - android:enableOnBackInvokedCallback="true"> + android:extractNativeLibs="true"> Date: Thu, 7 Sep 2023 16:33:31 +0300 Subject: [PATCH 03/13] docs(readme): minor improvements --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 07ed4a79..f10008f3 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,26 @@ The official ReVanced Manager based on Flutter. ## 🔽 Download -To download latest Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file. + +You can obtain ReVanced Manager by downloading it from either [revanced.app/download](https://revanced.app/download) or [GitHub Releases](https://github.com/ReVanced/revanced-manager/releases) ## 📝 Prerequisites + 1. Android 8 or higher -2. Does not work on some armv7 devices +2. Incompatible with certain ARMv7 devices ## 🔴 Issues + For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose). -## 💭 Discussion -If you wish to discuss the Manager, a thread has been made under the [#development](https://discord.com/channels/952946952348270622/1002922226443632761) channel in the Discord server, please note that this thread may be temporary and may be removed in the future. - - ## 🌐 Translation + [![Crowdin](https://badges.crowdin.net/revanced/localized.svg)](https://crowdin.com/project/revanced) -If you wish to translate ReVanced Manager, we're accepting translations on [Crowdin](https://translate.revanced.app) +We're accepting translations on [Crowdin](https://translate.revanced.app) ## 🛠️ Building Manager from source + 1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install) 2. Clone the repository locally 3. Add your github token in gradle.properties like [this](/docs/4_building.md) From 5c733932c7725c117758e96e56d6c01eb5d32737 Mon Sep 17 00:00:00 2001 From: Ushie Date: Thu, 7 Sep 2023 16:41:03 +0300 Subject: [PATCH 04/13] ci(pr-build): revert "sign apk with keystore (#1231)" This reverts commit 8bf08ff4641451db64c80fddc9a3281cdf73098b, as it fails for PRs originating from forks --- .github/workflows/pr-build.yml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 2d4127a4..0bb9bdb1 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -37,26 +37,9 @@ jobs: - name: Build with Flutter env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} - SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} - SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }} run: flutter build apk --debug - - name: Sign APK - id: sign_apk - uses: ilharp/sign-android-release@v1 - with: - releaseDir: build/app/outputs/apk/debug - signingKey: ${{ secrets.SIGNING_KEYSTORE }} - keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }} - keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }} - keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }} - - name: Get SHA short - id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Add version to APK - run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-debug-${{ steps.vars.outputs.sha_short }}.apk - name: Upload build uses: actions/upload-artifact@v3 with: name: revanced-manager - path: revanced-manager-debug-${{ steps.vars.outputs.sha_short }}.apk + path: build/app/outputs/flutter-apk/app-debug.apk From c1fc2c476645e52cba7472bdf09819ed9f3a7861 Mon Sep 17 00:00:00 2001 From: Pun Butrach Date: Sun, 10 Sep 2023 14:43:58 +0700 Subject: [PATCH 05/13] ci: bump actions/checkout to v4 --- .github/workflows/analyze.yml | 2 +- .github/workflows/pr-build.yml | 2 +- .github/workflows/release-build.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index e3f0f063..31f417ba 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -22,7 +22,7 @@ jobs: name: "Static analysis & format check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Flutter uses: subosito/flutter-action@v2 with: diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 0bb9bdb1..dbc20e29 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Make sure the release step uses its own credentials: # https://github.com/cycjimmy/semantic-release-action#private-packages diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 4b414eb7..7d31e5a0 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -9,7 +9,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Set up JDK 11 From b525ea1ba455910687891b7dcfa9f1982b6b2cf2 Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:40:00 -0700 Subject: [PATCH 06/13] ci: remove analyze workflow (#1262) Code style is not enforced so analysis is not needed. --- .github/workflows/analyze.yml | 38 ----------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/analyze.yml diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml deleted file mode 100644 index 31f417ba..00000000 --- a/.github/workflows/analyze.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Analyze Code - -on: - push: - branches: [ "dev" ] - paths: - - "**.dart" - - ".github/workflows/analyze.yml" - pull_request: - branches: [ "main", "dev" ] - types: - - opened - - reopened - - synchronize - - ready_for_review - paths: - - "**.dart" - - ".github/workflows/analyze.yml" - -jobs: - build: - name: "Static analysis & format check" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Flutter - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - cache: true - - name: Install Flutter dependencies - run: flutter pub get - - name: Generate files with Builder - run: flutter packages pub run build_runner build --delete-conflicting-outputs - - name: Analyze code - uses: ValentinVignal/action-dart-analyze@v0.15 - with: - fail-on: warning From f4b0a695d6cd21ee210106121fefa539dfc593aa Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Fri, 15 Sep 2023 08:58:15 -0700 Subject: [PATCH 07/13] fix: improve app list loading speed (#1166) --- lib/services/patcher_api.dart | 72 ++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index dfaaf3ec..9935d9ea 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -25,6 +25,8 @@ class PatcherAPI { late Directory _tmpDir; late File _keyStoreFile; List _patches = []; + List _universalPatches = []; + List _compatiblePackages = []; Map filteredPatches = >{}; File? _outFile; @@ -45,6 +47,24 @@ class PatcherAPI { } } + List getCompatiblePackages() { + final List compatiblePackages = []; + for (final Patch patch in _patches) { + for (final Package package in patch.compatiblePackages) { + if (!compatiblePackages.contains(package.name)) { + compatiblePackages.add(package.name); + } + } + } + return compatiblePackages; + } + + List getUniversalPatches() { + return _patches + .where((patch) => patch.compatiblePackages.isEmpty) + .toList(); + } + Future _loadPatches() async { try { if (_patches.isEmpty) { @@ -56,6 +76,9 @@ class PatcherAPI { } _patches = List.empty(); } + + _compatiblePackages = getCompatiblePackages(); + _universalPatches = getUniversalPatches(); } Future> getFilteredInstalledApps( @@ -63,48 +86,43 @@ class PatcherAPI { ) async { final List filteredApps = []; final bool allAppsIncluded = - _patches.any((patch) => patch.compatiblePackages.isEmpty) && + _universalPatches.isNotEmpty && showUniversalPatches; if (allAppsIncluded) { - final allPackages = await DeviceApps.getInstalledApplications( + final appList = await DeviceApps.getInstalledApplications( includeAppIcons: true, onlyAppsWithLaunchIntent: true, ); - for (final pkg in allPackages) { - if (!filteredApps.any((app) => app.packageName == pkg.packageName)) { - final appInfo = await DeviceApps.getApp( - pkg.packageName, - true, - ) as ApplicationWithIcon?; - if (appInfo != null) { - filteredApps.add(appInfo); - } - } + + for(final app in appList) { + filteredApps.add(app as ApplicationWithIcon); } } - for (final Patch patch in _patches) { - for (final Package package in patch.compatiblePackages) { - try { - if (!filteredApps.any((app) => app.packageName == package.name)) { - final ApplicationWithIcon? app = await DeviceApps.getApp( - package.name, - true, - ) as ApplicationWithIcon?; - if (app != null) { - filteredApps.add(app); - } - } - } on Exception catch (e) { - if (kDebugMode) { - print(e); + for (final packageName in _compatiblePackages) { + try { + if (!filteredApps.any((app) => app.packageName == packageName)) { + final ApplicationWithIcon? app = await DeviceApps.getApp( + packageName, + true, + ) as ApplicationWithIcon?; + if (app != null) { + filteredApps.add(app); } } + } on Exception catch (e) { + if (kDebugMode) { + print(e); + } } } return filteredApps; } List getFilteredPatches(String packageName) { + if (!_compatiblePackages.contains(packageName)) { + return _universalPatches; + } + final List patches = _patches .where( (patch) => From 4cdd9acd73e732c04263e11d6c0e172d58ccea3f Mon Sep 17 00:00:00 2001 From: KobeW50 <84587632+KobeW50@users.noreply.github.com> Date: Sat, 16 Sep 2023 06:38:06 -0400 Subject: [PATCH 08/13] docs(readme): add documentation and minor fixes (#1264) Co-authored-by: Pun Butrach --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f10008f3..89f41ea6 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ You can obtain ReVanced Manager by downloading it from either [revanced.app/down 1. Android 8 or higher 2. Incompatible with certain ARMv7 devices +## 📃 Documentation +The documentation can be found [here](https://github.com/revanced/revanced-manager/tree/main/docs). + ## 🔴 Issues For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose). @@ -19,14 +22,14 @@ For suggestions and bug reports, open an issue [here](https://github.com/revance [![Crowdin](https://badges.crowdin.net/revanced/localized.svg)](https://crowdin.com/project/revanced) -We're accepting translations on [Crowdin](https://translate.revanced.app) +We're accepting translations on [Crowdin](https://translate.revanced.app). ## 🛠️ Building Manager from source 1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install) 2. Clone the repository locally -3. Add your github token in gradle.properties like [this](/docs/4_building.md) +3. Add your GitHub token in gradle.properties like [this](/docs/4_building.md) 4. Open the project in terminal 5. Run `flutter pub get` in terminal 6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull) -7. To build release apk run `flutter build apk` +7. To build release APK run `flutter build apk` From 9ad1d6cbfb04a923d01bd1b900e0d27f07055131 Mon Sep 17 00:00:00 2001 From: Julienraptor01 Date: Wed, 20 Sep 2023 23:42:29 +0200 Subject: [PATCH 09/13] fix(custom-sources): ignore casing when checking if default repo is being used (#1281) --- lib/services/manager_api.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 6551d0f4..4d4b3f44 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -41,11 +41,11 @@ class ManagerAPI { String? patchesVersion = ''; String? integrationsVersion = ''; bool isDefaultPatchesRepo() { - return getPatchesRepo() == 'revanced/revanced-patches'; + return getPatchesRepo().toLowerCase() == 'revanced/revanced-patches'; } bool isDefaultIntegrationsRepo() { - return getIntegrationsRepo() == 'revanced/revanced-integrations'; + return getIntegrationsRepo().toLowerCase() == 'revanced/revanced-integrations'; } Future initialize() async { From 4b8542b35b0245caf36ee0c90b63b57e405998bc Mon Sep 17 00:00:00 2001 From: aAbed <39409020+TheAabedKhan@users.noreply.github.com> Date: Wed, 20 Sep 2023 22:35:32 +0000 Subject: [PATCH 10/13] fix: load patches via `PatchBundle` (#1242) --- .../revanced/manager/flutter/MainActivity.kt | 26 +++++++++ lib/services/github_api.dart | 4 +- lib/services/manager_api.dart | 53 +++++++++++-------- lib/services/patcher_api.dart | 1 - 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt index 4bdb6cf0..ef7a2a65 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt @@ -11,6 +11,9 @@ import app.revanced.patcher.PatchBundleLoader import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages +import app.revanced.patcher.extensions.PatchExtensions.dependencies +import app.revanced.patcher.extensions.PatchExtensions.description +import app.revanced.patcher.extensions.PatchExtensions.include import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.PatchResult import io.flutter.embedding.android.FlutterActivity @@ -89,6 +92,29 @@ class MainActivity : FlutterActivity() { stopResult = result } + "getPatches" -> { + val patchBundleFilePath = call.argument("patchBundleFilePath") + if (patchBundleFilePath != null) { + val patches = PatchBundleLoader.Dex( + File(patchBundleFilePath) + ).map { patch -> + val map = HashMap() + map["\"name\""] = "\"${patch.patchName.replace("\"","\\\"")}\"" + map["\"description\""] = "\"${patch.description?.replace("\"","\\\"")}\"" + map["\"excluded\""] = !patch.include + map["\"dependencies\""] = patch.dependencies?.map { "\"${it.java.patchName}\"" } ?: emptyList() + map["\"compatiblePackages\""] = patch.compatiblePackages?.map { + val map2 = HashMap() + map2["\"name\""] = "\"${it.name}\"" + map2["\"versions\""] = it.versions.map { version -> "\"${version}\"" } + map2 + } ?: emptyList() + map + } + result.success(patches) + } else result.notImplemented() + } + else -> result.notImplemented() } } diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index de8c160b..0949f1b9 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -222,10 +222,8 @@ class GithubAPI { final String downloadUrl = asset['browser_download_url']; if (extension == '.apk') { _managerAPI.setIntegrationsDownloadURL(downloadUrl); - } else if (extension == '.json') { - _managerAPI.setPatchesDownloadURL(downloadUrl, false); } else { - _managerAPI.setPatchesDownloadURL(downloadUrl, true); + _managerAPI.setPatchesDownloadURL(downloadUrl); } return await DefaultCacheManager().getSingleFile( downloadUrl, diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 4d4b3f44..0b2cb58d 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -11,6 +11,7 @@ import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; +import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; @@ -26,6 +27,7 @@ class ManagerAPI { final String patcherRepo = 'revanced-patcher'; final String cliRepo = 'revanced-cli'; late SharedPreferences _prefs; + List patches = []; bool isRooted = false; String storedPatchesFile = '/selected-patches.json'; String keystoreFile = @@ -79,12 +81,12 @@ class ManagerAPI { await _prefs.setString('repoUrl', url); } - String getPatchesDownloadURL(bool bundle) { - return _prefs.getString('patchesDownloadURL-$bundle') ?? ''; + String getPatchesDownloadURL() { + return _prefs.getString('patchesDownloadURL') ?? ''; } - Future setPatchesDownloadURL(String value, bool bundle) async { - await _prefs.setString('patchesDownloadURL-$bundle', value); + Future setPatchesDownloadURL(String value) async { + await _prefs.setString('patchesDownloadURL', value); } String getPatchesRepo() { @@ -300,28 +302,38 @@ class ManagerAPI { } Future> getPatches() async { - try { - final String repoName = getPatchesRepo(); - final String currentVersion = await getCurrentPatchesVersion(); - final String url = getPatchesDownloadURL(false); - return await _githubAPI.getPatches( - repoName, - currentVersion, - url, - ); - } on Exception catch (e) { - if (kDebugMode) { - print(e); - } - return []; + if (patches.isNotEmpty) { + return patches; } + final File? patchBundleFile = await downloadPatches(); + if (patchBundleFile != null) { + try { + final patchesObject = await PatcherAPI.patcherChannel.invokeMethod( + 'getPatches', + { + 'patchBundleFilePath': patchBundleFile.path, + }, + ); + final List> patchesMap = []; + patchesObject.forEach((patch) { + patchesMap.add(jsonDecode('$patch')); + }); + patches = patchesMap.map((patch) => Patch.fromJson(patch)).toList(); + return patches; + } on Exception catch (e) { + if (kDebugMode) { + print(e); + } + } + } + return List.empty(); } Future downloadPatches() async { try { final String repoName = getPatchesRepo(); final String currentVersion = await getCurrentPatchesVersion(); - final String url = getPatchesDownloadURL(true); + final String url = getPatchesDownloadURL(); return await _githubAPI.getPatchesReleaseFile( '.jar', repoName, @@ -447,8 +459,7 @@ class ManagerAPI { Future setCurrentPatchesVersion(String version) async { await _prefs.setString('patchesVersion', version); - await setPatchesDownloadURL('', false); - await setPatchesDownloadURL('', true); + await setPatchesDownloadURL(''); await downloadPatches(); } diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 9935d9ea..5838ab3b 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -32,7 +32,6 @@ class PatcherAPI { Future initialize() async { await _loadPatches(); - await _managerAPI.downloadPatches(); await _managerAPI.downloadIntegrations(); final Directory appCache = await getTemporaryDirectory(); _dataDir = await getExternalStorageDirectory() ?? appCache; From 42b6bbff7cd7d2f8035e84bfc4eeaf62e76e9af0 Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:16:55 -0700 Subject: [PATCH 11/13] fix: update youtube link (#1286) --- lib/ui/widgets/settingsView/social_media_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/widgets/settingsView/social_media_widget.dart b/lib/ui/widgets/settingsView/social_media_widget.dart index 73a6a2ee..7cc686f1 100644 --- a/lib/ui/widgets/settingsView/social_media_widget.dart +++ b/lib/ui/widgets/settingsView/social_media_widget.dart @@ -75,8 +75,8 @@ class SocialMediaWidget extends StatelessWidget { SocialMediaItem( icon: FaIcon(FontAwesomeIcons.youtube), title: Text('YouTube'), - subtitle: Text('youtube.com/revanced'), - url: 'https://youtube.com/revanced', + subtitle: Text('youtube.com/@revanced'), + url: 'https://youtube.com/@revanced', ), ], ), From b7acb475e916c1d30b393738bc5d9551caaf5f26 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 20 Sep 2023 16:57:13 -0700 Subject: [PATCH 12/13] fix: update install type dialog padding --- .../views/installer/installer_viewmodel.dart | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index b11d47da..f523f2a0 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -182,52 +182,54 @@ class InstallerViewModel extends BaseViewModel { backgroundColor: Theme.of(context).colorScheme.secondaryContainer, icon: const Icon(Icons.file_download_outlined), contentPadding: const EdgeInsets.symmetric(vertical: 16), - content: ValueListenableBuilder( - valueListenable: installType, - builder: (context, value, child) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 10, - ), - child: I18nText( - 'installerView.installTypeDescription', - child: Text( - '', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.secondary, + content: SingleChildScrollView( + child: ValueListenableBuilder( + valueListenable: installType, + builder: (context, value, child) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 10, + ), + child: I18nText( + 'installerView.installTypeDescription', + child: Text( + '', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), ), ), ), - ), - RadioListTile( - title: I18nText('installerView.installNonRootType'), - subtitle: I18nText('installerView.installRecommendedType'), - contentPadding: const EdgeInsets.symmetric(horizontal: 10), - value: 0, - groupValue: value, - onChanged: (selected) { - installType.value = selected!; - }, - ), - RadioListTile( - title: I18nText('installerView.installRootType'), - contentPadding: const EdgeInsets.symmetric(horizontal: 10), - value: 1, - groupValue: value, - onChanged: (selected) { - installType.value = selected!; - }, - ), - ], - ); - }, + RadioListTile( + title: I18nText('installerView.installNonRootType'), + subtitle: I18nText('installerView.installRecommendedType'), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + value: 0, + groupValue: value, + onChanged: (selected) { + installType.value = selected!; + }, + ), + RadioListTile( + title: I18nText('installerView.installRootType'), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + value: 1, + groupValue: value, + onChanged: (selected) { + installType.value = selected!; + }, + ), + ], + ); + }, + ), ), actions: [ CustomMaterialButton( From 6260a807388d47737b415aa8ec98a31807be6085 Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:25:23 -0700 Subject: [PATCH 13/13] feat(settings - appearance): add system option (#1279) Closes #1260 --- assets/i18n/en_US.json | 6 +- lib/services/manager_api.dart | 8 +- lib/ui/theme/dynamic_theme_builder.dart | 69 ++++++++-- .../navigation/navigation_viewmodel.dart | 7 +- .../settings_update_theme.dart | 124 ++++++++++++++---- 5 files changed, 165 insertions(+), 49 deletions(-) diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index 63e82677..05694ced 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -178,8 +178,10 @@ "exportSectionTitle": "Import & export", "logsSectionTitle": "Logs", - "darkThemeLabel": "Dark mode", - "darkThemeHint": "Welcome to the dark side", + "themeModeLabel": "App theme", + "systemThemeLabel": "System", + "lightThemeLabel": "Light", + "darkThemeLabel": "Dark", "dynamicThemeLabel": "Material You", "dynamicThemeHint": "Enjoy an experience closer to your device", diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 0b2cb58d..af996fe2 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -199,12 +199,12 @@ class ManagerAPI { await _prefs.setBool('useDynamicTheme', value); } - bool getUseDarkTheme() { - return _prefs.getBool('useDarkTheme') ?? false; + int getThemeMode() { + return _prefs.getInt('themeMode') ?? 2; } - Future setUseDarkTheme(bool value) async { - await _prefs.setBool('useDarkTheme', value); + Future setThemeMode(int value) async { + await _prefs.setInt('themeMode', value); } bool areUniversalPatchesEnabled() { diff --git a/lib/ui/theme/dynamic_theme_builder.dart b/lib/ui/theme/dynamic_theme_builder.dart index 65d74c2c..5fcd8831 100644 --- a/lib/ui/theme/dynamic_theme_builder.dart +++ b/lib/ui/theme/dynamic_theme_builder.dart @@ -1,12 +1,16 @@ +import 'dart:ui'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_themes/dynamic_themes.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.router.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/theme.dart'; import 'package:stacked_services/stacked_services.dart'; -class DynamicThemeBuilder extends StatelessWidget { +class DynamicThemeBuilder extends StatefulWidget { const DynamicThemeBuilder({ Key? key, required this.title, @@ -17,6 +21,35 @@ class DynamicThemeBuilder extends StatelessWidget { final Widget home; final Iterable localizationsDelegates; + @override + State createState() => _DynamicThemeBuilderState(); +} + +class _DynamicThemeBuilderState extends State with WidgetsBindingObserver { + Brightness brightness = PlatformDispatcher.instance.platformBrightness; + final ManagerAPI _managerAPI = locator(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangePlatformBrightness() { + setState(() { + brightness = PlatformDispatcher.instance.platformBrightness; + }); + if (_managerAPI.getThemeMode() < 2) { + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarIconBrightness: + brightness == Brightness.light ? Brightness.dark : Brightness.light, + ), + ); + } + } + @override Widget build(BuildContext context) { return DynamicColorBuilder( @@ -50,24 +83,32 @@ class DynamicThemeBuilder extends StatelessWidget { return DynamicTheme( themeCollection: ThemeCollection( themes: { - 0: lightCustomTheme, - 1: darkCustomTheme, - 2: lightDynamicTheme, - 3: darkDynamicTheme, + 0: brightness == Brightness.light ? lightCustomTheme : darkCustomTheme, + 1: brightness == Brightness.light ? lightDynamicTheme : darkDynamicTheme, + 2: lightCustomTheme, + 3: lightDynamicTheme, + 4: darkCustomTheme, + 5: darkDynamicTheme, }, - fallbackTheme: lightCustomTheme, + fallbackTheme: brightness == Brightness.light ? lightCustomTheme : darkCustomTheme, ), builder: (context, theme) => MaterialApp( - debugShowCheckedModeBanner: false, - title: title, - navigatorKey: StackedService.navigatorKey, - onGenerateRoute: StackedRouter().onGenerateRoute, - theme: theme, - home: home, - localizationsDelegates: localizationsDelegates, - ), + debugShowCheckedModeBanner: false, + title: widget.title, + navigatorKey: StackedService.navigatorKey, + onGenerateRoute: StackedRouter().onGenerateRoute, + theme: theme, + home: widget.home, + localizationsDelegates: widget.localizationsDelegates, + ), ); }, ); } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } } diff --git a/lib/ui/views/navigation/navigation_viewmodel.dart b/lib/ui/views/navigation/navigation_viewmodel.dart index 0967501d..8e36a630 100644 --- a/lib/ui/views/navigation/navigation_viewmodel.dart +++ b/lib/ui/views/navigation/navigation_viewmodel.dart @@ -30,11 +30,8 @@ class NavigationViewModel extends IndexTrackingViewModel { ); } - if (prefs.getBool('useDarkTheme') == null) { - final bool isDark = - MediaQuery.platformBrightnessOf(context) != Brightness.light; - await prefs.setBool('useDarkTheme', isDark); - await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0); + if (prefs.getInt('themeMode') == null) { + await prefs.setInt('themeMode', 0); } SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle( diff --git a/lib/ui/views/settings/settingsFragment/settings_update_theme.dart b/lib/ui/views/settings/settingsFragment/settings_update_theme.dart index 684abc96..1abd0f8c 100644 --- a/lib/ui/views/settings/settingsFragment/settings_update_theme.dart +++ b/lib/ui/views/settings/settingsFragment/settings_update_theme.dart @@ -8,6 +8,7 @@ import 'package:revanced_manager/app/app.locator.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/shared/custom_material_button.dart'; import 'package:stacked/stacked.dart'; final _settingViewModel = SettingsViewModel(); @@ -24,37 +25,114 @@ class SUpdateTheme extends BaseViewModel { Future setUseDynamicTheme(BuildContext context, bool value) async { await _managerAPI.setUseDynamicTheme(value); - final int currentTheme = DynamicTheme.of(context)!.themeId; - if (currentTheme.isEven) { - await DynamicTheme.of(context)!.setTheme(value ? 2 : 0); - } else { - await DynamicTheme.of(context)!.setTheme(value ? 3 : 1); - } + final int currentTheme = (DynamicTheme.of(context)!.themeId ~/ 2) * 2; + await DynamicTheme.of(context)!.setTheme(currentTheme + (value ? 1 : 0)); notifyListeners(); } - bool getDarkThemeStatus() { - return _managerAPI.getUseDarkTheme(); + int getThemeMode() { + return _managerAPI.getThemeMode(); } - Future setUseDarkTheme(BuildContext context, bool value) async { - await _managerAPI.setUseDarkTheme(value); - final int currentTheme = DynamicTheme.of(context)!.themeId; - if (currentTheme < 2) { - await DynamicTheme.of(context)!.setTheme(value ? 1 : 0); - } else { - await DynamicTheme.of(context)!.setTheme(value ? 3 : 2); - } + Future setThemeMode(BuildContext context, int value) async { + await _managerAPI.setThemeMode(value); + final bool isDynamicTheme = DynamicTheme.of(context)!.themeId.isEven; + await DynamicTheme.of(context)!.setTheme(value * 2 + (isDynamicTheme ? 0 : 1)); + final bool isLight = value != 2 && (value == 1 || DynamicTheme.of(context)!.theme.brightness == Brightness.light); SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle( systemNavigationBarIconBrightness: - value ? Brightness.light : Brightness.dark, + isLight ? Brightness.dark : Brightness.light, ), ); notifyListeners(); } + + I18nText getThemeModeName() { + switch (getThemeMode()) { + case 0: + return I18nText('settingsView.systemThemeLabel'); + case 1: + return I18nText('settingsView.lightThemeLabel'); + case 2: + return I18nText('settingsView.darkThemeLabel'); + default: + return I18nText('settingsView.systemThemeLabel'); + } + } + + Future showThemeDialog(BuildContext context) async { + final ValueNotifier newTheme = ValueNotifier(getThemeMode()); + + return showDialog( + context: context, + builder: (context) => AlertDialog( + title: I18nText('settingsView.themeModeLabel'), + icon: const Icon(Icons.palette), + contentPadding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Theme.of(context).colorScheme.secondaryContainer, + content: SingleChildScrollView( + child: ValueListenableBuilder( + valueListenable: newTheme, + builder: (context, value, child) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RadioListTile( + title: I18nText('settingsView.systemThemeLabel'), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + value: 0, + groupValue: value, + onChanged: (value) { + newTheme.value = value!; + }, + ), + RadioListTile( + title: I18nText('settingsView.lightThemeLabel'), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + value: 1, + groupValue: value, + onChanged: (value) { + newTheme.value = value!; + }, + ), + RadioListTile( + title: I18nText('settingsView.darkThemeLabel'), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + value: 2, + groupValue: value, + onChanged: (value) { + newTheme.value = value!; + }, + ), + ], + ); + }, + ), + ), + actions: [ + CustomMaterialButton( + isFilled: false, + label: I18nText('cancelButton'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + CustomMaterialButton( + label: I18nText('okButton'), + onPressed: () { + setThemeMode(context, newTheme.value); + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } } +final sUpdateTheme = SUpdateTheme(); class SUpdateThemeUI extends StatelessWidget { const SUpdateThemeUI({super.key}); @@ -63,10 +141,10 @@ class SUpdateThemeUI extends StatelessWidget { return SettingsSection( title: 'settingsView.appearanceSectionTitle', children: [ - SwitchListTile( + ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( - 'settingsView.darkThemeLabel', + 'settingsView.themeModeLabel', child: const Text( '', style: TextStyle( @@ -75,11 +153,9 @@ class SUpdateThemeUI extends StatelessWidget { ), ), ), - subtitle: I18nText('settingsView.darkThemeHint'), - value: SUpdateTheme().getDarkThemeStatus(), - onChanged: (value) => SUpdateTheme().setUseDarkTheme( - context, - value, + trailing: CustomMaterialButton( + label: sUpdateTheme.getThemeModeName(), + onPressed: () => { sUpdateTheme.showThemeDialog(context) }, ), ), FutureBuilder(