Compare commits

..

2 Commits

Author SHA1 Message Date
oSumAtrIX
d3d8f89d53 chore: bump version to 0.0.55 2023-02-10 05:34:10 +01:00
oSumAtrIX
3d8aee09c3 build: bump patcher version 2023-02-10 05:33:50 +01:00
85 changed files with 900 additions and 8925 deletions

3
.env Normal file
View File

@@ -0,0 +1,3 @@
sentryDSN=
apiKey=
appId=

2
.github/config.yaml vendored
View File

@@ -1,2 +0,0 @@
firstPRMergeComment: >
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.

View File

@@ -1,32 +0,0 @@
name: Analyze Code
on:
push:
branches: [ "main", "dev" ]
paths:
- "**.dart"
- ".github/workflows/analyze.yml"
pull_request:
branches: [ "main", "dev" ]
paths:
- "**.dart"
- ".github/workflows/analyze.yml"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- 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

55
.github/workflows/commit-build.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: "Android CI Actions"
on:
push:
branches:
- "**"
tags-ignore:
- "v*" # Ignore tags that start with "v" (e.g. v1.0.0) because they are handled by release-build.yml
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up JDK 12
uses: actions/setup-java@v3
with:
java-version: '12'
distribution: 'zulu'
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Set environment variables
run: echo $SECRETS | base64 -d > lib/utils/environment.dart
env:
SECRETS: ${{ secrets.SECRETS }}
- name: Set up Flutter
run: flutter pub get
- name: Generate files with Builder
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- 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
- name: Sign APK
id: sign_apk
uses: ilharp/sign-android-release@v1
with:
releaseDir: build/app/outputs/apk/release
signingKey: ${{ secrets.SIGNING_KEYSTORE }}
keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
- name: Add version to APK
run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Upload APK
uses: actions/upload-artifact@v3
with:
name: revanced-manager-${{ env.RELEASE_VERSION }}
path: revanced-manager-${{ env.RELEASE_VERSION }}.apk

View File

@@ -3,7 +3,7 @@ name: Sync Crowdin translations
on: on:
push: push:
branches: branches:
- "dev" - "flutter"
paths: paths:
- "assets/i18n/en_US.json" - "assets/i18n/en_US.json"
- ".github/workflows/crowdin.yml" - ".github/workflows/crowdin.yml"

View File

@@ -1,32 +1,30 @@
name: PR Build name: "Android CI PR Build"
on: on:
workflow_dispatch: pull_request:
branches:
- "**"
jobs: jobs:
build: release:
name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - uses: actions/checkout@v3
uses: actions/checkout@v3 - name: Set env
with: run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# Make sure the release step uses its own credentials: - name: Set up JDK 12
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Setup JDK
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
java-version: '17' java-version: '12'
distribution: 'zulu' distribution: 'zulu'
cache: 'gradle' - uses: subosito/flutter-action@v2
- name: Setup Flutter
uses: subosito/flutter-action@v2
with: with:
channel: 'stable' channel: 'stable'
cache: true - name: Set environment variables
- name: Install Flutter dependencies run: echo $SECRETS | base64 -d > lib/utils/environment.dart
env:
SECRETS: ${{ secrets.SECRETS }}
- name: Set up Flutter
run: flutter pub get run: flutter pub get
- name: Generate files with Builder - name: Generate files with Builder
run: flutter packages pub run build_runner build --delete-conflicting-outputs run: flutter packages pub run build_runner build --delete-conflicting-outputs
@@ -34,8 +32,10 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: flutter build apk run: flutter build apk
- name: Upload build - name: Add version to APK
run: mv build/app/outputs/flutter-apk/app-release.apk revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Upload APK
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: revanced-manager name: revanced-manager-${{ env.RELEASE_VERSION }}
path: build/app/outputs/flutter-apk/app-release.apk path: revanced-manager-${{ env.RELEASE_VERSION }}.apk

54
.github/workflows/release-build.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: "Release Build"
on:
push:
tags:
- "v*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up JDK 12
uses: actions/setup-java@v3
with:
java-version: '12'
distribution: 'zulu'
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Set environment variables
run: echo $SECRETS | base64 -d > lib/utils/environment.dart
env:
SECRETS: ${{ secrets.SECRETS }}
- name: Set up Flutter
run: flutter pub get
- name: Generate files with Builder
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- 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
- name: Sign APK
id: sign_apk
uses: ilharp/sign-android-release@v1
with:
releaseDir: build/app/outputs/apk/release
signingKey: ${{ secrets.SIGNING_KEYSTORE }}
keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
- name: Add version to APK
run: mv ${{steps.sign_apk.outputs.signedFile}} revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Publish release APK
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: revanced-manager-${{ env.RELEASE_VERSION }}.apk

View File

@@ -1,68 +0,0 @@
name: Release
on:
workflow_dispatch:
push:
branches:
- main
- dev
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Setup JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'zulu'
cache: 'gradle'
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- 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: Build with Flutter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: flutter build apk
- name: Sign APK
id: sign_apk
uses: ilharp/sign-android-release@v1
with:
releaseDir: build/app/outputs/apk/release
signingKey: ${{ secrets.SIGNING_KEYSTORE }}
keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
- name: Setup semantic-release
run: npm ci
- name: Get release version
run: npm exec -- semantic-release --dry-run
id: get-next-version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Add version to APK
run: mv $SIGNED_FILE_PATH $(dirname $SIGNED_FILE_PATH)/revanced-manager-${{ steps.get-next-version.outputs.new-release-version }}.apk
env:
SIGNED_FILE_PATH: ${{steps.sign_apk.outputs.signedFile}}
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
run: npm exec semantic-release

5
.gitignore vendored
View File

