Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3d8f89d53 | ||
|
|
3d8aee09c3 |
2
.github/config.yaml
vendored
@@ -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.
|
|
||||||
32
.github/workflows/analyze.yml
vendored
@@ -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
@@ -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
|
||||||
2
.github/workflows/crowdin.yml
vendored
@@ -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"
|
||||||
|
|||||||
42
.github/workflows/pull-request-build.yml
vendored
@@ -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
|
||||||
|
|||||||
10
.github/workflows/release-build.yml
vendored
@@ -15,11 +15,15 @@ jobs:
|
|||||||
- name: Set up JDK 12
|
- name: Set up JDK 12
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: "12"
|
java-version: '12'
|
||||||
distribution: "zulu"
|
distribution: 'zulu'
|
||||||
- uses: subosito/flutter-action@v2
|
- uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: 'stable'
|
||||||
|
- name: Set environment variables
|
||||||
|
run: echo $SECRETS | base64 -d > lib/utils/environment.dart
|
||||||
|
env:
|
||||||
|
SECRETS: ${{ secrets.SECRETS }}
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
- name: Generate files with Builder
|
- name: Generate files with Builder
|
||||||
|
|||||||
68
.github/workflows/release.yml
vendored
@@ -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
|
|
||||||
8
.gitignore
vendored
@@ -134,11 +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/
|
|
||||||
|
|
||||||
# FVM
|
|
||||||
.fvm
|
|
||||||
75
.releaserc
@@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -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).
|
||||||
|
|||||||
@@ -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:
|
||||||
@@ -98,6 +95,7 @@ linter:
|
|||||||
- prefer_const_declarations
|
- prefer_const_declarations
|
||||||
- prefer_const_literals_to_create_immutables
|
- prefer_const_literals_to_create_immutables
|
||||||
- prefer_contains
|
- prefer_contains
|
||||||
|
- prefer_equal_for_default_values
|
||||||
- prefer_final_fields
|
- prefer_final_fields
|
||||||
- prefer_final_in_for_each
|
- prefer_final_in_for_each
|
||||||
- prefer_final_locals
|
- prefer_final_locals
|
||||||
|
|||||||
@@ -71,10 +71,14 @@ 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.1.1"
|
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")
|
||||||
implementation("com.android.tools.build:apksig:7.2.2")
|
implementation("com.android.tools.build:apksig:7.2.2")
|
||||||
|
|
||||||
|
// MicroG cronet
|
||||||
|
implementation("org.microg:cronet-common:$cronetVersion")
|
||||||
|
implementation("org.microg:cronet-native:$cronetVersion")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -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>
|
||||||
|
|||||||
4
android/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#1B1B1B</color>
|
||||||
|
</resources>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.7.10'
|
ext.cronetVersion = '102.5005.125'
|
||||||
|
ext.kotlin_version = '1.8.0'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -31,6 +32,6 @@ subprojects {
|
|||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean", Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +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
|
|
||||||
distributionSha256Sum=6147605a23b4eff6c334927a86ff3508cb5d6722cd624c97ded4c2e8640f1f87
|
|
||||||
networkTimeout=10000
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -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 and might fail. 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 application is a split APK and cannot be selected. Unfortunately, this feature is only available for rooted users at the moment. However, you can still install the application by selecting its APK files from your device's storage instead"
|
|
||||||
},
|
},
|
||||||
"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",
|
||||||
|
|||||||
@@ -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),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
63
lib/services/crowdin_api.dart
Normal 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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,22 +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: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',
|
||||||
@@ -38,36 +37,32 @@ class GithubAPI {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,28 +81,24 @@ 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
|
||||||
.map(
|
.map(
|
||||||
(commit) => commit['commit']['message'].split('\n')[0] +
|
(commit) => (commit['commit']['message']).split('\n')[0] +
|
||||||
' - ' +
|
' - ' +
|
||||||
commit['commit']['author']['name'] +
|
commit['commit']['author']['name'] +
|
||||||
'\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 = await getLatestRelease(repoName);
|
final Map<String, dynamic>? release = await getLatestRelease(repoName);
|
||||||
if (release != null) {
|
if (release != null) {
|
||||||
@@ -121,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;
|
||||||
}
|
}
|
||||||
@@ -137,12 +127,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,12 +142,9 @@ class GithubAPI {
|
|||||||
} 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';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -1,64 +1,74 @@
|
|||||||
import 'dart:async';
|
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_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: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 {
|
||||||
try {
|
try {
|
||||||
_dio = Dio(
|
final bool isGMSInstalled = await checkForGMS();
|
||||||
BaseOptions(
|
|
||||||
baseUrl: apiUrl,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
|
if (!isGMSInstalled) {
|
||||||
} on Exception catch (e) {
|
_dio = Dio(
|
||||||
if (kDebugMode) {
|
BaseOptions(
|
||||||
print(e);
|
baseUrl: apiUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
log('ReVanced API: Using default engine + $isGMSInstalled');
|
||||||
|
} else {
|
||||||
|
_dio = Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: apiUrl,
|
||||||
|
),
|
||||||
|
)..httpClientAdapter = NativeAdapter();
|
||||||
|
log('ReVanced API: Using CronetEngine + $isGMSInstalled');
|
||||||
}
|
}
|
||||||
|
_dio.interceptors.add(_dioCacheManager.interceptor);
|
||||||
|
_dio.addSentry(
|
||||||
|
captureFailedRequests: true,
|
||||||
|
);
|
||||||
|
} 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;
|
||||||
@@ -66,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,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,
|
||||||
@@ -131,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,
|
||||||
@@ -191,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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart' hide SearchBar;
|
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/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,59 +76,30 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: model.noApps
|
child: model.noApps
|
||||||
? Center(
|
? Center(
|
||||||
child: I18nText(
|
child: I18nText('appSelectorCard.noAppsLabel'),
|
||||||
'appSelectorView.noAppsLabel',
|
|
||||||
child: Text(
|
|
||||||
'',
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
Theme.of(context).textTheme.titleLarge!.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
: model.apps.isEmpty
|
: model.apps.isEmpty
|
||||||
? const AppSkeletonLoader()
|
? const AppSkeletonLoader()
|
||||||
: Padding(
|
: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
||||||
.copyWith(
|
.copyWith(bottom: 80),
|
||||||
bottom:
|
|
||||||
MediaQuery.of(context).viewPadding.bottom + 8.0,
|
|
||||||
),
|
|
||||||
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.canSelectInstalled(context, app.packageName),
|
)
|
||||||
),
|
.toList(),
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
...model
|
|
||||||
.getFilteredAppsNames(_query)
|
|
||||||
.map(
|
|
||||||
(app) => NotInstalledAppItem(
|
|
||||||
name: app,
|
|
||||||
patchesCount: model.patchesCount(app),
|
|
||||||
suggestedVersion:
|
|
||||||
model.getSuggestedVersion(app),
|
|
||||||
onTap: () {
|
|
||||||
model.showDownloadToast();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,33 +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<bool> checkSplitApk(String packageName) async {
|
|
||||||
final app = await DeviceApps.getApp(packageName);
|
|
||||||
if (app != null) {
|
|
||||||
return app.isSplit;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
||||||
@@ -86,95 +50,6 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
locator<PatcherViewModel>().loadLastSelectedPatches();
|
locator<PatcherViewModel>().loadLastSelectedPatches();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> canSelectInstalled(
|
|
||||||
BuildContext context,
|
|
||||||
String packageName,
|
|
||||||
) async {
|
|
||||||
final app =
|
|
||||||
await DeviceApps.getApp(packageName, true) as ApplicationWithIcon?;
|
|
||||||
if (app != null) {
|
|
||||||
if (await checkSplitApk(packageName) && !isRooted) {
|
|
||||||
return showSelectFromStorageDialog(context);
|
|
||||||
} else if (!await checkSplitApk(packageName) || isRooted) {
|
|
||||||
selectApp(app);
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
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(
|
||||||
@@ -212,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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,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');
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -57,7 +57,6 @@ class ContributorsView extends StatelessWidget {
|
|||||||
title: 'contributorsView.managerContributors',
|
title: 'contributorsView.managerContributors',
|
||||||
contributors: model.managerContributors,
|
contributors: model.managerContributors,
|
||||||
),
|
),
|
||||||
SizedBox(height: MediaQuery.of(context).viewPadding.bottom)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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,32 +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? _latestManagerVersion = '';
|
|
||||||
|
|
||||||
Future<void> initialize(BuildContext context) async {
|
Future<void> initialize(BuildContext context) async {
|
||||||
_latestManagerVersion = await _managerAPI.getLatestManagerVersion();
|
|
||||||
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<
|
||||||
@@ -67,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());
|
||||||
}
|
}
|
||||||
@@ -112,157 +86,59 @@ class HomeViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> hasManagerUpdates() async {
|
Future<bool> hasManagerUpdates() async {
|
||||||
String currentVersion = await _managerAPI.getCurrentManagerVersion();
|
final String? latestVersion = await _managerAPI.getLatestManagerVersion();
|
||||||
|
final String currentVersion = await _managerAPI.getCurrentManagerVersion();
|
||||||
// add v to current version
|
|
||||||
if (!currentVersion.startsWith('v')) {
|
|
||||||
currentVersion = 'v$currentVersion';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_latestManagerVersion != 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$_latestManagerVersion',
|
|
||||||
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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,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,
|
||||||
@@ -302,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class InstallerView extends StatelessWidget {
|
|||||||
builder: (context, model, child) => WillPopScope(
|
builder: (context, model, child) => WillPopScope(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
controller: model.scrollController,
|
controller: model.scrollController,
|
||||||
@@ -29,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),
|
||||||
@@ -154,11 +153,6 @@ class InstallerView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverFillRemaining(
|
|
||||||
hasScrollBody: false,
|
|
||||||
child: SizedBox(
|
|
||||||
height: MediaQuery.of(context).viewPadding.bottom),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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: '',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,57 +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) {
|
|
||||||
final List<String> archs = info['arch'];
|
|
||||||
final supportedAbis = ['arm64-v8a', 'x86', 'x86_64'];
|
|
||||||
return !archs.any((arch) => supportedAbis.contains(arch));
|
|
||||||
});
|
|
||||||
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();
|
||||||
@@ -114,8 +77,6 @@ class PatcherViewModel extends BaseViewModel {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
navigateToInstaller();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,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 {
|
||||||
@@ -153,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart' hide SearchBar;
|
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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -129,32 +130,33 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Padding(
|
: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
||||||
const EdgeInsets.symmetric(horizontal: 12.0).copyWith(
|
.copyWith(bottom: 80),
|
||||||
bottom: MediaQuery.of(context).viewPadding.bottom + 8.0,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
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();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -171,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),
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -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();
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
),
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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>[
|
||||||
const Row(
|
I18nText('latestCommitCard.patcherLabel'),
|
||||||
children: <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>[
|
||||||
const Row(
|
I18nText('latestCommitCard.managerLabel'),
|
||||||
children: <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
|
||||||
|
: () => {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
36
lib/ui/widgets/settingsView/settings_logging_section.dart
Normal 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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,9 +39,9 @@ class SocialMediaWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
expanded: Padding(
|
expanded: Padding(
|
||||||
padding: padding ?? EdgeInsets.zero,
|
padding: padding ?? EdgeInsets.zero,
|
||||||
child: const CustomCard(
|
child: CustomCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: const <Widget>[
|
||||||
SocialMediaItem(
|
SocialMediaItem(
|
||||||
icon: FaIcon(FontAwesomeIcons.github),
|
icon: FaIcon(FontAwesomeIcons.github),
|
||||||
title: Text('GitHub'),
|
title: Text('GitHub'),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
lib/utils/check_for_gms.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Check for google mobile services on device
|
||||||
|
|
||||||
|
import 'package:device_apps/device_apps.dart';
|
||||||
|
|
||||||
|
Future<bool> checkForGMS() async {
|
||||||
|
bool isGMSInstalled = true;
|
||||||
|
isGMSInstalled = await DeviceApps.isAppInstalled('com.google.android.gms') ||
|
||||||
|
await DeviceApps.isAppInstalled('com.android.vending');
|
||||||
|
return isGMSInstalled;
|
||||||
|
}
|
||||||
@@ -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)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
5
lib/utils/environment.dart
Normal 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
10
package.json
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
83
pubspec.yaml
@@ -4,86 +4,88 @@ homepage: https://github.com/revanced/revanced-manager
|
|||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.2.0+100200000
|
version: 0.0.55+55
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.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:
|
cr_file_saver: ^0.0.2
|
||||||
git:
|
|
||||||
url: https://github.com/dhruvanbhalara/cr_file_saver
|
|
||||||
ref: "fix/incorrect_file_name"
|
|
||||||
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.33.0
|
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.18.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
|
||||||
|
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
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -91,3 +93,4 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
- assets/i18n/
|
- assets/i18n/
|
||||||
|
- .env
|
||||||
|
|||||||