@@ -134,8 +134,5 @@ app.*.map.json
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock !/dev/ci/**/Gemfile.lock
# Firebase related Firebase related
.firebase .firebase
# Dependency directories
node_modules/

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,74 +0,0 @@
# [1.0.0-dev.1](https://github.com/revanced/revanced-manager/compare/v0.0.57...v1.0.0-dev.1) (2023-04-29)
### Bug Fixes
* fix broken manager update implmentation ([6492507](https://github.com/revanced/revanced-manager/commit/6492507d0e0015b594f7d9e97bbadee9501b29d6))
* **i18n:** update translation for `refreshSucess` ([b286444](https://github.com/revanced/revanced-manager/commit/b286444ad93fc6009412ac14b996ff6268069811))
* open contributor links externally ([#791](https://github.com/revanced/revanced-manager/issues/791)) ([f0b0282](https://github.com/revanced/revanced-manager/commit/f0b028279c69f97817952063d84809d3e486ad6e))
* resized monochrome icon to match the original ([#789](https://github.com/revanced/revanced-manager/issues/789)) ([ac830cb](https://github.com/revanced/revanced-manager/commit/ac830cbe7f3b1ebd7849e586b829d3c077436a0d))
* update pubspec version ([65da6af](https://github.com/revanced/revanced-manager/commit/65da6af3f96550b138dcaf61832a5de23f529b32))
* chore!: merge `dev` to `main` (#830) ([782c012](https://github.com/revanced/revanced-manager/commit/782c0127b1f6f5380bd8ca23c8c2f280e2bfc432)), closes [#830](https://github.com/revanced/revanced-manager/issues/830)
### Features
* add continue anyway button to select from storage dialog ([#810](https://github.com/revanced/revanced-manager/issues/810)) ([affba66](https://github.com/revanced/revanced-manager/commit/affba669ce1ca6866a1dd1bd801e3f33e4bfe051))
* add option to import/export keystore ([#776](https://github.com/revanced/revanced-manager/issues/776)) ([dca2d4f](https://github.com/revanced/revanced-manager/commit/dca2d4fe126a6966a094d335e0f27bb62d76c5e8))
* appreciation message for new contributors ([0a1f2da](https://github.com/revanced/revanced-manager/commit/0a1f2da33da7d44f0613b19f3e6b2b7b50240548))
* auto select default patches ([4c9cb56](https://github.com/revanced/revanced-manager/commit/4c9cb560e3e38295a5140419f2565b478cb6c497))
* change `continue anyways` to `cancel` ([cfc866b](https://github.com/revanced/revanced-manager/commit/cfc866bef2497bc1675bf5dea834cea59d9cc969))
* **ci:** update crowdin workflow to use new main branch ([ded59d2](https://github.com/revanced/revanced-manager/commit/ded59d2da0d193b2dea4a5a7f2fc8eefaceecc0a))
* confirmation dialog for deleting keystore ([#764](https://github.com/revanced/revanced-manager/issues/764)) ([054afbb](https://github.com/revanced/revanced-manager/commit/054afbbedd70a1933d8241ff5b63a772f90b555f))
* disable selecting installed apps for nonroot ([bb681e3](https://github.com/revanced/revanced-manager/commit/bb681e31c9c4e8a5b7b0c883edd1bc5c28505627))
* improve suggested app version text ([#822](https://github.com/revanced/revanced-manager/issues/822)) ([ad17995](https://github.com/revanced/revanced-manager/commit/ad17995f2883682f67eb42b1f82ca865fba86ef9))
* improve ux ([#752](https://github.com/revanced/revanced-manager/issues/752)) ([3b677f8](https://github.com/revanced/revanced-manager/commit/3b677f8ae3739c079e2116417fef6ed395c2ff06)), closes [#782](https://github.com/revanced/revanced-manager/issues/782)
* progress bar for manager updates ([f5aafdb](https://github.com/revanced/revanced-manager/commit/f5aafdb7d6f51386b667abbccf7f2521ef664ba5))
* remove notice about stale development [skip ci] ([62f7a82](https://github.com/revanced/revanced-manager/commit/62f7a820d8ee2506376306e119698d427de745ef))
* resetting source to default dismiss the sources pop-up ([#797](https://github.com/revanced/revanced-manager/issues/797)) ([62ef1c8](https://github.com/revanced/revanced-manager/commit/62ef1c88fe0352d3962f8c73edff4b99ea347c0f))
* trim extra space when setting custom source ([#771](https://github.com/revanced/revanced-manager/issues/771)) ([37b583f](https://github.com/revanced/revanced-manager/commit/37b583f560910c0b5da2a8e64de9b53f5a26bb56))
* warning for armv7 devices ([cdfb09f](https://github.com/revanced/revanced-manager/commit/cdfb09fbfa8e74d84ddcc91565489c3c5b61dfa2))
### BREAKING CHANGES
* this is to release as 1.0.0
# [1.0.0](https://github.com/revanced/revanced-manager/compare/v0.0.57...v1.0.0) (2023-04-29)
### Bug Fixes
* **i18n:** update translation for `refreshSucess` ([b286444](https://github.com/revanced/revanced-manager/commit/b286444ad93fc6009412ac14b996ff6268069811))
* open contributor links externally ([#791](https://github.com/revanced/revanced-manager/issues/791)) ([f0b0282](https://github.com/revanced/revanced-manager/commit/f0b028279c69f97817952063d84809d3e486ad6e))
* resized monochrome icon to match the original ([#789](https://github.com/revanced/revanced-manager/issues/789)) ([ac830cb](https://github.com/revanced/revanced-manager/commit/ac830cbe7f3b1ebd7849e586b829d3c077436a0d))
* update pubspec version ([65da6af](https://github.com/revanced/revanced-manager/commit/65da6af3f96550b138dcaf61832a5de23f529b32))
* chore!: merge `dev` to `main` (#830) ([782c012](https://github.com/revanced/revanced-manager/commit/782c0127b1f6f5380bd8ca23c8c2f280e2bfc432)), closes [#830](https://github.com/revanced/revanced-manager/issues/830)
### Features
* add continue anyway button to select from storage dialog ([#810](https://github.com/revanced/revanced-manager/issues/810)) ([affba66](https://github.com/revanced/revanced-manager/commit/affba669ce1ca6866a1dd1bd801e3f33e4bfe051))
* add option to import/export keystore ([#776](https://github.com/revanced/revanced-manager/issues/776)) ([dca2d4f](https://github.com/revanced/revanced-manager/commit/dca2d4fe126a6966a094d335e0f27bb62d76c5e8))
* appreciation message for new contributors ([0a1f2da](https://github.com/revanced/revanced-manager/commit/0a1f2da33da7d44f0613b19f3e6b2b7b50240548))
* auto select default patches ([4c9cb56](https://github.com/revanced/revanced-manager/commit/4c9cb560e3e38295a5140419f2565b478cb6c497))
* change `continue anyways` to `cancel` ([cfc866b](https://github.com/revanced/revanced-manager/commit/cfc866bef2497bc1675bf5dea834cea59d9cc969))
* **ci:** update crowdin workflow to use new main branch ([ded59d2](https://github.com/revanced/revanced-manager/commit/ded59d2da0d193b2dea4a5a7f2fc8eefaceecc0a))
* confirmation dialog for deleting keystore ([#764](https://github.com/revanced/revanced-manager/issues/764)) ([054afbb](https://github.com/revanced/revanced-manager/commit/054afbbedd70a1933d8241ff5b63a772f90b555f))
* disable selecting installed apps for nonroot ([bb681e3](https://github.com/revanced/revanced-manager/commit/bb681e31c9c4e8a5b7b0c883edd1bc5c28505627))
* improve suggested app version text ([#822](https://github.com/revanced/revanced-manager/issues/822)) ([ad17995](https://github.com/revanced/revanced-manager/commit/ad17995f2883682f67eb42b1f82ca865fba86ef9))
* improve ux ([#752](https://github.com/revanced/revanced-manager/issues/752)) ([3b677f8](https://github.com/revanced/revanced-manager/commit/3b677f8ae3739c079e2116417fef6ed395c2ff06)), closes [#782](https://github.com/revanced/revanced-manager/issues/782)
* progress bar for manager updates ([f5aafdb](https://github.com/revanced/revanced-manager/commit/f5aafdb7d6f51386b667abbccf7f2521ef664ba5))
* remove notice about stale development [skip ci] ([62f7a82](https://github.com/revanced/revanced-manager/commit/62f7a820d8ee2506376306e119698d427de745ef))
* resetting source to default dismiss the sources pop-up ([#797](https://github.com/revanced/revanced-manager/issues/797)) ([62ef1c8](https://github.com/revanced/revanced-manager/commit/62ef1c88fe0352d3962f8c73edff4b99ea347c0f))
* trim extra space when setting custom source ([#771](https://github.com/revanced/revanced-manager/issues/771)) ([37b583f](https://github.com/revanced/revanced-manager/commit/37b583f560910c0b5da2a8e64de9b53f5a26bb56))
* warning for armv7 devices ([cdfb09f](https://github.com/revanced/revanced-manager/commit/cdfb09fbfa8e74d84ddcc91565489c3c5b61dfa2))
### BREAKING CHANGES
* this is to release as 1.0.0

View File

@@ -2,12 +2,18 @@
The official ReVanced Manager based on Flutter. The official ReVanced Manager based on Flutter.
> **Warning**: This repository currently has no active maintainer. For that reason, development is stale. Active development continues for [revanced-cli](https://github.com/revanced/revanced-cli). If you are interested in maintaining this repository, please let us know at manager@revanced.app.
## 🔽 Download ## 🔽 Download
To download latest Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file. To download the Alpha version of Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file.
## 📝 Prerequisites ## 📝 Prerequisites
1. Android 8 or higher 1. Android 8 or higher
2. Does not work on some armv7 devices 2. Does not work on some armv7 devices
3. [Vanced MicroG](https://github.com/TeamVanced/VancedMicroG/releases) required for YouTube and YouTube Music (Only for non-root)
## ⚠️ Disclaimer
*Please note that even though we're releasing the Manager, it is an ALPHA version. There's a big chance that the Manager might not work at all for you.*
## 🔴 Issues ## 🔴 Issues
For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose). For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose).

View File

@@ -11,10 +11,7 @@ include: package:flutter_lints/flutter.yaml
analyzer: analyzer:
exclude: exclude:
- lib/app/app.locator.dart - lib/utils/env_class.g.dart
- lib/app/app.router.dart
- lib/models/patch.g.dart
- lib/models/patched_application.g.dart
linter: linter:
rules: rules:

View File

@@ -30,7 +30,6 @@ android {
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11
} }
@@ -49,7 +48,6 @@ android {
targetSdkVersion 33 targetSdkVersion 33
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true
} }
buildTypes { buildTypes {
@@ -73,7 +71,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced // ReVanced
implementation "app.revanced:revanced-patcher:7.0.0" implementation "app.revanced:revanced-patcher:6.4.3"
// Signing & aligning // Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
@@ -83,11 +81,4 @@ dependencies {
implementation("org.microg:cronet-common:$cronetVersion") implementation("org.microg:cronet-common:$cronetVersion")
implementation("org.microg:cronet-native:$cronetVersion") implementation("org.microg:cronet-native:$cronetVersion")
// Core libraries
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Window
implementation 'androidx.window:window:1.0.0'
implementation 'androidx.window:window-java:1.0.0'
} }

View File

@@ -43,9 +43,8 @@ class MainActivity : FlutterActivity() {
val integrationsPath = call.argument<String>("integrationsPath") val integrationsPath = call.argument<String>("integrationsPath")
val selectedPatches = call.argument<List<String>>("selectedPatches") val selectedPatches = call.argument<List<String>>("selectedPatches")
val cacheDirPath = call.argument<String>("cacheDirPath") val cacheDirPath = call.argument<String>("cacheDirPath")
val mergeIntegrations = call.argument<Boolean>("mergeIntegrations")
val keyStoreFilePath = call.argument<String>("keyStoreFilePath") val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
val keystorePassword = call.argument<String>("keystorePassword")
if (patchBundleFilePath != null && if (patchBundleFilePath != null &&
originalFilePath != null && originalFilePath != null &&
inputFilePath != null && inputFilePath != null &&
@@ -54,8 +53,8 @@ class MainActivity : FlutterActivity() {
integrationsPath != null && integrationsPath != null &&
selectedPatches != null && selectedPatches != null &&
cacheDirPath != null && cacheDirPath != null &&
keyStoreFilePath != null && mergeIntegrations != null &&
keystorePassword != null keyStoreFilePath != null
) { ) {
runPatcher( runPatcher(
result, result,
@@ -67,8 +66,8 @@ class MainActivity : FlutterActivity() {
integrationsPath, integrationsPath,
selectedPatches, selectedPatches,
cacheDirPath, cacheDirPath,
keyStoreFilePath, mergeIntegrations,
keystorePassword keyStoreFilePath
) )
} else { } else {
result.notImplemented() result.notImplemented()
@@ -89,8 +88,8 @@ class MainActivity : FlutterActivity() {
integrationsPath: String, integrationsPath: String,
selectedPatches: List<String>, selectedPatches: List<String>,
cacheDirPath: String, cacheDirPath: String,
keyStoreFilePath: String, mergeIntegrations: Boolean,
keystorePassword: String keyStoreFilePath: String
) { ) {
val originalFile = File(originalFilePath) val originalFile = File(originalFilePath)
val inputFile = File(inputFilePath) val inputFile = File(inputFilePath)
@@ -140,17 +139,19 @@ class MainActivity : FlutterActivity() {
mapOf("progress" to 0.3, "header" to "", "log" to "") mapOf("progress" to 0.3, "header" to "", "log" to "")
) )
} }
handler.post { if (mergeIntegrations) {
installerChannel.invokeMethod( handler.post {
"update", installerChannel.invokeMethod(
mapOf( "update",
"progress" to 0.4, mapOf(
"header" to "Merging integrations...", "progress" to 0.4,
"log" to "Merging integrations" "header" to "Merging integrations...",
"log" to "Merging integrations"
)
) )
) }
patcher.addFiles(listOf(integrations)) {}
} }
patcher.addIntegrations(listOf(integrations)) {}
handler.post { handler.post {
installerChannel.invokeMethod( installerChannel.invokeMethod(
@@ -247,7 +248,7 @@ class MainActivity : FlutterActivity() {
// Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile) // Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
try { try {
Signer("ReVanced", keystorePassword).signApk(patchedFile, outFile, keyStoreFile) Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
} catch (e: Exception) { } catch (e: Exception) {
//log to console //log to console
print("Error signing apk: ${e.message}") print("Error signing apk: ${e.message}")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/> <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1B1B1B</color>
</resources>

View File

@@ -1,6 +1,6 @@
buildscript { buildscript {
ext.cronetVersion = '102.5005.125' ext.cronetVersion = '102.5005.125'
ext.kotlin_version = '1.7.10' ext.kotlin_version = '1.8.0'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()

View File

@@ -1,6 +1,6 @@
#Mon May 09 12:07:41 MSK 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-1-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@@ -13,7 +13,6 @@
"settingsTab": "Settings" "settingsTab": "Settings"
}, },
"homeView": { "homeView": {
"refreshSuccess": "Refreshed successfully",
"widgetTitle": "Dashboard", "widgetTitle": "Dashboard",
"updatesSubtitle": "Updates", "updatesSubtitle": "Updates",
"patchedSubtitle": "Patched applications", "patchedSubtitle": "Patched applications",
@@ -48,8 +47,7 @@
"patcherView": { "patcherView": {
"widgetTitle": "Patcher", "widgetTitle": "Patcher",
"patchButton": "Patch", "patchButton": "Patch",
"patchDialogText": "You have selected a resource patch and a split APK installation has been detected, so patching errors may occur.\nAre you sure you want to proceed?", "patchDialogText": "You have selected a resource patch and a split APK installation has been detected, so patching errors may occur.\nAre you sure you want to proceed?"
"armv7WarningDialogText": "Patching on ARMv7 devices is not yet supported. Proceed anyways?"
}, },
"appSelectorCard": { "appSelectorCard": {
"widgetTitle": "Select an application", "widgetTitle": "Select an application",
@@ -57,7 +55,7 @@
"widgetSubtitle": "No application selected", "widgetSubtitle": "No application selected",
"noAppsLabel": "No applications found", "noAppsLabel": "No applications found",
"currentVersion": "Current", "currentVersion": "Current",
"suggestedVersion": "Suggested", "recommendedVersion": "Recommended",
"anyVersion": "any" "anyVersion": "any"
}, },
"patchSelectorCard": { "patchSelectorCard": {
@@ -73,25 +71,20 @@
"appSelectorView": { "appSelectorView": {
"viewTitle": "Select an application", "viewTitle": "Select an application",
"searchBarHint": "Search applications", "searchBarHint": "Search applications",
"selectFromStorageButton": "Select from storage",
"storageButton": "Storage", "storageButton": "Storage",
"errorMessage": "Unable to use selected application", "errorMessage": "Unable to use selected application"
"downloadToast": "Download function is not available yet",
"featureNotAvailable": "Feature not implemented",
"featureNotAvailableText": "This feature has not been added yet for non-root. You'll need to select APK files from storage for now."
}, },
"patchesSelectorView": { "patchesSelectorView": {
"viewTitle": "Select patches", "viewTitle": "Select patches",
"searchBarHint": "Search patches", "searchBarHint": "Search patches",
"doneButton": "Done", "doneButton": "Done",
"default": "Default", "recommended": "Recommended",
"defaultTooltip": "Select all default patches", "all": "All",
"none": "None", "none": "None",
"noneTooltip": "Deselect all patches",
"loadPatchesSelection": "Load patches selection", "loadPatchesSelection": "Load patches selection",
"noSavedPatches": "No saved patches for the selected app.\nPress Done to save current selection.", "noSavedPatches": "No saved patches for the selected app.\nPress Done to save current selection.",
"noPatchesFound": "No patches found for the selected app", "noPatchesFound": "No patches found for the selected app",
"selectAllPatchesWarningContent": "You are about to select all patches, that includes non-suggested patches and can cause unwanted behavior." "selectAllPatchesWarningContent": "You are about to select all patches, that includes unrecommended patches and can cause unwanted behavior."
}, },
"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}",
@@ -152,10 +145,11 @@
"exportSectionTitle": "Import & export", "exportSectionTitle": "Import & export",
"aboutLabel": "About", "aboutLabel": "About",
"snackbarMessage": "Copied to clipboard", "snackbarMessage": "Copied to clipboard",
"sentryLabel": "Sentry logging",
"sentryHint": "Send anonymous logs to help us improve ReVanced Manager",
"restartAppForChanges": "Restart the app to apply changes", "restartAppForChanges": "Restart the app to apply changes",
"deleteKeystoreLabel": "Delete keystore", "deleteKeystoreLabel": "Delete keystore",
"deleteKeystoreHint": "Delete the keystore used to sign the app", "deleteKeystoreHint": "Delete the keystore used to sign the app",
"deleteKeystoreDialogText": "Are you sure you want to delete the keystore used to sign patched applications?",
"deletedKeystore": "Keystore deleted", "deletedKeystore": "Keystore deleted",
"deleteTempDirLabel": "Delete temporary files", "deleteTempDirLabel": "Delete temporary files",
"deleteTempDirHint": "Delete unused temporary files", "deleteTempDirHint": "Delete unused temporary files",
@@ -173,17 +167,7 @@
"jsonSelectorErrorMessage": "Unable to use selected JSON file", "jsonSelectorErrorMessage": "Unable to use selected JSON file",
"deleteLogsLabel": "Delete logs", "deleteLogsLabel": "Delete logs",
"deleteLogsHint": "Delete collected manager logs", "deleteLogsHint": "Delete collected manager logs",
"deletedLogs": "Logs deleted", "deletedLogs": "Logs deleted"
"exportKeystoreLabel": "Export keystore",
"exportKeystoreHint": "Export keystore used to sign apps",
"exportedKeystore": "Keystore exported",
"noKeystoreExportFileFound": "No keystore to export",
"importKeystoreLabel": "Import keystore",
"importKeystoreHint": "Import keystore used to sign apps",
"importedKeystore": "Keystore imported",
"keystoreSelectorErrorMessage": "Unable to use selected KEYSTORE file",
"selectKeystorePassword": "Keystore Password",
"selectKeystorePasswordHint": "Select keystore password used to sign the apk"
}, },
"appInfoView": { "appInfoView": {
"widgetTitle": "App info", "widgetTitle": "App info",

View File

@@ -1,3 +1,4 @@
import 'package:revanced_manager/services/crowdin_api.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';
@@ -37,6 +38,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: CrowdinAPI),
LazySingleton(classType: Toast), LazySingleton(classType: Toast),
], ],
) )

View File

@@ -4,12 +4,15 @@ 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/crowdin_api.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';
import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart';
import 'package:revanced_manager/utils/environment.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked_themes/stacked_themes.dart'; import 'package:stacked_themes/stacked_themes.dart';
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
@@ -22,12 +25,36 @@ Future main() async {
await locator<ManagerAPI>().initialize(); await locator<ManagerAPI>().initialize();
final String apiUrl = locator<ManagerAPI>().getApiUrl(); final String apiUrl = locator<ManagerAPI>().getApiUrl();
await locator<RevancedAPI>().initialize(apiUrl); await locator<RevancedAPI>().initialize(apiUrl);
await locator<CrowdinAPI>().initialize();
final bool isSentryEnabled = locator<ManagerAPI>().isSentryEnabled();
final String repoUrl = locator<ManagerAPI>().getRepoUrl(); final String repoUrl = locator<ManagerAPI>().getRepoUrl();
locator<GithubAPI>().initialize(repoUrl); locator<GithubAPI>().initialize(repoUrl);
await locator<PatcherAPI>().initialize(); await locator<PatcherAPI>().initialize();
tz.initializeTimeZones(); tz.initializeTimeZones();
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
await SentryFlutter.init(
(options) {
options
..dsn = isSentryEnabled ? Environment.sentryDSN : ''
..environment = 'alpha'
..release = '0.1'
..tracesSampleRate = 1.0
..anrEnabled = true
..enableOutOfMemoryTracking = true
..sampleRate = isSentryEnabled ? 1.0 : 0.0
..beforeSend = (event, hint) {
if (isSentryEnabled) {
return event;
} else {
return null;
}
} as BeforeSendCallback?;
},
appRunner: () {
runApp(const MyApp());
},
);
runApp(const MyApp()); runApp(const MyApp());
} }

View File

@@ -0,0 +1,63 @@
import 'package:dio/dio.dart';
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
import 'package:injectable/injectable.dart' hide Environment;
import 'package:revanced_manager/utils/environment.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@lazySingleton
class CrowdinAPI {
late Dio _dio = Dio();
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
final apiKey = Environment.crowdinKEY;
Future<void> initialize() async {
try {
_dio = Dio(
BaseOptions(
baseUrl: 'https://api.crowdin.com/api/v2',
),
);
_dio.interceptors.add(_dioCacheManager.interceptor);
_dio.addSentry(
captureFailedRequests: true,
);
} on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
}
}
Future<void> clearAllCache() async {
try {
await _dioCacheManager.clearAll();
} on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
}
}
Future<List> getLanguages() async {
try {
final response = await _dio.get(
'/projects',
options: buildCacheOptions(
const Duration(hours: 6),
maxStale: const Duration(days: 1),
options: Options(
headers: {
'Authorization': 'Bearer $apiKey',
},
contentType: 'application/json',
),
),
);
final List targetLanguages =
await response.data['data'][0]['data']['targetLanguages'];
return targetLanguages;
} on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
return [];
}
}
}

View File

@@ -3,23 +3,21 @@ 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:dio_http_cache_lts/dio_http_cache_lts.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:native_dio_adapter/native_dio_adapter.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@lazySingleton @lazySingleton
class GithubAPI { class GithubAPI {
late Dio _dio = Dio(); late Dio _dio = Dio();
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
final _cacheOptions = CacheOptions( final Options _cacheOptions = buildCacheOptions(
store: MemCacheStore(), const Duration(hours: 6),
maxStale: const Duration(days: 1), maxStale: const Duration(days: 1),
priority: CachePriority.high,
); );
final Map<String, String> repoAppPath = { final Map<String, String> repoAppPath = {
'com.google.android.youtube': 'youtube', 'com.google.android.youtube': 'youtube',
'com.google.android.apps.youtube.music': 'music', 'com.google.android.apps.youtube.music': 'music',
@@ -33,58 +31,38 @@ class GithubAPI {
Future<void> initialize(String repoUrl) async { Future<void> initialize(String repoUrl) async {
try { try {
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) {
final CronetEngine androidCronetEngine = await CronetEngine.build(
userAgent: 'ReVanced Manager',
enableBrotli: true,
enableQuic: true,
);
_dio.httpClientAdapter =
NativeAdapter(androidCronetEngine: androidCronetEngine);
_dio = Dio(
BaseOptions(
baseUrl: repoUrl,
),
);
}
_dio = Dio( _dio = Dio(
BaseOptions( BaseOptions(
baseUrl: repoUrl, baseUrl: repoUrl,
), ),
); );
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions)); _dio.interceptors.add(_dioCacheManager.interceptor);
} on Exception catch (e) { _dio.addSentry(
if (kDebugMode) { captureFailedRequests: true,
print(e); );
} } on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
} }
} }
Future<void> clearAllCache() async { Future<void> clearAllCache() async {
try { try {
await _cacheOptions.store!.clean(); await _dioCacheManager.clearAll();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
Future<Map<String, dynamic>?> getLatestRelease( Future<Map<String, dynamic>?> getLatestRelease(String repoName) async {
String repoName,
) async {
try { try {
final response = await _dio.get( final response = await _dio.get(
'/repos/$repoName/releases', '/repos/$repoName/releases',
options: _cacheOptions,
); );
return response.data[0]; return response.data[0];
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
} }
@@ -103,6 +81,7 @@ class GithubAPI {
'path': path, 'path': path,
'since': since.toIso8601String(), 'since': since.toIso8601String(),
}, },
options: _cacheOptions,
); );
final List<dynamic> commits = response.data; final List<dynamic> commits = response.data;
return commits return commits
@@ -113,21 +92,15 @@ class GithubAPI {
'\n' as String, '\n' as String,
) )
.toList(); .toList();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); return List.empty();
}
} }
return [];
} }
Future<File?> getLatestReleaseFile( Future<File?> getLatestReleaseFile(String extension, String repoName) async {
String extension,
String repoName,
) async {
try { try {
final Map<String, dynamic>? release = final Map<String, dynamic>? release = await getLatestRelease(repoName);
await getLatestRelease(repoName);
if (release != null) { if (release != null) {
final Map<String, dynamic>? asset = final Map<String, dynamic>? asset =
(release['assets'] as List<dynamic>).firstWhereOrNull( (release['assets'] as List<dynamic>).firstWhereOrNull(
@@ -139,10 +112,9 @@ class GithubAPI {
); );
} }
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); return null;
}
} }
return null; return null;
} }
@@ -155,30 +127,24 @@ class GithubAPI {
final List<dynamic> list = jsonDecode(f.readAsStringSync()); final List<dynamic> list = jsonDecode(f.readAsStringSync());
patches = list.map((patch) => Patch.fromJson(patch)).toList(); patches = list.map((patch) => Patch.fromJson(patch)).toList();
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); return List.empty();
}
} }
return patches; return patches;
} }
Future<String> getLastestReleaseVersion(String repoName) async { Future<String> getLastestReleaseVersion(String repoName) async {
try { try {
final Map<String, dynamic>? release = final Map<String, dynamic>? release = await getLatestRelease(repoName);
await getLatestRelease(repoName);
if (release != null) { if (release != null) {
return release['tag_name']; return release['tag_name'];
} else { } else {
return 'Unknown'; return 'Unknown';
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); return '';
}
return 'Unknown';
} }
} }
} }

View File

@@ -1,7 +1,6 @@
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:flutter/foundation.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@@ -11,7 +10,7 @@ import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@lazySingleton @lazySingleton
@@ -22,11 +21,7 @@ class ManagerAPI {
final String patcherRepo = 'revanced-patcher'; final String patcherRepo = 'revanced-patcher';
final String cliRepo = 'revanced-cli'; final String cliRepo = 'revanced-cli';
late SharedPreferences _prefs; late SharedPreferences _prefs;
bool isRooted = false;
String storedPatchesFile = '/selected-patches.json'; String storedPatchesFile = '/selected-patches.json';
String keystoreFile =
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore';
String defaultKeystorePassword = 's3cur3p@ssw0rd';
String defaultApiUrl = 'https://releases.revanced.app/'; String defaultApiUrl = 'https://releases.revanced.app/';
String defaultRepoUrl = 'https://api.github.com'; String defaultRepoUrl = 'https://api.github.com';
String defaultPatcherRepo = 'revanced/revanced-patcher'; String defaultPatcherRepo = 'revanced/revanced-patcher';
@@ -34,14 +29,9 @@ class ManagerAPI {
String defaultIntegrationsRepo = 'revanced/revanced-integrations'; String defaultIntegrationsRepo = 'revanced/revanced-integrations';
String defaultCliRepo = 'revanced/revanced-cli'; String defaultCliRepo = 'revanced/revanced-cli';
String defaultManagerRepo = 'revanced/revanced-manager'; String defaultManagerRepo = 'revanced/revanced-manager';
String? patchesVersion = '';
bool isDefaultPatchesRepo() {
return getPatchesRepo() == 'revanced/revanced-patches';
}
Future<void> initialize() async { Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
isRooted = await _rootAPI.isRooted();
storedPatchesFile = storedPatchesFile =
(await getApplicationDocumentsDirectory()).path + storedPatchesFile; (await getApplicationDocumentsDirectory()).path + storedPatchesFile;
} }
@@ -108,6 +98,14 @@ class ManagerAPI {
await _prefs.setBool('useDarkTheme', value); await _prefs.setBool('useDarkTheme', value);
} }
bool isSentryEnabled() {
return _prefs.getBool('sentryEnabled') ?? true;
}
Future<void> setSentryStatus(bool value) async {
await _prefs.setBool('sentryEnabled', value);
}
bool areUniversalPatchesEnabled() { bool areUniversalPatchesEnabled() {
return _prefs.getBool('universalPatchesEnabled') ?? false; return _prefs.getBool('universalPatchesEnabled') ?? false;
} }
@@ -124,14 +122,6 @@ class ManagerAPI {
await _prefs.setBool('experimentalPatchesEnabled', value); await _prefs.setBool('experimentalPatchesEnabled', value);
} }
Future<void> setKeystorePassword(String password) async {
await _prefs.setString('keystorePassword', password);
}
String getKeystorePassword() {
return _prefs.getString('keystorePassword') ?? defaultKeystorePassword;
}
Future<void> deleteTempFolder() async { Future<void> deleteTempFolder() async {
final Directory dir = Directory('/data/local/tmp/revanced-manager'); final Directory dir = Directory('/data/local/tmp/revanced-manager');
if (await dir.exists()) { if (await dir.exists()) {
@@ -141,7 +131,7 @@ class ManagerAPI {
Future<void> deleteKeystore() async { Future<void> deleteKeystore() async {
final File keystore = File( final File keystore = File(
keystoreFile, '/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore',
); );
if (await keystore.exists()) { if (await keystore.exists()) {
await keystore.delete(); await keystore.delete();
@@ -153,9 +143,7 @@ class ManagerAPI {
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList(); return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
} }
Future<void> setPatchedApps( Future<void> setPatchedApps(List<PatchedApplication> patchedApps) async {
List<PatchedApplication> patchedApps,
) async {
if (patchedApps.length > 1) { if (patchedApps.length > 1) {
patchedApps.sort((a, b) => a.name.compareTo(b.name)); patchedApps.sort((a, b) => a.name.compareTo(b.name));
} }
@@ -191,10 +179,8 @@ class ManagerAPI {
try { try {
_revancedAPI.clearAllCache(); _revancedAPI.clearAllCache();
_githubAPI.clearAllCache(); _githubAPI.clearAllCache();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
@@ -210,10 +196,8 @@ class ManagerAPI {
} else { } else {
return await _githubAPI.getPatches(repoName); return await _githubAPI.getPatches(repoName);
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return []; return [];
} }
} }
@@ -229,10 +213,8 @@ class ManagerAPI {
} else { } else {
return await _githubAPI.getLatestReleaseFile('.jar', repoName); return await _githubAPI.getLatestReleaseFile('.jar', repoName);
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
} }
@@ -248,33 +230,22 @@ class ManagerAPI {
} else { } else {
return await _githubAPI.getLatestReleaseFile('.apk', repoName); return await _githubAPI.getLatestReleaseFile('.apk', repoName);
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
} }
Future<File?> downloadManager() async { Future<File?> downloadManager() async {
return await _revancedAPI.getLatestReleaseFile( return await _revancedAPI.getLatestReleaseFile('.apk', defaultManagerRepo);
'.apk',
defaultManagerRepo,
);
} }
Future<String?> getLatestPatcherReleaseTime() async { Future<String?> getLatestPatcherReleaseTime() async {
return await _revancedAPI.getLatestReleaseTime( return await _revancedAPI.getLatestReleaseTime('.gz', defaultPatcherRepo);
'.gz',
defaultPatcherRepo,
);
} }
Future<String?> getLatestManagerReleaseTime() async { Future<String?> getLatestManagerReleaseTime() async {
return await _revancedAPI.getLatestReleaseTime( return await _revancedAPI.getLatestReleaseTime('.apk', defaultManagerRepo);
'.apk',
defaultManagerRepo,
);
} }
Future<String?> getLatestManagerVersion() async { Future<String?> getLatestManagerVersion() async {
@@ -296,19 +267,6 @@ class ManagerAPI {
return packageInfo.version; return packageInfo.version;
} }
Future<String?> getCurrentPatchesVersion() async {
if (isDefaultPatchesRepo()) {
patchesVersion = await getLatestPatchesVersion();
// print('Patches version: $patchesVersion');
return patchesVersion ?? '0.0.0';
} else {
// fetch from github
patchesVersion =
await _githubAPI.getLastestReleaseVersion(getPatchesRepo());
}
return null;
}
Future<List<PatchedApplication>> getAppsToRemove( Future<List<PatchedApplication>> getAppsToRemove(
List<PatchedApplication> patchedApps, List<PatchedApplication> patchedApps,
) async { ) async {
@@ -401,9 +359,8 @@ class ManagerAPI {
final int currentInstalledVersionInt = int.parse( final int currentInstalledVersionInt = int.parse(
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''), currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''),
); );
final int currentSavedVersionInt = int.parse( final int currentSavedVersionInt =
currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''), int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
);
if (currentInstalledVersionInt > currentSavedVersionInt) { if (currentInstalledVersionInt > currentSavedVersionInt) {
app.hasUpdates = true; app.hasUpdates = true;
} }
@@ -426,10 +383,7 @@ class ManagerAPI {
return !existsNonRoot; return !existsNonRoot;
} }
Future<bool> hasAppUpdates( Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
String packageName,
DateTime patchDate,
) async {
final List<String> commits = await _githubAPI.getCommits( final List<String> commits = await _githubAPI.getCommits(
packageName, packageName,
getPatchesRepo(), getPatchesRepo(),
@@ -467,10 +421,7 @@ class ManagerAPI {
return app != null && app.isSplit; return app != null && app.isSplit;
} }
Future<void> setSelectedPatches( Future<void> setSelectedPatches(String app, List<String> patches) async {
String app,
List<String> patches,
) async {
final File selectedPatchesFile = File(storedPatchesFile); final File selectedPatchesFile = File(storedPatchesFile);
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile(); final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
if (patches.isEmpty) { if (patches.isEmpty) {
@@ -481,33 +432,9 @@ class ManagerAPI {
selectedPatchesFile.writeAsString(jsonEncode(patchesMap)); selectedPatchesFile.writeAsString(jsonEncode(patchesMap));
} }
// get default patches for app
Future<List<String>> getDefaultPatches() async {
final List<Patch> patches = await getPatches();
final List<String> defaultPatches = [];
if (areExperimentalPatchesEnabled() == false) {
defaultPatches.addAll(
patches
.where(
(element) =>
element.excluded == false && isPatchSupported(element),
)
.map((p) => p.name),
);
} else {
defaultPatches.addAll(
patches
.where((element) => isPatchSupported(element))
.map((p) => p.name),
);
}
return defaultPatches;
}
Future<List<String>> getSelectedPatches(String app) async { Future<List<String>> getSelectedPatches(String app) async {
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile(); final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
final List<String> defaultPatches = await getDefaultPatches(); return List.from(patchesMap.putIfAbsent(app, () => List.empty()));
return List.from(patchesMap.putIfAbsent(app, () => defaultPatches));
} }
Future<Map<String, dynamic>> readSelectedPatchesFile() async { Future<Map<String, dynamic>> readSelectedPatchesFile() async {

View File

@@ -13,6 +13,7 @@ import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/services/root_api.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:share_extend/share_extend.dart'; import 'package:share_extend/share_extend.dart';
@lazySingleton @lazySingleton
@@ -48,10 +49,8 @@ class PatcherAPI {
if (_patches.isEmpty) { if (_patches.isEmpty) {
_patches = await _managerAPI.getPatches(); _patches = await _managerAPI.getPatches();
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
_patches = List.empty(); _patches = List.empty();
} }
} }
@@ -92,10 +91,9 @@ class PatcherAPI {
filteredApps.add(app); filteredApps.add(app);
} }
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); continue;
}
} }
} }
} }
@@ -118,17 +116,32 @@ class PatcherAPI {
return filteredPatches[packageName]; return filteredPatches[packageName];
} }
Future<List<Patch>> getAppliedPatches( Future<List<Patch>> getAppliedPatches(List<String> appliedPatches) async {
List<String> appliedPatches,
) async {
return _patches return _patches
.where((patch) => appliedPatches.contains(patch.name)) .where((patch) => appliedPatches.contains(patch.name))
.toList(); .toList();
} }
Future<bool> needsResourcePatching( bool dependencyNeedsIntegrations(String name) {
List<Patch> selectedPatches, return name.contains('integrations') ||
) async { _patches.any(
(patch) =>
patch.name == name &&
(patch.dependencies.any(
(dep) => dependencyNeedsIntegrations(dep),
)),
);
}
Future<bool> needsIntegrations(List<Patch> selectedPatches) async {
return selectedPatches.any(
(patch) => patch.dependencies.any(
(dep) => dependencyNeedsIntegrations(dep),
),
);
}
Future<bool> needsResourcePatching(List<Patch> selectedPatches) async {
return selectedPatches.any( return selectedPatches.any(
(patch) => patch.dependencies.any( (patch) => patch.dependencies.any(
(dep) => dep.contains('resource-'), (dep) => dep.contains('resource-'),
@@ -157,10 +170,8 @@ class PatcherAPI {
); );
} }
return originalFilePath; return originalFilePath;
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return originalFilePath; return originalFilePath;
} }
} }
@@ -170,6 +181,7 @@ class PatcherAPI {
String originalFilePath, String originalFilePath,
List<Patch> selectedPatches, List<Patch> selectedPatches,
) async { ) async {
final bool mergeIntegrations = await needsIntegrations(selectedPatches);
final bool includeSettings = await needsSettingsPatch(selectedPatches); final bool includeSettings = await needsSettingsPatch(selectedPatches);
if (includeSettings) { if (includeSettings) {
try { try {
@@ -181,14 +193,16 @@ class PatcherAPI {
if (settingsPatch != null) { if (settingsPatch != null) {
selectedPatches.add(settingsPatch); selectedPatches.add(settingsPatch);
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); // ignore
}
} }
} }
final File? patchBundleFile = await _managerAPI.downloadPatches(); final File? patchBundleFile = await _managerAPI.downloadPatches();
final File? integrationsFile = await _managerAPI.downloadIntegrations(); File? integrationsFile;
if (mergeIntegrations) {
integrationsFile = await _managerAPI.downloadIntegrations();
}
if (patchBundleFile != null) { if (patchBundleFile != null) {
_dataDir.createSync(); _dataDir.createSync();
_tmpDir.createSync(); _tmpDir.createSync();
@@ -210,17 +224,18 @@ class PatcherAPI {
'inputFilePath': inputFile.path, 'inputFilePath': inputFile.path,
'patchedFilePath': patchedFile.path, 'patchedFilePath': patchedFile.path,
'outFilePath': _outFile!.path, 'outFilePath': _outFile!.path,
'integrationsPath': integrationsFile!.path, 'integrationsPath': mergeIntegrations ? integrationsFile!.path : '',
'selectedPatches': selectedPatches.map((p) => p.name).toList(), 'selectedPatches': selectedPatches.map((p) => p.name).toList(),
'cacheDirPath': cacheDir.path, 'cacheDirPath': cacheDir.path,
'mergeIntegrations': mergeIntegrations,
'keyStoreFilePath': _keyStoreFile.path, 'keyStoreFilePath': _keyStoreFile.path,
'keystorePassword': _managerAPI.getKeystorePassword(),
}, },
); );
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { if (kDebugMode) {
print(e); print(e);
} }
throw await Sentry.captureException(e, stackTrace: s);
} }
} }
} }
@@ -239,14 +254,10 @@ class PatcherAPI {
} }
} else { } else {
await AppInstaller.installApk(_outFile!.path); await AppInstaller.installApk(_outFile!.path);
return await DeviceApps.isAppInstalled( return await DeviceApps.isAppInstalled(patchedApp.packageName);
patchedApp.packageName,
);
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
} }
} on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
return false; return false;
} }
} }
@@ -257,17 +268,11 @@ class PatcherAPI {
try { try {
if (_outFile != null) { if (_outFile != null) {
final String newName = _getFileName(appName, version); final String newName = _getFileName(appName, version);
CRFileSaver.saveFileWithDialog( CRFileSaver.saveFileWithDialog(SaveFileDialogParams(
SaveFileDialogParams( sourceFilePath: _outFile!.path, destinationFileName: newName,),);
sourceFilePath: _outFile!.path,
destinationFileName: newName,
),
);
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
} }
} on Exception catch (e, s) {
Sentry.captureException(e, stackTrace: s);
} }
} }
@@ -281,10 +286,8 @@ class PatcherAPI {
final File shareFile = _outFile!.copySync(newPath); final File shareFile = _outFile!.copySync(newPath);
ShareExtend.share(shareFile.path, 'file'); ShareExtend.share(shareFile.path, 'file');
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
@@ -310,7 +313,7 @@ class PatcherAPI {
ShareExtend.share(log.path, 'file'); ShareExtend.share(log.path, 'file');
} }
String getSuggestedVersion(String packageName) { String getRecommendedVersion(String packageName) {
final Map<String, int> versions = {}; final Map<String, int> versions = {};
for (final Patch patch in _patches) { for (final Patch patch in _patches) {
final Package? package = patch.compatiblePackages.firstWhereOrNull( final Package? package = patch.compatiblePackages.firstWhereOrNull(

View File

@@ -1,26 +1,25 @@
import 'dart:async';
import 'dart:developer'; import 'dart:developer';
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:dio_http_cache_lts/dio_http_cache_lts.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:native_dio_adapter/native_dio_adapter.dart'; import 'package:native_dio_client/native_dio_client.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/utils/check_for_gms.dart'; import 'package:revanced_manager/utils/check_for_gms.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:timeago/timeago.dart'; import 'package:timeago/timeago.dart';
@lazySingleton @lazySingleton
class RevancedAPI { class RevancedAPI {
late Dio _dio = Dio(); late Dio _dio = Dio();
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
final _cacheOptions = CacheOptions( final Options _cacheOptions = buildCacheOptions(
store: MemCacheStore(), const Duration(hours: 6),
maxStale: const Duration(days: 1), maxStale: const Duration(days: 1),
priority: CachePriority.high,
); );
Future<void> initialize(String apiUrl) async { Future<void> initialize(String apiUrl) async {
@@ -35,55 +34,41 @@ class RevancedAPI {
); );
log('ReVanced API: Using default engine + $isGMSInstalled'); log('ReVanced API: Using default engine + $isGMSInstalled');
} else { } else {
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) { _dio = Dio(
final CronetEngine androidCronetEngine = await CronetEngine.build( BaseOptions(
userAgent: 'ReVanced Manager', baseUrl: apiUrl,
enableBrotli: true, ),
enableQuic: true, )..httpClientAdapter = NativeAdapter();
);
_dio.httpClientAdapter =
NativeAdapter(androidCronetEngine: androidCronetEngine);
_dio = Dio(
BaseOptions(
baseUrl: apiUrl,
),
);
}
log('ReVanced API: Using CronetEngine + $isGMSInstalled'); log('ReVanced API: Using CronetEngine + $isGMSInstalled');
} }
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions)); _dio.interceptors.add(_dioCacheManager.interceptor);
} on Exception catch (e) { _dio.addSentry(
if (kDebugMode) { captureFailedRequests: true,
print(e); );
} } on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s);
} }
} }
Future<void> clearAllCache() async { Future<void> clearAllCache() async {
try { try {
await _cacheOptions.store!.clean(); await _dioCacheManager.clearAll();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
Future<Map<String, List<dynamic>>> getContributors() async { Future<Map<String, List<dynamic>>> getContributors() async {
final Map<String, List<dynamic>> contributors = {}; final Map<String, List<dynamic>> contributors = {};
try { try {
final response = await _dio.get('/contributors'); final response = await _dio.get('/contributors', options: _cacheOptions);
final List<dynamic> repositories = response.data['repositories']; final List<dynamic> repositories = response.data['repositories'];
for (final Map<String, dynamic> repo in repositories) { for (final Map<String, dynamic> repo in repositories) {
final String name = repo['name']; final String name = repo['name'];
contributors[name] = repo['contributors']; contributors[name] = repo['contributors'];
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return {}; return {};
} }
return contributors; return contributors;
@@ -91,13 +76,11 @@ class RevancedAPI {
Future<List<Patch>> getPatches() async { Future<List<Patch>> getPatches() async {
try { try {
final response = await _dio.get('/patches'); final response = await _dio.get('/patches', options: _cacheOptions);
final List<dynamic> patches = response.data; final List<dynamic> patches = response.data;
return patches.map((patch) => Patch.fromJson(patch)).toList(); return patches.map((patch) => Patch.fromJson(patch)).toList();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return List.empty(); return List.empty();
} }
} }
@@ -107,17 +90,15 @@ class RevancedAPI {
String repoName, String repoName,
) async { ) async {
try { try {
final response = await _dio.get('/tools'); final response = await _dio.get('/tools', options: _cacheOptions);
final List<dynamic> tools = response.data['tools']; final List<dynamic> tools = response.data['tools'];
return tools.firstWhereOrNull( return tools.firstWhereOrNull(
(t) => (t) =>
t['repository'] == repoName && t['repository'] == repoName &&
(t['name'] as String).endsWith(extension), (t['name'] as String).endsWith(extension),
); );
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
} }
@@ -134,19 +115,14 @@ class RevancedAPI {
if (release != null) { if (release != null) {
return release['version']; return release['version'];
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
return null; return null;
} }
Future<File?> getLatestReleaseFile( Future<File?> getLatestReleaseFile(String extension, String repoName) async {
String extension,
String repoName,
) async {
try { try {
final Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, extension,
@@ -156,52 +132,13 @@ class RevancedAPI {
final String url = release['browser_download_url']; final String url = release['browser_download_url'];
return await DefaultCacheManager().getSingleFile(url); return await DefaultCacheManager().getSingleFile(url);
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
return null; return null;
} }
StreamController<double> managerUpdateProgress = StreamController<double>();
void updateManagerDownloadProgress(int progress) {
managerUpdateProgress.add(progress.toDouble());
}
Stream<double> getManagerUpdateProgress() {
return managerUpdateProgress.stream;
}
void disposeManagerUpdateProgress() {
managerUpdateProgress.close();
}
Future<File?> downloadManager() async {
final Map<String, dynamic>? release = await _getLatestRelease(
'.apk',
'revanced/revanced-manager',
);
File? outputFile;
await for (final result in DefaultCacheManager().getFileStream(
release!['browser_download_url'] as String,
withProgress: true,
)) {
if (result is DownloadProgress) {
final totalSize = result.totalSize ?? 10000000;
final progress = (result.downloaded / totalSize * 100).round();
updateManagerDownloadProgress(progress);
} else if (result is FileInfo) {
// The download is complete; convert the FileInfo object to a File object
outputFile = File(result.file.path);
}
}
return outputFile;
}
Future<String?> getLatestReleaseTime( Future<String?> getLatestReleaseTime(
String extension, String extension,
String repoName, String repoName,
@@ -216,10 +153,8 @@ class RevancedAPI {
DateTime.parse(release['timestamp'] as String); DateTime.parse(release['timestamp'] as String);
return format(timestamp, locale: 'en_short'); return format(timestamp, locale: 'en_short');
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return null; return null;
} }
return null; return null;

View File

@@ -1,5 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:root/root.dart'; import 'package:root/root.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
class RootAPI { class RootAPI {
final String _managerDirPath = '/data/local/tmp/revanced-manager'; final String _managerDirPath = '/data/local/tmp/revanced-manager';
@@ -10,10 +10,8 @@ class RootAPI {
try { try {
final bool? isRooted = await Root.isRootAvailable(); final bool? isRooted = await Root.isRootAvailable();
return isRooted != null && isRooted; return isRooted != null && isRooted;
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return false; return false;
} }
} }
@@ -26,10 +24,8 @@ class RootAPI {
return isRooted != null && isRooted; return isRooted != null && isRooted;
} }
return false; return false;
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return false; return false;
} }
} }
@@ -82,10 +78,8 @@ class RootAPI {
apps.removeWhere((pack) => pack.isEmpty); apps.removeWhere((pack) => pack.isEmpty);
return apps.map((pack) => pack.trim()).toList(); return apps.map((pack) => pack.trim()).toList();
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return List.empty(); return List.empty();
} }
return List.empty(); return List.empty();
@@ -131,10 +125,8 @@ class RootAPI {
await installApk(packageName, patchedFilePath); await installApk(packageName, patchedFilePath);
await mountApk(packageName, originalFilePath); await mountApk(packageName, originalFilePath);
return true; return true;
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return false; return false;
} }
} }

View File

@@ -12,7 +12,7 @@ var lightCustomTheme = ThemeData(
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
TextStyle( TextStyle(
color: lightCustomColorScheme.onSurface, color: lightCustomColorScheme.secondary,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
@@ -33,12 +33,13 @@ var darkCustomTheme = ThemeData(
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
TextStyle( TextStyle(
color: darkCustomColorScheme.onSurface, color: darkCustomColorScheme.secondary,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
), ),
canvasColor: const Color(0xff1B1A1D), canvasColor: const Color(0xff1B1A1D),
scaffoldBackgroundColor: const Color(0xff1B1A1D), scaffoldBackgroundColor: const Color(0xff1B1A1D),
toggleableActiveColor: const Color(0xffA5CAFF),
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme), textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
); );

View File

@@ -23,28 +23,48 @@ class DynamicThemeBuilder extends StatelessWidget {
builder: (lightColorScheme, darkColorScheme) { builder: (lightColorScheme, darkColorScheme) {
final ThemeData lightDynamicTheme = ThemeData( final ThemeData lightDynamicTheme = ThemeData(
useMaterial3: true, useMaterial3: true,
canvasColor: lightColorScheme?.surface,
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
backgroundColor: lightColorScheme?.surface,
indicatorColor: lightColorScheme?.secondaryContainer,
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
GoogleFonts.roboto( GoogleFonts.roboto(
color: lightColorScheme?.onSurface, color: lightColorScheme?.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
iconTheme: MaterialStateProperty.all(
IconThemeData(
color: lightColorScheme?.onSecondaryContainer,
),
),
), ),
scaffoldBackgroundColor: lightColorScheme?.surface,
colorScheme: lightColorScheme?.harmonized(), colorScheme: lightColorScheme?.harmonized(),
toggleableActiveColor: lightColorScheme?.primary,
textTheme: GoogleFonts.robotoTextTheme(ThemeData.light().textTheme), textTheme: GoogleFonts.robotoTextTheme(ThemeData.light().textTheme),
); );
final ThemeData darkDynamicTheme = ThemeData( final ThemeData darkDynamicTheme = ThemeData(
useMaterial3: true, useMaterial3: true,
canvasColor: darkColorScheme?.surface,
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
backgroundColor: darkColorScheme?.surface,
indicatorColor: darkColorScheme?.secondaryContainer,
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
GoogleFonts.roboto( GoogleFonts.roboto(
color: darkColorScheme?.onSurface, color: darkColorScheme?.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
iconTheme: MaterialStateProperty.all(
IconThemeData(
color: darkColorScheme?.onSecondaryContainer,
),
),
), ),
scaffoldBackgroundColor: darkColorScheme?.surface,
colorScheme: darkColorScheme?.harmonized(), colorScheme: darkColorScheme?.harmonized(),
toggleableActiveColor: darkColorScheme?.primary,
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme), textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
); );
return DynamicTheme( return DynamicTheme(

View File

@@ -3,7 +3,6 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/not_installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart'; import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
import 'package:stacked/stacked.dart' hide SkeletonLoader; import 'package:stacked/stacked.dart' hide SkeletonLoader;
@@ -42,14 +41,14 @@ class _AppSelectorViewState extends State<AppSelectorView> {
child: Text( child: Text(
'', '',
style: TextStyle( style: TextStyle(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back, Icons.arrow_back,
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
@@ -77,16 +76,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
SliverToBoxAdapter( SliverToBoxAdapter(
child: model.noApps child: model.noApps
? Center( ? Center(
child: I18nText( child: I18nText('appSelectorCard.noAppsLabel'),
'appSelectorView.noApps',
child: Text(
'',
style: TextStyle(
color:
Theme.of(context).textTheme.titleLarge!.color,
),
),
),
) )
: model.apps.isEmpty : model.apps.isEmpty
? const AppSkeletonLoader() ? const AppSkeletonLoader()
@@ -94,48 +84,22 @@ class _AppSelectorViewState extends State<AppSelectorView> {
padding: const EdgeInsets.symmetric(horizontal: 12.0) padding: const EdgeInsets.symmetric(horizontal: 12.0)
.copyWith(bottom: 80), .copyWith(bottom: 80),
child: Column( child: Column(
children: [ children: model
...model .getFilteredApps(_query)
.getFilteredApps(_query) .map(
.map( (app) => InstalledAppItem(
(app) => InstalledAppItem( name: app.appName,
name: app.appName, pkgName: app.packageName,
pkgName: app.packageName, icon: app.icon,
icon: app.icon, patchesCount:
patchesCount: model.patchesCount(app.packageName),
model.patchesCount(app.packageName), onTap: () {
suggestedVersion: model.selectApp(app);
model.getSuggestedVersion( Navigator.of(context).pop();
app.packageName, },
), ),
onTap: () { )
model.isRooted .toList(),
? model.selectApp(app).then(
(_) => Navigator.of(context)
.pop(),
)
: model.showSelectFromStorageDialog(
context,
);
},
),
)
.toList(),
...model
.getFilteredAppsNames(_query)
.map(
(app) => NotInstalledAppItem(
name: app,
patchesCount: model.patchesCount(app),
suggestedVersion:
model.getSuggestedVersion(app),
onTap: () {
model.showDownloadToast();
},
),
)
.toList(),
],
), ),
), ),
), ),

View File

@@ -2,39 +2,27 @@ import 'dart:io';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart'; 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/models/patched_application.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';
import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/toast.dart'; 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/widgets/shared/custom_material_button.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class AppSelectorViewModel extends BaseViewModel { class AppSelectorViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
final List<ApplicationWithIcon> apps = []; final List<ApplicationWithIcon> apps = [];
List<String> allApps = [];
bool noApps = false; bool noApps = false;
bool isRooted = false;
int patchesCount(String packageName) { int patchesCount(String packageName) {
return _patcherAPI.getFilteredPatches(packageName).length; return _patcherAPI.getFilteredPatches(packageName).length;
} }
List<Patch> patches = [];
Future<void> initialize() async { Future<void> initialize() async {
patches = await _revancedAPI.getPatches();
isRooted = _managerAPI.isRooted;
apps.addAll( apps.addAll(
await _patcherAPI await _patcherAPI
.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()), .getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
@@ -46,25 +34,9 @@ class AppSelectorViewModel extends BaseViewModel {
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length), .compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
); );
noApps = apps.isEmpty; noApps = apps.isEmpty;
getAllApps();
notifyListeners(); notifyListeners();
} }
List<String> getAllApps() {
allApps = patches
.expand((e) => e.compatiblePackages.map((p) => p.name))
.toSet()
.where((name) => !apps.any((app) => app.packageName == name))
.toList();
return allApps;
}
String getSuggestedVersion(String packageName) {
return _patcherAPI.getSuggestedVersion(packageName);
}
Future<void> selectApp(ApplicationWithIcon application) async { Future<void> selectApp(ApplicationWithIcon application) async {
locator<PatcherViewModel>().selectedApp = PatchedApplication( locator<PatcherViewModel>().selectedApp = PatchedApplication(
name: application.appName, name: application.appName,
@@ -78,80 +50,6 @@ class AppSelectorViewModel extends BaseViewModel {
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
} }
Future showSelectFromStorageDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) => SimpleDialog(
alignment: Alignment.center,
contentPadding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
children: [
const SizedBox(height: 10),
Icon(
Icons.block,
size: 28,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 20),
I18nText(
'appSelectorView.featureNotAvailable',
child: const Text(
'',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
wordSpacing: 1.5,
),
),
),
const SizedBox(height: 20),
I18nText(
'appSelectorView.featureNotAvailableText',
child: const Text(
'',
style: TextStyle(
fontSize: 14,
),
),
),
const SizedBox(height: 30),
CustomMaterialButton(
onPressed: () => selectAppFromStorage(context).then(
(_) {
Navigator.pop(context);
Navigator.pop(context);
},
),
label: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.sd_card),
const SizedBox(width: 10),
I18nText('appSelectorView.selectFromStorageButton'),
],
),
),
const SizedBox(height: 10),
CustomMaterialButton(
isFilled: false,
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
label: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 10),
I18nText('cancelButton'),
],
),
),
],
),
);
}
Future<void> selectAppFromStorage(BuildContext context) async { Future<void> selectAppFromStorage(BuildContext context) async {
try { try {
final FilePickerResult? result = await FilePicker.platform.pickFiles( final FilePickerResult? result = await FilePicker.platform.pickFiles(
@@ -189,10 +87,8 @@ class AppSelectorViewModel extends BaseViewModel {
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
} }
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
_toast.showBottom('appSelectorView.errorMessage'); _toast.showBottom('appSelectorView.errorMessage');
} }
} }
@@ -207,18 +103,4 @@ class AppSelectorViewModel extends BaseViewModel {
) )
.toList(); .toList();
} }
List<String> getFilteredAppsNames(String query) {
return allApps
.where(
(app) =>
query.isEmpty ||
query.length < 2 ||
app.toLowerCase().contains(query.toLowerCase()),
)
.toList();
}
void showDownloadToast() =>
_toast.showBottom('appSelectorView.downloadToast');
} }

View File

@@ -23,7 +23,7 @@ class ContributorsView extends StatelessWidget {
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),

View File

@@ -7,6 +7,7 @@ import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/homeView/available_updates_card.dart'; import 'package:revanced_manager/ui/widgets/homeView/available_updates_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/installed_apps_card.dart'; import 'package:revanced_manager/ui/widgets/homeView/installed_apps_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/latest_commit_card.dart'; import 'package:revanced_manager/ui/widgets/homeView/latest_commit_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_chip.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';
@@ -17,11 +18,12 @@ class HomeView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<HomeViewModel>.reactive( return ViewModelBuilder<HomeViewModel>.reactive(
disposeViewModel: false, disposeViewModel: false,
fireOnViewModelReadyOnce: true,
onViewModelReady: (model) => model.initialize(context), onViewModelReady: (model) => model.initialize(context),
viewModelBuilder: () => locator<HomeViewModel>(), viewModelBuilder: () => locator<HomeViewModel>(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: RefreshIndicator( body: RefreshIndicator(
color: Theme.of(context).colorScheme.secondary,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
onRefresh: () => model.forceRefresh(context), onRefresh: () => model.forceRefresh(context),
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
@@ -32,7 +34,7 @@ class HomeView extends StatelessWidget {
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
@@ -46,67 +48,40 @@ class HomeView extends StatelessWidget {
'homeView.updatesSubtitle', 'homeView.updatesSubtitle',
child: Text( child: Text(
'', '',
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.headline6,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
LatestCommitCard( LatestCommitCard(
onPressedManager: () => onPressed: () =>
model.showUpdateConfirmationDialog(context), model.showUpdateConfirmationDialog(context),
onPressedPatches: () => model.forceRefresh(context),
), ),
const SizedBox(height: 23), const SizedBox(height: 23),
I18nText( I18nText(
'homeView.patchedSubtitle', 'homeView.patchedSubtitle',
child: Text( child: Text(
'', '',
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.headline6,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
children: <Widget>[ children: <Widget>[
ActionChip( CustomChip(
avatar: const Icon(Icons.grid_view),
label: I18nText('homeView.installed'), label: I18nText('homeView.installed'),
side: BorderSide( isSelected: !model.showUpdatableApps,
color: model.showUpdatableApps onSelected: (value) {
? Theme.of(context).colorScheme.outline
: Theme.of(context)
.colorScheme
.secondaryContainer,
width: model.showUpdatableApps ? 1 : 1,
),
backgroundColor: model.showUpdatableApps
? Theme.of(context).colorScheme.background
: Theme.of(context)
.colorScheme
.secondaryContainer,
onPressed: () {
model.toggleUpdatableApps(false); model.toggleUpdatableApps(false);
}, },
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
ActionChip( CustomChip(
avatar: const Icon(Icons.update),
label: I18nText('homeView.updatesAvailable'), label: I18nText('homeView.updatesAvailable'),
side: BorderSide( isSelected: model.showUpdatableApps,
color: !model.showUpdatableApps onSelected: (value) {
? Theme.of(context).colorScheme.outline
: Theme.of(context)
.colorScheme
.secondaryContainer,
width: !model.showUpdatableApps ? 1 : 1,
),
backgroundColor: !model.showUpdatableApps
? Theme.of(context).colorScheme.background
: Theme.of(context)
.colorScheme
.secondaryContainer,
onPressed: () {
model.toggleUpdatableApps(true); model.toggleUpdatableApps(true);
}, },
), )
], ],
), ),
const SizedBox(height: 14), const SizedBox(height: 14),

View File

@@ -1,30 +1,27 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:app_installer/app_installer.dart'; import 'package:app_installer/app_installer.dart';
import 'package:cross_connectivity/cross_connectivity.dart'; import 'package:cross_connectivity/cross_connectivity.dart';
import 'package:flutter/foundation.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.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';
import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.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/widgets/homeView/update_confirmation_dialog.dart'; import 'package:revanced_manager/ui/widgets/homeView/update_confirmation_dialog.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:revanced_manager/utils/about_info.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import 'package:timezone/timezone.dart' as tz;
@lazySingleton @lazySingleton
class HomeViewModel extends BaseViewModel { class HomeViewModel extends BaseViewModel {
@@ -32,35 +29,20 @@ class HomeViewModel extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final GithubAPI _githubAPI = locator<GithubAPI>(); final GithubAPI _githubAPI = locator<GithubAPI>();
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
DateTime? _lastUpdate; DateTime? _lastUpdate;
bool showUpdatableApps = false; bool showUpdatableApps = false;
List<PatchedApplication> patchedInstalledApps = []; List<PatchedApplication> patchedInstalledApps = [];
List<PatchedApplication> patchedUpdatableApps = []; List<PatchedApplication> patchedUpdatableApps = [];
String _managerVersion = '';
Future<void> initialize(BuildContext context) async { Future<void> initialize(BuildContext context) async {
_managerVersion = await AboutInfo.getInfo().then(
(value) => value.keys.contains('version') ? value['version']! : '',
);
_managerVersion = await _managerAPI.getCurrentManagerVersion();
await flutterLocalNotificationsPlugin.initialize( await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings( const InitializationSettings(
android: AndroidInitializationSettings('ic_notification'), android: AndroidInitializationSettings('ic_notification'),
), ),
onDidReceiveNotificationResponse: (response) async { onSelectNotification: (p) =>
if (response.id == 0) { DeviceApps.openApp('app.revanced.manager.flutter'),
_toast.showBottom('homeView.installingMessage');
final File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) {
await AppInstaller.installApk(managerApk.path);
} else {
_toast.showBottom('homeView.errorDownloadMessage');
}
}
},
); );
flutterLocalNotificationsPlugin flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation< .resolvePlatformSpecificImplementation<
@@ -70,17 +52,6 @@ class HomeViewModel extends BaseViewModel {
if (!isConnected) { if (!isConnected) {
_toast.showBottom('homeView.noConnection'); _toast.showBottom('homeView.noConnection');
} }
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
_toast.showBottom('homeView.installingMessage');
final File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) {
await AppInstaller.installApk(managerApk.path);
} else {
_toast.showBottom('homeView.errorDownloadMessage');
}
}
_getPatchedApps(); _getPatchedApps();
_managerAPI.reAssessSavedApps().then((_) => _getPatchedApps()); _managerAPI.reAssessSavedApps().then((_) => _getPatchedApps());
} }
@@ -116,157 +87,58 @@ class HomeViewModel extends BaseViewModel {
Future<bool> hasManagerUpdates() async { Future<bool> hasManagerUpdates() async {
final String? latestVersion = await _managerAPI.getLatestManagerVersion(); final String? latestVersion = await _managerAPI.getLatestManagerVersion();
String currentVersion = await _managerAPI.getCurrentManagerVersion(); final String currentVersion = await _managerAPI.getCurrentManagerVersion();
// add v to current version
if (!currentVersion.startsWith('v')) {
currentVersion = 'v$currentVersion';
}
if (latestVersion != currentVersion) {
return true;
}
return false;
}
Future<bool> hasPatchesUpdates() async {
final String? latestVersion = await _managerAPI.getLatestPatchesVersion();
final String? currentVersion = await _managerAPI.getCurrentPatchesVersion();
if (latestVersion != null) { if (latestVersion != null) {
try { try {
final int latestVersionInt = final int latestVersionInt =
int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), '')); int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), ''));
final int currentVersionInt = final int currentVersionInt =
int.parse(currentVersion!.replaceAll(RegExp('[^0-9]'), '')); int.parse(currentVersion.replaceAll(RegExp('[^0-9]'), ''));
return latestVersionInt > currentVersionInt; return latestVersionInt > currentVersionInt;
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
return false; return false;
} }
} }
return false; return false;
} }
Future<File?> downloadManager() async {
try {
final response = await _revancedAPI.downloadManager();
final bytes = await response!.readAsBytes();
final tempDir = await getTemporaryDirectory();
final tempFile = File('${tempDir.path}/revanced-manager.apk');
await tempFile.writeAsBytes(bytes);
return tempFile;
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
return null;
}
}
Future<void> updateManager(BuildContext context) async { Future<void> updateManager(BuildContext context) async {
try { try {
_toast.showBottom('homeView.downloadingMessage'); _toast.showBottom('homeView.downloadingMessage');
showDialog( final File? managerApk = await _managerAPI.downloadManager();
context: context, if (managerApk != null) {
builder: (context) => SimpleDialog( await flutterLocalNotificationsPlugin.zonedSchedule(
contentPadding: const EdgeInsets.all(16.0), 0,
title: I18nText( FlutterI18n.translate(
'homeView.downloadingMessage', context,
child: Text( 'homeView.notificationTitle',
'', ),
style: TextStyle( FlutterI18n.translate(
fontSize: 20, context,
fontWeight: FontWeight.w500, 'homeView.notificationText',
color: Theme.of(context).colorScheme.secondary, ),
), tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
const NotificationDetails(
android: AndroidNotificationDetails(
'revanced_manager_channel',
'ReVanced Manager Channel',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
), ),
), ),
children: [ androidAllowWhileIdle: true,
Column( uiLocalNotificationDateInterpretation:
children: [ UILocalNotificationDateInterpretation.absoluteTime,
Row( );
children: [
Icon(
Icons.new_releases_outlined,
color: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 8.0),
Text(
'v$_managerVersion',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary,
),
),
],
),
const SizedBox(height: 16.0),
StreamBuilder<double>(
initialData: 0.0,
stream: _revancedAPI.managerUpdateProgress.stream,
builder: (context, snapshot) {
return LinearProgressIndicator(
value: snapshot.data! * 0.01,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.secondary,
),
);
},
),
const SizedBox(height: 16.0),
Align(
alignment: Alignment.centerRight,
child: CustomMaterialButton(
label: I18nText('cancelButton'),
onPressed: () {
_revancedAPI.disposeManagerUpdateProgress();
Navigator.of(context).pop();
},
),
),
],
),
],
),
);
final File? managerApk = await downloadManager();
if (managerApk != null) {
// await flutterLocalNotificationsPlugin.zonedSchedule(
// 0,
// FlutterI18n.translate(
// context,
// 'homeView.notificationTitle',
// ),
// FlutterI18n.translate(
// context,
// 'homeView.notificationText',
// ),
// tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
// const NotificationDetails(
// android: AndroidNotificationDetails(
// 'revanced_manager_channel',
// 'ReVanced Manager Channel',
// importance: Importance.max,
// priority: Priority.high,
// ticker: 'ticker',
// ),
// ),
// androidAllowWhileIdle: true,
// uiLocalNotificationDateInterpretation:
// UILocalNotificationDateInterpretation.absoluteTime,
// );
_toast.showBottom('homeView.installingMessage'); _toast.showBottom('homeView.installingMessage');
await AppInstaller.installApk(managerApk.path); await AppInstaller.installApk(managerApk.path);
} else { } else {
_toast.showBottom('homeView.errorDownloadMessage'); _toast.showBottom('homeView.errorDownloadMessage');
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
_toast.showBottom('homeView.errorInstallMessage'); _toast.showBottom('homeView.errorInstallMessage');
} }
} }
@@ -275,9 +147,7 @@ class HomeViewModel extends BaseViewModel {
_toast.showBottom('homeView.updatesDisabled'); _toast.showBottom('homeView.updatesDisabled');
} }
Future<void> showUpdateConfirmationDialog( Future<void> showUpdateConfirmationDialog(BuildContext parentContext) {
BuildContext parentContext,
) {
return showModalBottomSheet( return showModalBottomSheet(
context: parentContext, context: parentContext,
isScrollControlled: true, isScrollControlled: true,
@@ -306,7 +176,6 @@ class HomeViewModel extends BaseViewModel {
_lastUpdate!.difference(DateTime.now()).inSeconds > 2) { _lastUpdate!.difference(DateTime.now()).inSeconds > 2) {
_managerAPI.clearAllData(); _managerAPI.clearAllData();
} }
_toast.showBottom('homeView.refreshSuccess');
initialize(context); initialize(context);
} }
} }

View File

@@ -28,7 +28,7 @@ class InstallerView extends StatelessWidget {
title: Text( title: Text(
model.headerLogs, model.headerLogs,
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
onBackButtonPressed: () => model.onWillPop(context), onBackButtonPressed: () => model.onWillPop(context),

View File

@@ -1,6 +1,5 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_background/flutter_background.dart'; import 'package:flutter_background/flutter_background.dart';
@@ -15,6 +14,7 @@ import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/services/toast.dart'; 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/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:wakelock/wakelock.dart'; import 'package:wakelock/wakelock.dart';
@@ -56,10 +56,9 @@ class InstallerViewModel extends BaseViewModel {
), ),
), ),
).then((value) => FlutterBackground.enableBackgroundExecution()); ).then((value) => FlutterBackground.enableBackgroundExecution());
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); // ignore
} // ignore
} }
} }
await Wakelock.enable(); await Wakelock.enable();
@@ -132,15 +131,14 @@ class InstallerViewModel extends BaseViewModel {
_app.apkFilePath, _app.apkFilePath,
_patches, _patches,
); );
} on Exception catch (e) { } on Exception catch (e, s) {
update( update(
-100.0, -100.0,
'Aborting...', 'Aborting...',
'An error occurred! Aborting\nError:\n$e', 'An error occurred! Aborting\nError:\n$e',
); );
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); throw await Sentry.captureException(e, stackTrace: s);
}
} }
} else { } else {
update(-100.0, 'Aborting...', 'No app or patches selected! Aborting'); update(-100.0, 'Aborting...', 'No app or patches selected! Aborting');
@@ -148,17 +146,14 @@ class InstallerViewModel extends BaseViewModel {
if (FlutterBackground.isBackgroundExecutionEnabled) { if (FlutterBackground.isBackgroundExecutionEnabled) {
try { try {
FlutterBackground.disableBackgroundExecution(); FlutterBackground.disableBackgroundExecution();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e); // ignore
} // ignore
} }
} }
await Wakelock.disable(); await Wakelock.disable();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
@@ -216,30 +211,24 @@ class InstallerViewModel extends BaseViewModel {
await _managerAPI.savePatchedApp(_app); await _managerAPI.savePatchedApp(_app);
} }
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
void exportResult() { void exportResult() {
try { try {
_patcherAPI.exportPatchedFile(_app.name, _app.version); _patcherAPI.exportPatchedFile(_app.name, _app.version);
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
void shareResult() { void shareResult() {
try { try {
_patcherAPI.sharePatchedFile(_app.name, _app.version); _patcherAPI.sharePatchedFile(_app.name, _app.version);
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
@@ -253,10 +242,8 @@ class InstallerViewModel extends BaseViewModel {
locator<PatcherViewModel>().selectedApp = null; locator<PatcherViewModel>().selectedApp = null;
locator<PatcherViewModel>().selectedPatches.clear(); locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners(); locator<PatcherViewModel>().notifyListeners();
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }

View File

@@ -42,6 +42,7 @@ class NavigationView extends StatelessWidget {
context, context,
'navigationView.dashboardTab', 'navigationView.dashboardTab',
), ),
tooltip: '',
), ),
NavigationDestination( NavigationDestination(
icon: model.isIndexSelected(1) icon: model.isIndexSelected(1)
@@ -51,6 +52,7 @@ class NavigationView extends StatelessWidget {
context, context,
'navigationView.patcherTab', 'navigationView.patcherTab',
), ),
tooltip: '',
), ),
NavigationDestination( NavigationDestination(
icon: model.isIndexSelected(2) icon: model.isIndexSelected(2)
@@ -60,6 +62,7 @@ class NavigationView extends StatelessWidget {
context, context,
'navigationView.settingsTab', 'navigationView.settingsTab',
), ),
tooltip: '',
), ),
], ],
), ),

View File

@@ -17,14 +17,12 @@ import 'package:stacked/stacked.dart';
class NavigationViewModel extends IndexTrackingViewModel { 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 = final SharedPreferences prefs = await SharedPreferences.getInstance();
await SharedPreferences.getInstance();
if (prefs.getBool('permissionsRequested') == null) { if (prefs.getBool('permissionsRequested') == null) {
await prefs.setBool('permissionsRequested', true); await prefs.setBool('permissionsRequested', true);
RootAPI().hasRootPermissions().then( RootAPI().hasRootPermissions().then(
(value) => Permission.requestInstallPackages.request().then( (value) => Permission.requestInstallPackages.request().then(
(value) => (value) => Permission.ignoreBatteryOptimizations.request(),
Permission.ignoreBatteryOptimizations.request(),
), ),
); );
} }
@@ -39,8 +37,7 @@ class NavigationViewModel extends IndexTrackingViewModel {
SystemUiOverlayStyle( SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent, systemNavigationBarColor: Colors.transparent,
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness:
DynamicTheme.of(context)!.theme.brightness == DynamicTheme.of(context)!.theme.brightness == Brightness.light
Brightness.light
? Brightness.dark ? Brightness.dark
: Brightness.light, : Brightness.light,
), ),

View File

@@ -34,7 +34,7 @@ class PatcherView extends StatelessWidget {
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),

View File

@@ -1,5 +1,3 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
@@ -10,7 +8,6 @@ import 'package:revanced_manager/models/patched_application.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';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:revanced_manager/utils/about_info.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
@@ -55,59 +52,23 @@ class PatcherViewModel extends BaseViewModel {
Future<void> showPatchConfirmationDialog(BuildContext context) async { Future<void> showPatchConfirmationDialog(BuildContext context) async {
final bool isValid = await isValidPatchConfig(); final bool isValid = await isValidPatchConfig();
if (context.mounted) { if (isValid) {
if (isValid) { navigateToInstaller();
showArmv7WarningDialog(context); } else {
} else {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('warning'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('patcherView.armv7WarningDialogText'),
actions: <Widget>[
CustomMaterialButton(
label: I18nText('noButton'),
onPressed: () => Navigator.of(context).pop(),
),
CustomMaterialButton(
label: I18nText('yesButton'),
isFilled: false,
onPressed: () {
Navigator.of(context).pop();
showArmv7WarningDialog(context);
},
)
],
),
);
}
}
}
Future<void> showArmv7WarningDialog(BuildContext context) async {
final bool armv7 = await AboutInfo.getInfo().then(
(info) =>
info['arch'] != null &&
info['arch']!.contains('armeabi-v7a') &&
!info['arch']!.contains('arm64-v8a'),
);
if (context.mounted && armv7) {
return showDialog( return showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: I18nText('warning'), title: I18nText('warning'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer, backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('patcherView.armv7WarningDialogText'), content: I18nText('patcherView.patchDialogText'),
actions: <Widget>[ actions: <Widget>[
CustomMaterialButton( CustomMaterialButton(
isFilled: false,
label: I18nText('noButton'), label: I18nText('noButton'),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
CustomMaterialButton( CustomMaterialButton(
label: I18nText('yesButton'), label: I18nText('yesButton'),
isFilled: false,
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
navigateToInstaller(); navigateToInstaller();
@@ -116,8 +77,6 @@ class PatcherViewModel extends BaseViewModel {
], ],
), ),
); );
} else {
navigateToInstaller();
} }
} }
@@ -129,24 +88,24 @@ class PatcherViewModel extends BaseViewModel {
return text; return text;
} }
String getSuggestedVersionString(BuildContext context) { String getRecommendedVersionString(BuildContext context) {
String suggestedVersion = String recommendedVersion =
_patcherAPI.getSuggestedVersion(selectedApp!.packageName); _patcherAPI.getRecommendedVersion(selectedApp!.packageName);
if (suggestedVersion.isEmpty) { if (recommendedVersion.isEmpty) {
suggestedVersion = FlutterI18n.translate( recommendedVersion = FlutterI18n.translate(
context, context,
'appSelectorCard.anyVersion', 'appSelectorCard.anyVersion',
); );
} else { } else {
suggestedVersion = 'v$suggestedVersion'; recommendedVersion = 'v$recommendedVersion';
} }
return '${FlutterI18n.translate( return '${FlutterI18n.translate(
context, context,
'appSelectorCard.currentVersion', 'appSelectorCard.currentVersion',
)}: v${selectedApp!.version}\n${FlutterI18n.translate( )}: v${selectedApp!.version}\n${FlutterI18n.translate(
context, context,
'appSelectorCard.suggestedVersion', 'appSelectorCard.recommendedVersion',
)}: $suggestedVersion'; )}: $recommendedVersion';
} }
Future<void> loadLastSelectedPatches() async { Future<void> loadLastSelectedPatches() async {
@@ -155,8 +114,7 @@ class PatcherViewModel extends BaseViewModel {
await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName); await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName);
final List<Patch> patches = final List<Patch> patches =
_patcherAPI.getFilteredPatches(selectedApp!.originalPackageName); _patcherAPI.getFilteredPatches(selectedApp!.originalPackageName);
this this.selectedPatches
.selectedPatches
.addAll(patches.where((patch) => selectedPatches.contains(patch.name))); .addAll(patches.where((patch) => selectedPatches.contains(patch.name)));
notifyListeners(); notifyListeners();
} }

View File

@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.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_chip.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.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:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class PatchesSelectorView extends StatefulWidget { class PatchesSelectorView extends StatefulWidget {
@@ -50,19 +50,20 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
child: Text( child: Text(
'', '',
style: TextStyle( style: TextStyle(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back, Icons.arrow_back,
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
actions: [ actions: [
Container( Container(
height: 2,
margin: const EdgeInsets.only(top: 12, bottom: 12), margin: const EdgeInsets.only(top: 12, bottom: 12),
padding: padding:
const EdgeInsets.symmetric(horizontal: 6, vertical: 6), const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
@@ -74,7 +75,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
child: Text( child: Text(
model.patchesVersion!, model.patchesVersion!,
style: TextStyle( style: TextStyle(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
@@ -135,24 +136,27 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
children: [ children: [
Row( Row(
children: [ children: [
ActionChip( CustomChip(
label: I18nText('patchesSelectorView.default'), label:
tooltip: FlutterI18n.translate( I18nText('patchesSelectorView.recommended'),
context, onSelected: (value) {
'patchesSelectorView.defaultTooltip', model.selectRecommendedPatches();
),
onPressed: () {
model.selectDefaultPatches();
}, },
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
ActionChip( CustomChip(
label: I18nText('patchesSelectorView.all'),
onSelected: (value) {
if (value) {
model.selectAllPatcherWarning(context);
}
model.selectAllPatches(true);
},
),
const SizedBox(width: 8),
CustomChip(
label: I18nText('patchesSelectorView.none'), label: I18nText('patchesSelectorView.none'),
tooltip: FlutterI18n.translate( onSelected: (value) {
context,
'patchesSelectorView.noneTooltip',
),
onPressed: () {
model.clearPatches(); model.clearPatches();
}, },
), ),
@@ -169,7 +173,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
packageVersion: model.getAppVersion(), packageVersion: model.getAppVersion(),
supportedPackageVersions: supportedPackageVersions:
model.getSupportedVersions(patch), model.getSupportedVersions(patch),
isUnsupported: !isPatchSupported(patch), isUnsupported: !model.isPatchSupported(patch),
isSelected: model.isSelected(patch), isSelected: model.isSelected(patch),
onChanged: (value) => onChanged: (value) =>
model.selectPatch(patch, value), model.selectPatch(patch, value),

View File

@@ -1,4 +1,6 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.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/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
@@ -7,7 +9,7 @@ import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/toast.dart'; 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/utils/check_for_supported_patch.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class PatchesSelectorViewModel extends BaseViewModel { class PatchesSelectorViewModel extends BaseViewModel {
@@ -23,7 +25,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
} }
Future<void> initialize() async { Future<void> initialize() async {
getPatchesVersion().whenComplete(() => notifyListeners()); getPatchesVersion();
patches.addAll( patches.addAll(
_patcherAPI.getFilteredPatches( _patcherAPI.getFilteredPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName, locator<PatcherViewModel>().selectedApp!.originalPackageName,
@@ -48,7 +50,39 @@ class PatchesSelectorViewModel extends BaseViewModel {
notifyListeners(); notifyListeners();
} }
void selectDefaultPatches() { Future<void> selectAllPatcherWarning(BuildContext context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('warning'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('patchesSelectorView.selectAllPatchesWarningContent'),
actions: <Widget>[
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () => Navigator.of(context).pop(),
)
],
),
);
}
void selectAllPatches(bool isSelected) {
selectedPatches.clear();
if (isSelected && _managerAPI.areExperimentalPatchesEnabled() == false) {
selectedPatches
.addAll(patches.where((element) => isPatchSupported(element)));
}
if (isSelected && _managerAPI.areExperimentalPatchesEnabled()) {
selectedPatches.addAll(patches);
}
notifyListeners();
}
void selectRecommendedPatches() {
selectedPatches.clear(); selectedPatches.clear();
if (_managerAPI.areExperimentalPatchesEnabled() == false) { if (_managerAPI.areExperimentalPatchesEnabled() == false) {
@@ -119,6 +153,16 @@ class PatchesSelectorViewModel extends BaseViewModel {
} }
} }
bool isPatchSupported(Patch patch) {
final PatchedApplication app = locator<PatcherViewModel>().selectedApp!;
return patch.compatiblePackages.isEmpty ||
patch.compatiblePackages.any(
(pack) =>
pack.name == app.packageName &&
(pack.versions.isEmpty || pack.versions.contains(app.version)),
);
}
void onMenuSelection(value) { void onMenuSelection(value) {
switch (value) { switch (value) {
case 0: case 0:

View File

@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.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/services/toast.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart'; import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
@@ -12,7 +11,6 @@ import 'package:stacked/stacked.dart';
class SManageApiUrl extends BaseViewModel { class SManageApiUrl extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final Toast _toast = locator<Toast>();
final TextEditingController _apiUrlController = TextEditingController(); final TextEditingController _apiUrlController = TextEditingController();
@@ -92,7 +90,7 @@ class SManageApiUrl extends BaseViewModel {
label: I18nText('yesButton'), label: I18nText('yesButton'),
onPressed: () { onPressed: () {
_managerAPI.setApiUrl(''); _managerAPI.setApiUrl('');
_toast.showBottom('settingsView.restartAppForChanges'); Navigator.of(context).pop();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
) )

View File

@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.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/services/toast.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart'; import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
@@ -12,7 +11,6 @@ import 'package:stacked/stacked.dart';
class SManageSources extends BaseViewModel { class SManageSources extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final Toast _toast = locator<Toast>();
final TextEditingController _hostSourceController = TextEditingController(); final TextEditingController _hostSourceController = TextEditingController();
final TextEditingController _orgPatSourceController = TextEditingController(); final TextEditingController _orgPatSourceController = TextEditingController();
@@ -119,14 +117,13 @@ class SManageSources extends BaseViewModel {
CustomMaterialButton( CustomMaterialButton(
label: I18nText('okButton'), label: I18nText('okButton'),
onPressed: () { onPressed: () {
_managerAPI.setRepoUrl(_hostSourceController.text.trim()); _managerAPI.setRepoUrl(_hostSourceController.text);
_managerAPI.setPatchesRepo( _managerAPI.setPatchesRepo(
'${_orgPatSourceController.text.trim()}/${_patSourceController.text.trim()}', '${_orgPatSourceController.text}/${_patSourceController.text}',
); );
_managerAPI.setIntegrationsRepo( _managerAPI.setIntegrationsRepo(
'${_orgIntSourceController.text.trim()}/${_intSourceController.text.trim()}', '${_orgIntSourceController.text}/${_intSourceController.text}',
); );
_toast.showBottom('settingsView.restartAppForChanges');
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
) )
@@ -154,10 +151,9 @@ class SManageSources extends BaseViewModel {
_managerAPI.setRepoUrl(''); _managerAPI.setRepoUrl('');
_managerAPI.setPatchesRepo(''); _managerAPI.setPatchesRepo('');
_managerAPI.setIntegrationsRepo(''); _managerAPI.setIntegrationsRepo('');
_toast.showBottom('settingsView.restartAppForChanges'); Navigator.of(context).pop();
Navigator.of(context) Navigator.of(context).pop();
..pop() Navigator.of(context).pop();
..pop();
}, },
) )
], ],

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:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/main.dart'; import 'package:revanced_manager/main.dart';
import 'package:revanced_manager/services/crowdin_api.dart';
import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
@@ -15,6 +16,7 @@ import 'package:timeago/timeago.dart' as timeago;
final _settingViewModel = SettingsViewModel(); final _settingViewModel = SettingsViewModel();
class SUpdateLanguage extends BaseViewModel { class SUpdateLanguage extends BaseViewModel {
final CrowdinAPI _crowdinAPI = locator<CrowdinAPI>();
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
late SharedPreferences _prefs; late SharedPreferences _prefs;
String selectedLanguage = 'English'; String selectedLanguage = 'English';
@@ -41,6 +43,7 @@ class SUpdateLanguage extends BaseViewModel {
} }
Future<void> initLang() async { Future<void> initLang() async {
languages = await _crowdinAPI.getLanguages();
languages.sort((a, b) => a['name'].compareTo(b['name'])); languages.sort((a, b) => a['name'].compareTo(b['name']));
notifyListeners(); notifyListeners();
} }

View File

@@ -7,6 +7,7 @@ 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/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_switch_tile.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
@@ -63,8 +64,8 @@ class SUpdateThemeUI extends StatelessWidget {
return SettingsSection( return SettingsSection(
title: 'settingsView.appearanceSectionTitle', title: 'settingsView.appearanceSectionTitle',
children: <Widget>[ children: <Widget>[
SwitchListTile( CustomSwitchTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.darkThemeLabel', 'settingsView.darkThemeLabel',
child: const Text( child: const Text(
@@ -77,7 +78,7 @@ class SUpdateThemeUI extends StatelessWidget {
), ),
subtitle: I18nText('settingsView.darkThemeHint'), subtitle: I18nText('settingsView.darkThemeHint'),
value: SUpdateTheme().getDarkThemeStatus(), value: SUpdateTheme().getDarkThemeStatus(),
onChanged: (value) => SUpdateTheme().setUseDarkTheme( onTap: (value) => SUpdateTheme().setUseDarkTheme(
context, context,
value, value,
), ),
@@ -87,8 +88,8 @@ class SUpdateThemeUI extends StatelessWidget {
builder: (context, snapshot) => Visibility( builder: (context, snapshot) => Visibility(
visible: visible:
snapshot.hasData && snapshot.data! >= ANDROID_12_SDK_VERSION, snapshot.hasData && snapshot.data! >= ANDROID_12_SDK_VERSION,
child: SwitchListTile( child: CustomSwitchTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.dynamicThemeLabel', 'settingsView.dynamicThemeLabel',
child: const Text( child: const Text(
@@ -101,12 +102,11 @@ class SUpdateThemeUI extends StatelessWidget {
), ),
subtitle: I18nText('settingsView.dynamicThemeHint'), subtitle: I18nText('settingsView.dynamicThemeHint'),
value: _settingViewModel.sUpdateTheme.getDynamicThemeStatus(), value: _settingViewModel.sUpdateTheme.getDynamicThemeStatus(),
onChanged: (value) => { onTap: (value) =>
_settingViewModel.sUpdateTheme.setUseDynamicTheme( _settingViewModel.sUpdateTheme.setUseDynamicTheme(
context, context,
value, value,
), ),
},
), ),
), ),
), ),

View File

@@ -1,86 +0,0 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart';
class SManageKeystorePassword extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final TextEditingController _keystorePasswordController =
TextEditingController();
Future<void> showKeystoreDialog(BuildContext context) async {
final String keystorePasswordText = _managerAPI.getKeystorePassword();
_keystorePasswordController.text = keystorePasswordText;
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: <Widget>[
I18nText('settingsView.selectKeystorePassword'),
const Spacer(),
IconButton(
icon: const Icon(Icons.manage_history_outlined),
onPressed: () => _keystorePasswordController.text =
_managerAPI.defaultKeystorePassword,
color: Theme.of(context).colorScheme.secondary,
)
],
),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: SingleChildScrollView(
child: Column(
children: <Widget>[
CustomTextField(
inputController: _keystorePasswordController,
label: I18nText('settingsView.selectKeystorePassword'),
hint: '',
onChanged: (value) => notifyListeners(),
),
],
),
),
actions: <Widget>[
CustomMaterialButton(
isFilled: false,
label: I18nText('cancelButton'),
onPressed: () {
_keystorePasswordController.clear();
Navigator.of(context).pop();
},
),
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () {
final String passwd = _keystorePasswordController.text;
_managerAPI.setKeystorePassword(passwd);
Navigator.of(context).pop();
},
)
],
),
);
}
}
final sManageKeystorePassword = SManageKeystorePassword();
class SManageKeystorePasswordUI extends StatelessWidget {
const SManageKeystorePasswordUI({super.key});
@override
Widget build(BuildContext context) {
return SettingsTileDialog(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: 'settingsView.selectKeystorePassword',
subtitle: 'settingsView.selectKeystorePasswordHint',
onTap: () => sManageKeystorePassword.showKeystoreDialog(context),
);
}
}

View File

@@ -3,7 +3,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:google_fonts/google_fonts.dart'; 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/settingsFragement/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_export_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_export_section.dart';
@@ -32,7 +32,7 @@ class SettingsView extends StatelessWidget {
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
@@ -49,6 +49,8 @@ class SettingsView extends StatelessWidget {
_settingsDivider, _settingsDivider,
SExportSection(), SExportSection(),
_settingsDivider, _settingsDivider,
// SLoggingSection(),
// _settingsDivider,
SInfoSection(), SInfoSection(),
], ],
), ),

View File

@@ -2,7 +2,6 @@ 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: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:logcat/logcat.dart'; import 'package:logcat/logcat.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
@@ -10,15 +9,15 @@ import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/toast.dart'; 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/settings/settingsFragment/settings_update_language.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_update_language.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_theme.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_update_theme.dart';
import 'package:sentry_flutter/sentry_flutter.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';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
class SettingsViewModel extends BaseViewModel { class SettingsViewModel extends BaseViewModel {
final NavigationService _navigationService = final NavigationService _navigationService = locator<NavigationService>();
locator<NavigationService>();
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
@@ -29,6 +28,16 @@ class SettingsViewModel extends BaseViewModel {
_navigationService.navigateTo(Routes.contributorsView); _navigationService.navigateTo(Routes.contributorsView);
} }
bool isSentryEnabled() {
return _managerAPI.isSentryEnabled();
}
void useSentry(bool value) {
_managerAPI.setSentryStatus(value);
_toast.showBottom('settingsView.restartAppForChanges');
notifyListeners();
}
bool areUniversalPatchesEnabled() { bool areUniversalPatchesEnabled() {
return _managerAPI.areUniversalPatchesEnabled(); return _managerAPI.areUniversalPatchesEnabled();
} }
@@ -63,32 +72,22 @@ class SettingsViewModel extends BaseViewModel {
try { try {
final File outFile = File(_managerAPI.storedPatchesFile); final File outFile = File(_managerAPI.storedPatchesFile);
if (outFile.existsSync()) { if (outFile.existsSync()) {
final String dateTime = DateTime.now() final String dateTime =
.toString() DateTime.now().toString().replaceAll(' ', '_').split('.').first;
.replaceAll(' ', '_') await CRFileSaver.saveFileWithDialog(SaveFileDialogParams(
.split('.') sourceFilePath: outFile.path, destinationFileName: 'selected_patches_$dateTime.json',),);
.first;
await CRFileSaver.saveFileWithDialog(
SaveFileDialogParams(
sourceFilePath: outFile.path,
destinationFileName: 'selected_patches_$dateTime.json',
),
);
_toast.showBottom('settingsView.exportedPatches'); _toast.showBottom('settingsView.exportedPatches');
} else { } else {
_toast.showBottom('settingsView.noExportFileFound'); _toast.showBottom('settingsView.noExportFileFound');
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { Sentry.captureException(e, stackTrace: s);
print(e);
}
} }
} }
Future<void> importPatches() async { Future<void> importPatches() async {
try { try {
final FilePickerResult? result = final FilePickerResult? result = await FilePicker.platform.pickFiles(
await FilePicker.platform.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: ['json'], allowedExtensions: ['json'],
); );
@@ -101,54 +100,12 @@ class SettingsViewModel extends BaseViewModel {
} }
_toast.showBottom('settingsView.importedPatches'); _toast.showBottom('settingsView.importedPatches');
} }
} on Exception catch (e) { } on Exception catch (e, s) {
if (kDebugMode) { await Sentry.captureException(e, stackTrace: s);
print(e);
}
_toast.showBottom('settingsView.jsonSelectorErrorMessage'); _toast.showBottom('settingsView.jsonSelectorErrorMessage');
} }
} }
Future<void> exportKeystore() async {
try {
final File outFile = File(_managerAPI.keystoreFile);
if (outFile.existsSync()) {
final String dateTime =
DateTime.now().toString().replaceAll(' ', '_').split('.').first;
await CRFileSaver.saveFileWithDialog(
SaveFileDialogParams(
sourceFilePath: outFile.path,
destinationFileName: 'keystore_$dateTime.keystore',
),
);
_toast.showBottom('settingsView.exportedKeystore');
} else {
_toast.showBottom('settingsView.noKeystoreExportFileFound');
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
}
Future<void> importKeystore() async {
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null && result.files.single.path != null) {
final File inFile = File(result.files.single.path!);
inFile.copySync(_managerAPI.keystoreFile);
_toast.showBottom('settingsView.importedKeystore');
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
_toast.showBottom('settingsView.keystoreSelectorErrorMessage');
}
}
void resetSelectedPatches() { void resetSelectedPatches() {
_managerAPI.resetLastSelectedPatches(); _managerAPI.resetLastSelectedPatches();
_toast.showBottom('settingsView.resetStoredPatches'); _toast.showBottom('settingsView.resetStoredPatches');
@@ -156,7 +113,7 @@ class SettingsViewModel extends BaseViewModel {
Future<int> getSdkVersion() async { Future<int> getSdkVersion() async {
final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo; final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo;
return info.version.sdkInt; return info.version.sdkInt ?? -1;
} }
Future<void> deleteLogs() async { Future<void> deleteLogs() async {

View File

@@ -27,7 +27,7 @@ class AppInfoView extends StatelessWidget {
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
), ),
), ),
@@ -51,13 +51,13 @@ class AppInfoView extends StatelessWidget {
Text( Text(
app.name, app.name,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.headline6,
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
app.version, app.version,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.subtitle1,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Padding( Padding(

View File

@@ -9,14 +9,12 @@ class InstalledAppItem extends StatefulWidget {
required this.pkgName, required this.pkgName,
required this.icon, required this.icon,
required this.patchesCount, required this.patchesCount,
required this.suggestedVersion,
this.onTap, this.onTap,
}) : super(key: key); }) : super(key: key);
final String name; final String name;
final String pkgName; final String pkgName;
final Uint8List icon; final Uint8List icon;
final int patchesCount; final int patchesCount;
final String suggestedVersion;
final Function()? onTap; final Function()? onTap;
@override @override
@@ -48,35 +46,31 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text(
widget.name,
maxLines: 2,
overflow: TextOverflow.visible,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(widget.pkgName),
Row( Row(
children: [ children: <Widget>[
Text( Text(
widget.suggestedVersion.isEmpty widget.name,
? 'All versions' maxLines: 2,
: widget.suggestedVersion, overflow: TextOverflow.visible,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
), ),
const SizedBox(width: 4), const SizedBox(width: 6),
Text( Text(
widget.patchesCount == 1 widget.patchesCount == 1
? '${widget.patchesCount} patch' ? '${widget.patchesCount} patch'
: '${widget.patchesCount} patches', : '${widget.patchesCount} patches',
style: TextStyle( style: TextStyle(
fontSize: 8,
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
), ),
], ],
), ),
const SizedBox(height: 4),
Text(widget.pkgName),
], ],
), ),
), ),

View File

@@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class NotInstalledAppItem extends StatefulWidget {
const NotInstalledAppItem({
Key? key,
required this.name,
required this.patchesCount,
required this.suggestedVersion,
this.onTap,
}) : super(key: key);
final String name;
final int patchesCount;
final String suggestedVersion;
final Function()? onTap;
@override
State<NotInstalledAppItem> createState() => _NotInstalledAppItem();
}
class _NotInstalledAppItem extends State<NotInstalledAppItem> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: CustomCard(
onTap: widget.onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: 48,
padding: const EdgeInsets.symmetric(vertical: 4.0),
alignment: Alignment.center,
child: const CircleAvatar(
backgroundColor: Colors.transparent,
child: Icon(
Icons.square_rounded,
color: Colors.grey,
size: 44,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
const Text('App not installed.'),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.suggestedVersion.isEmpty
? 'All versions'
: widget.suggestedVersion,
),
const SizedBox(width: 4),
Text(
widget.patchesCount == 1
? '${widget.patchesCount} patch'
: '${widget.patchesCount} patches',
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
),
),
],
),
],
),
),
],
),
),
);
}
}

View File

@@ -55,7 +55,6 @@ class _ContributorsCardState extends State<ContributorsCard> {
Uri.parse( Uri.parse(
widget.contributors[index]['html_url'], widget.contributors[index]['html_url'],
), ),
mode: LaunchMode.externalApplication,
), ),
child: FutureBuilder<File?>( child: FutureBuilder<File?>(
future: DefaultCacheManager().getSingleFile( future: DefaultCacheManager().getSingleFile(

View File

@@ -28,7 +28,7 @@ class AvailableUpdatesCard extends StatelessWidget {
child: Text( child: Text(
'', '',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium!.copyWith( style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
), ),
@@ -53,7 +53,7 @@ class AvailableUpdatesCard extends StatelessWidget {
// child: Text( // child: Text(
// '', // '',
// textAlign: TextAlign.center, // textAlign: TextAlign.center,
// style: Theme.of(context).textTheme.titleMedium!.copyWith( // style: Theme.of(context).textTheme.subtitle1!.copyWith(
// color: Theme.of(context).colorScheme.secondary, // color: Theme.of(context).colorScheme.secondary,
// ), // ),
// ), // ),

View File

@@ -30,7 +30,7 @@ class InstalledAppsCard extends StatelessWidget {
child: Text( child: Text(
'', '',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium!.copyWith( style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
), ),

View File

@@ -8,11 +8,9 @@ import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
class LatestCommitCard extends StatefulWidget { class LatestCommitCard extends StatefulWidget {
const LatestCommitCard({ const LatestCommitCard({
Key? key, Key? key,
required this.onPressedManager, required this.onPressed,
required this.onPressedPatches,
}) : super(key: key); }) : super(key: key);
final Function() onPressedManager; final Function() onPressed;
final Function() onPressedPatches;
@override @override
State<LatestCommitCard> createState() => _LatestCommitCardState(); State<LatestCommitCard> createState() => _LatestCommitCardState();
@@ -23,109 +21,66 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return CustomCard(
children: [ child: Row(
// ReVanced Manager mainAxisAlignment: MainAxisAlignment.spaceBetween,
CustomCard( children: <Widget>[
child: Row( Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Column( Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( I18nText('latestCommitCard.patcherLabel'),
children: const <Widget>[ FutureBuilder<String?>(
Text('ReVanced Manager'), future: model.getLatestPatcherReleaseTime(),
], builder: (context, snapshot) => Text(
), snapshot.hasData && snapshot.data!.isNotEmpty
const SizedBox(height: 4), ? FlutterI18n.translate(
Row( context,
children: <Widget>[ 'latestCommitCard.timeagoLabel',
FutureBuilder<String?>( translationParams: {'time': snapshot.data!},
future: model.getLatestManagerReleaseTime(), )
builder: (context, snapshot) => : FlutterI18n.translate(
snapshot.hasData && snapshot.data!.isNotEmpty context,
? I18nText( 'latestCommitCard.loadingLabel',
'latestCommitCard.timeagoLabel', ),
translationParams: {'time': snapshot.data!}, ),
)
: I18nText('latestCommitCard.loadingLabel'),
),
],
), ),
], ],
), ),
FutureBuilder<bool>( const SizedBox(height: 4),
future: model.hasManagerUpdates(), Row(
initialData: false,
builder: (context, snapshot) => Opacity(
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
child: CustomMaterialButton(
label: I18nText('updateButton'),
onPressed: snapshot.hasData && snapshot.data!
? widget.onPressedManager
: () => {},
),
),
),
],
),
),
const SizedBox(height: 16),
// ReVanced Patches
CustomCard(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( I18nText('latestCommitCard.managerLabel'),
children: const <Widget>[ FutureBuilder<String?>(
Text('ReVanced Patches'), future: model.getLatestManagerReleaseTime(),
], builder: (context, snapshot) =>
), snapshot.hasData && snapshot.data!.isNotEmpty
const SizedBox(height: 4), ? I18nText(
Row( 'latestCommitCard.timeagoLabel',
children: <Widget>[ translationParams: {'time': snapshot.data!},
FutureBuilder<String?>( )
future: model.getLatestPatcherReleaseTime(), : I18nText('latestCommitCard.loadingLabel'),
builder: (context, snapshot) => Text(
snapshot.hasData && snapshot.data!.isNotEmpty
? FlutterI18n.translate(
context,
'latestCommitCard.timeagoLabel',
translationParams: {'time': snapshot.data!},
)
: FlutterI18n.translate(
context,
'latestCommitCard.loadingLabel',
),
),
),
],
), ),
], ],
), ),
FutureBuilder<bool>(
future: locator<HomeViewModel>().hasPatchesUpdates(),
initialData: false,
builder: (context, snapshot) => Opacity(
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
child: CustomMaterialButton(
label: I18nText('updateButton'),
onPressed: snapshot.hasData && snapshot.data!
? widget.onPressedPatches
: () => {},
),
),
),
], ],
), ),
), FutureBuilder<bool>(
], future: locator<HomeViewModel>().hasManagerUpdates(),
initialData: false,
builder: (context, snapshot) => Opacity(
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
child: CustomMaterialButton(
label: I18nText('latestCommitCard.updateButton'),
onPressed: snapshot.hasData && snapshot.data!
? widget.onPressed
: () => {},
),
),
),
],
),
); );
} }
} }

View File

@@ -16,12 +16,12 @@ class UpdateConfirmationDialog extends StatelessWidget {
expand: false, expand: false,
snap: true, snap: true,
snapSizes: const [0.5], snapSizes: const [0.5],
builder: (_, scrollController) => SingleChildScrollView( builder: (context, scrollController) => SingleChildScrollView(
controller: scrollController, controller: scrollController,
child: SafeArea( child: SafeArea(
child: FutureBuilder<Map<String, dynamic>?>( child: FutureBuilder<Map<String, dynamic>?>(
future: model.getLatestManagerRelease(), future: model.getLatestManagerRelease(),
builder: (_, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const SizedBox( return const SizedBox(
height: 300, height: 300,
@@ -45,8 +45,7 @@ class UpdateConfirmationDialog extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [ children: [
I18nText( I18nText(
'homeView.updateDialogTitle', 'homeView.updateDialogTitle',
@@ -63,14 +62,12 @@ class UpdateConfirmationDialog extends StatelessWidget {
children: [ children: [
Icon( Icon(
Icons.new_releases_outlined, Icons.new_releases_outlined,
color: Theme.of(context) color:
.colorScheme Theme.of(context).colorScheme.secondary,
.secondary,
), ),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Text(
snapshot.data!['tag_name'] ?? snapshot.data!['tag_name'] ?? 'Unknown',
'Unknown',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -96,8 +93,7 @@ class UpdateConfirmationDialog extends StatelessWidget {
), ),
), ),
Padding( Padding(
padding: padding: const EdgeInsets.only(left: 24.0, bottom: 12.0),
const EdgeInsets.only(left: 24.0, bottom: 12.0),
child: I18nText( child: I18nText(
'homeView.updateChangelogTitle', 'homeView.updateChangelogTitle',
child: Text( child: Text(
@@ -113,12 +109,9 @@ class UpdateConfirmationDialog extends StatelessWidget {
), ),
), ),
Container( Container(
margin: margin: const EdgeInsets.symmetric(horizontal: 24.0),
const EdgeInsets.symmetric(horizontal: 24.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context) color: Theme.of(context).colorScheme.secondaryContainer,
.colorScheme
.secondaryContainer,
borderRadius: BorderRadius.circular(12.0), borderRadius: BorderRadius.circular(12.0),
), ),
child: Markdown( child: Markdown(

View File

@@ -63,7 +63,7 @@ class AppSelectorCard extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
locator<PatcherViewModel>() locator<PatcherViewModel>()
.getSuggestedVersionString(context), .getRecommendedVersionString(context),
), ),
], ],
), ),

View File

@@ -75,7 +75,7 @@ class _PatchItemState extends State<PatchItem> {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: Text( child: Text(
widget.simpleName, widget.simpleName,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
@@ -99,6 +99,7 @@ class _PatchItemState extends State<PatchItem> {
Text( Text(
widget.description, widget.description,
softWrap: true, softWrap: true,
maxLines: 3,
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,

View File

@@ -61,7 +61,7 @@ class OptionsFilePicker extends StatelessWidget {
child: Text( child: Text(
'Select File', 'Select File',
style: TextStyle( style: TextStyle(
color: Theme.of(context).textTheme.bodyLarge?.color, color: Theme.of(context).textTheme.bodyText1?.color,
), ),
), ),
), ),

View File

@@ -2,13 +2,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.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/settingsFragement/settings_manage_api_url.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_sources.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_manage_sources.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_experimental_patches.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_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';
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
@@ -22,7 +21,6 @@ class SAdvancedSection extends StatelessWidget {
children: <Widget>[ children: <Widget>[
SManageApiUrlUI(), SManageApiUrlUI(),
SManageSourcesUI(), SManageSourcesUI(),
// SManageKeystorePasswordUI(),
SExperimentalUniversalPatches(), SExperimentalUniversalPatches(),
SExperimentalPatches(), SExperimentalPatches(),
ListTile( ListTile(
@@ -38,7 +36,7 @@ class SAdvancedSection extends StatelessWidget {
), ),
), ),
subtitle: I18nText('settingsView.deleteKeystoreHint'), subtitle: I18nText('settingsView.deleteKeystoreHint'),
onTap: () => _showDeleteKeystoreDialog(context), onTap: () => _settingsViewModel.deleteKeystore,
), ),
ListTile( ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
@@ -73,31 +71,4 @@ class SAdvancedSection extends StatelessWidget {
], ],
); );
} }
Future<void> _showDeleteKeystoreDialog(context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('warning'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText(
'settingsView.deleteKeystoreDialogText',
),
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,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.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/custom_switch_tile.dart';
class SExperimentalPatches extends StatefulWidget { class SExperimentalPatches extends StatefulWidget {
const SExperimentalPatches({super.key}); const SExperimentalPatches({super.key});
@@ -14,8 +15,8 @@ final _settingsViewModel = SettingsViewModel();
class _SExperimentalPatchesState extends State<SExperimentalPatches> { class _SExperimentalPatchesState extends State<SExperimentalPatches> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SwitchListTile( return CustomSwitchTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.experimentalPatchesLabel', 'settingsView.experimentalPatchesLabel',
child: const Text( child: const Text(
@@ -28,7 +29,7 @@ class _SExperimentalPatchesState extends State<SExperimentalPatches> {
), ),
subtitle: I18nText('settingsView.experimentalPatchesHint'), subtitle: I18nText('settingsView.experimentalPatchesHint'),
value: _settingsViewModel.areExperimentalPatchesEnabled(), value: _settingsViewModel.areExperimentalPatchesEnabled(),
onChanged: (value) { onTap: (value) {
setState(() { setState(() {
_settingsViewModel.useExperimentalPatches(value); _settingsViewModel.useExperimentalPatches(value);
}); });

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.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/custom_switch_tile.dart';
class SExperimentalUniversalPatches extends StatefulWidget { class SExperimentalUniversalPatches extends StatefulWidget {
const SExperimentalUniversalPatches({super.key}); const SExperimentalUniversalPatches({super.key});
@@ -16,8 +17,8 @@ class _SExperimentalUniversalPatchesState
extends State<SExperimentalUniversalPatches> { extends State<SExperimentalUniversalPatches> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SwitchListTile( return CustomSwitchTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(
'settingsView.experimentalUniversalPatchesLabel', 'settingsView.experimentalUniversalPatchesLabel',
child: const Text( child: const Text(
@@ -30,7 +31,7 @@ class _SExperimentalUniversalPatchesState
), ),
subtitle: I18nText('settingsView.experimentalUniversalPatchesHint'), subtitle: I18nText('settingsView.experimentalUniversalPatchesHint'),
value: _settingsViewModel.areUniversalPatchesEnabled(), value: _settingsViewModel.areUniversalPatchesEnabled(),
onChanged: (value) { onTap: (value) {
setState(() { setState(() {
_settingsViewModel.showUniversalPatches(value); _settingsViewModel.showUniversalPatches(value);
}); });

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_keystore_password.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_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
@@ -44,40 +43,6 @@ class SExportSection extends StatelessWidget {
subtitle: I18nText('settingsView.importPatchesHint'), subtitle: I18nText('settingsView.importPatchesHint'),
onTap: () => _settingsViewModel.importPatches(), onTap: () => _settingsViewModel.importPatches(),
), ),
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: () {
_settingsViewModel.importKeystore();
final sManageKeystorePassword = SManageKeystorePassword();
sManageKeystorePassword.showKeystoreDialog(context);
},
),
ListTile( ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText( title: I18nText(

View File

@@ -0,0 +1,36 @@
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/custom_switch_tile.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
final _settingsViewModel = SettingsViewModel();
class SLoggingSection extends StatelessWidget {
const SLoggingSection({super.key});
@override
Widget build(BuildContext context) {
return SettingsSection(
title: 'settingsView.logsSectionTitle',
children: <Widget>[
CustomSwitchTile(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.sentryLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.sentryHint'),
value: _settingsViewModel.isSentryEnabled(),
onTap: (value) => _settingsViewModel.useSentry(value),
),
],
);
}
}

View File

@@ -17,7 +17,7 @@ class CustomChip extends StatelessWidget {
showCheckmark: false, showCheckmark: false,
label: label, label: label,
selected: isSelected, selected: isSelected,
labelStyle: Theme.of(context).textTheme.titleSmall!.copyWith( labelStyle: Theme.of(context).textTheme.subtitle2!.copyWith(
color: isSelected color: isSelected
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.secondary, : Theme.of(context).colorScheme.secondary,

View File

@@ -33,7 +33,7 @@ class CustomSliverAppBar extends StatelessWidget {
: IconButton( : IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back, Icons.arrow_back,
color: Theme.of(context).textTheme.titleLarge!.color, color: Theme.of(context).textTheme.headline6!.color,
), ),
onPressed: onPressed:
onBackButtonPressed ?? () => Navigator.of(context).pop(), onBackButtonPressed ?? () => Navigator.of(context).pop(),

View File

@@ -11,7 +11,7 @@ class AboutInfo {
'flavor': kReleaseMode ? 'release' : 'debug', 'flavor': kReleaseMode ? 'release' : 'debug',
'model': info.model, 'model': info.model,
'androidVersion': info.version.release, 'androidVersion': info.version.release,
'arch': info.supportedAbis 'arch': info.supportedAbis.first
}; };
} }
} }

View File

@@ -1,14 +0,0 @@
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/ui/views/patcher/patcher_viewmodel.dart';
bool isPatchSupported(Patch patch) {
final PatchedApplication app = locator<PatcherViewModel>().selectedApp!;
return patch.compatiblePackages.isEmpty ||
patch.compatiblePackages.any(
(pack) =>
pack.name == app.packageName &&
(pack.versions.isEmpty || pack.versions.contains(app.version)),
);
}

View File

@@ -0,0 +1,5 @@
// Dummy environment variables used for building the app locally. These automatically get set with correct values during workflow builds.
class Environment {
static const sentryDSN = '';
static const crowdinKEY = '';
}

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

@@ -1,84 +1,96 @@
name: revanced_manager name: revanced_manager
description: The official ReVanced Manager. description: The official ReVanced Manager.
homepage: https://github.com/revanced/revanced-manager homepage: https://github.com/revanced/revanced-manager
publish_to: none
version: 1.0.0-dev.1+100000100 publish_to: 'none'
version: 0.0.55+55
environment: environment:
sdk: ">=2.17.5 <3.0.0" sdk: ">=2.17.5 <3.0.0"
dependencies: dependencies:
animations: ^2.0.7 sentry_flutter: ^6.12.2
animations: ^2.0.4
app_installer: ^1.1.0 app_installer: ^1.1.0
collection: ^1.17.0 collection: ^1.16.0
cross_connectivity: ^3.0.5 cross_connectivity: ^3.0.5
cr_file_saver: ^0.0.2+1 cr_file_saver: ^0.0.2
device_apps: device_apps:
git: git:
url: https://github.com/ponces/flutter_plugin_device_apps url: https://github.com/ponces/flutter_plugin_device_apps
ref: revanced-manager ref: revanced-manager
device_info_plus: ^8.1.0 device_info_plus: ^4.1.2
dynamic_color: ^1.6.3 dio: ^4.0.6
dio: ^5.0.0 dio_brotli_transformer: ^1.0.1
dio_http_cache_lts: ^0.4.1
dynamic_color: ^1.5.4
dynamic_themes: ^1.1.0 dynamic_themes: ^1.1.0
expandable: ^5.0.1 expandable: ^5.0.1
file_picker: file_picker:
git: git:
url: https://github.com/alexmercerind/flutter_file_picker url: https://github.com/alexmercerind/flutter_file_picker
ref: master ref: master
flex_color_scheme: ^7.0.1 flex_color_scheme: ^6.0.0
flutter: flutter:
sdk: flutter sdk: flutter
flutter_background: ^1.2.0 flutter_background: ^1.1.0
flutter_cache_manager: ^3.3.0 flutter_cache_manager: ^3.3.0
flutter_i18n: ^0.32.4 flutter_i18n: ^0.32.4
flutter_local_notifications: ^13.0.0 flutter_local_notifications: ^9.8.0+1
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
flutter_svg: ^2.0.4 flutter_svg: ^1.1.1+1
fluttertoast: ^8.2.1 fluttertoast: ^8.0.9
font_awesome_flutter: ^10.4.0 font_awesome_flutter: ^10.1.0
get_it: 7.2.0 get_it: ^7.2.0
google_fonts: ^4.0.3 google_fonts: ^3.0.1
http: ^0.13.5 http: ^0.13.4
injectable: ^2.1.1 injectable: ^1.5.3
intl: ^0.17.0 intl: ^0.17.0
json_annotation: ^4.8.0 json_annotation: ^4.6.0
logcat: logcat:
git: git:
url: https://github.com/SuaMusica/logcat url: https://github.com/SuaMusica/logcat
ref: feature/nullSafe ref: feature/nullSafe
package_info_plus: ^3.0.3 native_dio_client: ^0.0.1-dev+1
path_provider: ^2.0.14 package_info_plus: ^1.4.3+1
permission_handler: ^10.2.0 path_provider: ^2.0.11
native_dio_adapter: ^0.1.0 permission_handler: ^10.0.0
pull_to_refresh: ^2.0.0 pull_to_refresh: ^2.0.0
root: root:
git: git:
url: https://github.com/gokul1630/root url: https://github.com/gokul1630/root
ref: main ref: main
share_extend: ^2.0.0 share_extend: ^2.0.0
shared_preferences: ^2.1.0 shared_preferences: ^2.0.15
skeletons: ^0.0.3 skeletons: ^0.0.3
stacked: ^3.2.0 stacked: ^3.0.0
stacked_generator: ^1.1.0 stacked_generator: ^0.8.0
stacked_services: ^1.0.0 stacked_services: ^0.9.3
stacked_themes: ^0.3.10 stacked_themes: ^0.3.9
timeago: ^3.3.0 timeago: ^3.2.2
timezone: ^0.9.0 timezone: ^0.8.0
url_launcher: ^6.1.10 url_launcher: ^6.1.5
wakelock: ^0.6.2 wakelock: ^0.6.2
sentry_dio: ^6.12.2
flutter_dotenv: ^5.0.2 flutter_dotenv: ^5.0.2
flutter_markdown: ^0.6.14
pub_release: ^8.0.3 pub_release: ^8.0.3
dio_cache_interceptor: ^3.4.0 flutter_markdown: ^0.6.13
dev_dependencies: dev_dependencies:
json_serializable: ^6.6.1 json_serializable: ^6.3.1
build_runner: any build_runner: any
flutter_launcher_icons: ^0.13.0 flutter_launcher_icons: ^0.10.0
flutter_lints: ^2.0.1 flutter_lints: ^2.0.1
flutter_test: flutter_test:
sdk: flutter sdk: flutter
injectable_generator: ^2.1.5 injectable_generator: ^1.5.4
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/i18n/ - assets/i18n/
- .env