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.
|
|
||||||
27
.github/workflows/analyze.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
name: Analyze Code
|
|
||||||
|
|
||||||
on:
|
|
||||||
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"
|
||||||
|
|||||||
82
.github/workflows/pull-request-build.yml
vendored
@@ -1,41 +1,41 @@
|
|||||||
name: PR Build
|
name: "Android CI PR Build"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
pull_request:
|
||||||
|
branches:
|
||||||
jobs:
|
- "**"
|
||||||
build:
|
|
||||||
name: Build
|
jobs:
|
||||||
runs-on: ubuntu-latest
|
release:
|
||||||
steps:
|
runs-on: ubuntu-latest
|
||||||
- name: Checkout
|
steps:
|
||||||
uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
- name: Set env
|
||||||
# Make sure the release step uses its own credentials:
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
- name: Set up JDK 12
|
||||||
persist-credentials: false
|
uses: actions/setup-java@v3
|
||||||
fetch-depth: 0
|
with:
|
||||||
- name: Setup JDK
|
java-version: '12'
|
||||||
uses: actions/setup-java@v3
|
distribution: 'zulu'
|
||||||
with:
|
- uses: subosito/flutter-action@v2
|
||||||
java-version: '17'
|
with:
|
||||||
distribution: 'zulu'
|
channel: 'stable'
|
||||||
cache: 'gradle'
|
- name: Set environment variables
|
||||||
- name: Setup Flutter
|
run: echo $SECRETS | base64 -d > lib/utils/environment.dart
|
||||||
uses: subosito/flutter-action@v2
|
env:
|
||||||
with:
|
SECRETS: ${{ secrets.SECRETS }}
|
||||||
channel: 'stable'
|
- name: Set up Flutter
|
||||||
cache: true
|
run: flutter pub get
|
||||||
- name: Install Flutter dependencies
|
- name: Generate files with Builder
|
||||||
run: flutter pub get
|
run: flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
- name: Generate files with Builder
|
- name: Build with Flutter
|
||||||
run: flutter packages pub run build_runner build --delete-conflicting-outputs
|
env:
|
||||||
- name: Build with Flutter
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
env:
|
run: flutter build apk
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
- name: Add version to APK
|
||||||
run: flutter build apk
|
run: mv build/app/outputs/flutter-apk/app-release.apk revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
||||||
- name: Upload build
|
- 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
|
||||||
|
|||||||
12
.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
|
||||||
@@ -47,4 +51,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
files: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
||||||
|
|||||||
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
|
|
||||||
19
.github/workflows/update-documentation.yml
vendored
@@ -1,19 +0,0 @@
|
|||||||
name: Update documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- docs/**
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
trigger:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Dispatch event to documentation repository
|
|
||||||
if: github.ref == 'refs/heads/main'
|
|
||||||
steps:
|
|
||||||
- uses: peter-evans/repository-dispatch@v2
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
|
|
||||||
repository: revanced/revanced-documentation
|
|
||||||
event-type: update-documentation
|
|
||||||
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'
|
|
||||||
10
.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 @@
|
|||||||
|
|
||||||
10
README.md
@@ -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).
|
||||||
@@ -24,7 +30,7 @@ If you wish to translate ReVanced Manager, we're accepting translations on [Crow
|
|||||||
## 🛠️ Building Manager from source
|
## 🛠️ Building Manager from source
|
||||||
1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
|
1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
|
||||||
2. Clone the repository locally
|
2. Clone the repository locally
|
||||||
3. Add your github token in gradle.properties like [this](/docs/4_building.md)
|
3. Add your github token in gradle.properties like [this](https://github.com/revanced/revanced-manager/blob/docs/docs/5_building-from-source.md)
|
||||||
4. Open the project in terminal
|
4. Open the project in terminal
|
||||||
5. Run `flutter pub get` in terminal
|
5. Run `flutter pub get` in terminal
|
||||||
6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull)
|
6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
source "https://rubygems.org"
|
|
||||||
|
|
||||||
gem "fastlane"
|
|
||||||
@@ -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:11.0.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")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
|||||||
@@ -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}")
|
||||||
@@ -271,8 +272,8 @@ class MainActivity : FlutterActivity() {
|
|||||||
"update",
|
"update",
|
||||||
mapOf(
|
mapOf(
|
||||||
"progress" to -100.0,
|
"progress" to -100.0,
|
||||||
"header" to "Aborted...",
|
"header" to "Aborting...",
|
||||||
"log" to "An error occurred! Aborted\nError:\n$stack"
|
"log" to "An error occurred! Aborting\nError:\n$stack"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 814 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,30 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="256"
|
|
||||||
android:viewportHeight="256">
|
|
||||||
<group android:scaleX="0.23"
|
|
||||||
android:scaleY="0.23"
|
|
||||||
android:translateX="98.56"
|
|
||||||
android:translateY="98.56">
|
|
||||||
<path
|
|
||||||
android:pathData="M253.85,4.9C254.32,3.82 254.22,2.57 253.58,1.58C252.93,0.6 251.83,0 250.64,0C243.29,0 230.47,0 225.95,0C224.96,0 224.06,0.59 223.66,1.5C216.03,18.88 144.1,182.7 130.29,214.16C129.89,215.07 128.99,215.66 128,215.66C127.01,215.66 126.11,215.07 125.71,214.16C111.9,182.7 39.97,18.88 32.34,1.5C31.94,0.59 31.04,0 30.05,0C25.53,0 12.71,0 5.36,0C4.17,0 3.07,0.6 2.42,1.58C1.78,2.57 1.68,3.82 2.15,4.9C16.78,38.3 101.47,231.61 111.24,253.9C111.8,255.18 113.06,256 114.45,256C120.29,256 135.71,256 141.55,256C142.94,256 144.2,255.18 144.76,253.9C154.52,231.61 239.22,38.3 253.85,4.9Z"
|
|
||||||
android:fillColor="#ffffff"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M130.59,131.75C130.06,132.68 129.07,133.25 128,133.25C126.93,133.25 125.94,132.68 125.4,131.75C113.45,111.06 63.88,25.19 51.93,4.5C51.4,3.57 51.4,2.43 51.93,1.5C52.47,0.57 53.46,-0 54.53,-0L201.47,-0C202.54,-0 203.53,0.57 204.06,1.5C204.6,2.43 204.6,3.57 204.06,4.5C192.12,25.19 142.54,111.06 130.59,131.75Z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:startX="128"
|
|
||||||
android:startY="-0"
|
|
||||||
android:endX="128"
|
|
||||||
android:endY="254.6"
|
|
||||||
android:type="linear">
|
|
||||||
<item android:offset="0" android:color="#FFF04E98"/>
|
|
||||||
<item android:offset="0.5" android:color="#FF5F65D4"/>
|
|
||||||
<item android:offset="1" android:color="#FF4E98F0"/>
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 13 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="@color/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_foreground"/>
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#1B1B1B</color>
|
<color name="ic_launcher_background">#1B1B1B</color>
|
||||||
</resources>
|
</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
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "لم يتم اختيار أي تطبيق",
|
"widgetSubtitle": "لم يتم اختيار أي تطبيق",
|
||||||
"noAppsLabel": "لم يتم العثور على تطبيقات",
|
"noAppsLabel": "لم يتم العثور على تطبيقات",
|
||||||
"currentVersion": "الحالي",
|
"currentVersion": "الحالي",
|
||||||
"recommendedVersion": "موصى به"
|
"recommendedVersion": "موصى به",
|
||||||
|
"anyVersion": "أي"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "اختر التعديلات",
|
"widgetTitle": "اختر التعديلات",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Heç bir tətbiq seçilməyib",
|
"widgetSubtitle": "Heç bir tətbiq seçilməyib",
|
||||||
"noAppsLabel": "Heç bir tətbiq tapılmadı",
|
"noAppsLabel": "Heç bir tətbiq tapılmadı",
|
||||||
"currentVersion": "Hazırkı",
|
"currentVersion": "Hazırkı",
|
||||||
"recommendedVersion": "Tövsiyyə edilən"
|
"recommendedVersion": "Tövsiyyə edilən",
|
||||||
|
"anyVersion": "istənilən"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Yamaqları seçin",
|
"widgetTitle": "Yamaqları seçin",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Няма избрано приложение",
|
"widgetSubtitle": "Няма избрано приложение",
|
||||||
"noAppsLabel": "Няма намерени приложения",
|
"noAppsLabel": "Няма намерени приложения",
|
||||||
"currentVersion": "Текуща",
|
"currentVersion": "Текуща",
|
||||||
"recommendedVersion": "Препоръчана"
|
"recommendedVersion": "Препоръчана",
|
||||||
|
"anyVersion": "всяка"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Изберете модификации",
|
"widgetTitle": "Изберете модификации",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "কোনো অ্যাপ্লিকেশন নির্বাচন করা হয়নি",
|
"widgetSubtitle": "কোনো অ্যাপ্লিকেশন নির্বাচন করা হয়নি",
|
||||||
"noAppsLabel": "কোন অ্যাপ্লিকেশন পাওয়া যায়নি",
|
"noAppsLabel": "কোন অ্যাপ্লিকেশন পাওয়া যায়নি",
|
||||||
"currentVersion": "বর্তমান",
|
"currentVersion": "বর্তমান",
|
||||||
"recommendedVersion": "প্রস্তাবিত"
|
"recommendedVersion": "প্রস্তাবিত",
|
||||||
|
"anyVersion": "যেকোনো সংস্করণ"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "প্যাচ নির্বাচন করুন",
|
"widgetTitle": "প্যাচ নির্বাচন করুন",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Nebyla vybrána žádná aplikace",
|
"widgetSubtitle": "Nebyla vybrána žádná aplikace",
|
||||||
"noAppsLabel": "Nebyly nalezeny žádné aplikace",
|
"noAppsLabel": "Nebyly nalezeny žádné aplikace",
|
||||||
"currentVersion": "Aktuální",
|
"currentVersion": "Aktuální",
|
||||||
"recommendedVersion": "Doporučeno"
|
"recommendedVersion": "Doporučeno",
|
||||||
|
"anyVersion": "jakákoliv"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Vybrat záplaty",
|
"widgetTitle": "Vybrat záplaty",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Ingen applikation valgt",
|
"widgetSubtitle": "Ingen applikation valgt",
|
||||||
"noAppsLabel": "Ingen applikationer fundet",
|
"noAppsLabel": "Ingen applikationer fundet",
|
||||||
"currentVersion": "Nuværende",
|
"currentVersion": "Nuværende",
|
||||||
"recommendedVersion": "Anbefalet"
|
"recommendedVersion": "Anbefalet",
|
||||||
|
"anyVersion": "enhver"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Vælg patches",
|
"widgetTitle": "Vælg patches",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Keine App ausgewählt",
|
"widgetSubtitle": "Keine App ausgewählt",
|
||||||
"noAppsLabel": "Keine Apps gefunden",
|
"noAppsLabel": "Keine Apps gefunden",
|
||||||
"currentVersion": "Aktuell",
|
"currentVersion": "Aktuell",
|
||||||
"recommendedVersion": "Empfohlen"
|
"recommendedVersion": "Empfohlen",
|
||||||
|
"anyVersion": "beliebig"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Patches auswählen",
|
"widgetTitle": "Patches auswählen",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Δεν έχει επιλεχθεί εφαρμογή",
|
"widgetSubtitle": "Δεν έχει επιλεχθεί εφαρμογή",
|
||||||
"noAppsLabel": "Δεν βρέθηκαν εφαρμογές",
|
"noAppsLabel": "Δεν βρέθηκαν εφαρμογές",
|
||||||
"currentVersion": "Τρέχουσα",
|
"currentVersion": "Τρέχουσα",
|
||||||
"recommendedVersion": "Προτεινόμενη"
|
"recommendedVersion": "Προτεινόμενη",
|
||||||
|
"anyVersion": "οποιαδήποτε"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Επιλέξτε τροποποιήσεις",
|
"widgetTitle": "Επιλέξτε τροποποιήσεις",
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
"updateButton": "Update",
|
"updateButton": "Update",
|
||||||
"enabledLabel": "Enabled",
|
"enabledLabel": "Enabled",
|
||||||
"disabledLabel": "Disabled",
|
"disabledLabel": "Disabled",
|
||||||
"installed":"Installed: {version}",
|
|
||||||
"suggested":"Suggested: {version}",
|
|
||||||
"yesButton": "Yes",
|
"yesButton": "Yes",
|
||||||
"noButton": "No",
|
"noButton": "No",
|
||||||
"warning": "Warning",
|
"warning": "Warning",
|
||||||
@@ -15,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",
|
||||||
@@ -24,13 +21,11 @@
|
|||||||
"WIP": "Work in progress...",
|
"WIP": "Work in progress...",
|
||||||
"noInstallations": "No patched applications installed",
|
"noInstallations": "No patched applications installed",
|
||||||
"installed": "Installed",
|
"installed": "Installed",
|
||||||
"installUpdate": "Continue to install the update?",
|
|
||||||
"updateDialogTitle": "Update Manager",
|
"updateDialogTitle": "Update Manager",
|
||||||
"updateChangelogTitle": "Changelog",
|
"updateChangelogTitle": "Changelog",
|
||||||
"notificationTitle": "Update downloaded",
|
"notificationTitle": "Update downloaded",
|
||||||
"notificationText": "Tap to install the update",
|
"notificationText": "Tap to install the update",
|
||||||
"downloadingMessage": "Downloading update...",
|
"downloadingMessage": "Downloading update...",
|
||||||
"downloadedMessage": "Update downloaded!",
|
|
||||||
"installingMessage": "Installing update...",
|
"installingMessage": "Installing update...",
|
||||||
"errorDownloadMessage": "Unable to download update",
|
"errorDownloadMessage": "Unable to download update",
|
||||||
"errorInstallMessage": "Unable to install update",
|
"errorInstallMessage": "Unable to install update",
|
||||||
@@ -52,18 +47,16 @@
|
|||||||
"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",
|
||||||
"widgetTitleSelected": "Selected application",
|
"widgetTitleSelected": "Selected application",
|
||||||
"widgetSubtitle": "No application selected",
|
"widgetSubtitle": "No application selected",
|
||||||
"noAppsLabel": "No applications found",
|
"noAppsLabel": "No applications found",
|
||||||
"notInstalled":"App not installed",
|
|
||||||
"currentVersion": "Current",
|
"currentVersion": "Current",
|
||||||
"suggestedVersion": "Suggested",
|
"recommendedVersion": "Recommended",
|
||||||
"allVersions": "All versions"
|
"anyVersion": "any"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Select patches",
|
"widgetTitle": "Select patches",
|
||||||
@@ -78,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}",
|
||||||
@@ -157,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",
|
||||||
@@ -178,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",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Ninguna aplicación seleccionada",
|
"widgetSubtitle": "Ninguna aplicación seleccionada",
|
||||||
"noAppsLabel": "No se encontraron aplicaciones",
|
"noAppsLabel": "No se encontraron aplicaciones",
|
||||||
"currentVersion": "Versión actual",
|
"currentVersion": "Versión actual",
|
||||||
"recommendedVersion": "Versión recomendada"
|
"recommendedVersion": "Versión recomendada",
|
||||||
|
"anyVersion": "cualquiera"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Seleccionar parches",
|
"widgetTitle": "Seleccionar parches",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Ninguna aplicación seleccionada",
|
"widgetSubtitle": "Ninguna aplicación seleccionada",
|
||||||
"noAppsLabel": "No se encontraron aplicaciones",
|
"noAppsLabel": "No se encontraron aplicaciones",
|
||||||
"currentVersion": "Versión actual",
|
"currentVersion": "Versión actual",
|
||||||
"recommendedVersion": "Versión recomendada"
|
"recommendedVersion": "Versión recomendada",
|
||||||
|
"anyVersion": "Cualquier versión"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Seleccionar parches",
|
"widgetTitle": "Seleccionar parches",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Sovellusta ei ole valittu",
|
"widgetSubtitle": "Sovellusta ei ole valittu",
|
||||||
"noAppsLabel": "Sovelluksia ei löytynyt",
|
"noAppsLabel": "Sovelluksia ei löytynyt",
|
||||||
"currentVersion": "Nykyinen",
|
"currentVersion": "Nykyinen",
|
||||||
"recommendedVersion": "Suositeltu"
|
"recommendedVersion": "Suositeltu",
|
||||||
|
"anyVersion": "mikä tahansa"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Valitse paikkaukset",
|
"widgetTitle": "Valitse paikkaukset",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Aucune application sélectionnée",
|
"widgetSubtitle": "Aucune application sélectionnée",
|
||||||
"noAppsLabel": "Aucune application trouvée",
|
"noAppsLabel": "Aucune application trouvée",
|
||||||
"currentVersion": "Version actuelle",
|
"currentVersion": "Version actuelle",
|
||||||
"recommendedVersion": "Version recommandée"
|
"recommendedVersion": "Version recommandée",
|
||||||
|
"anyVersion": "n'importe laquelle"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Sélectionner les patchs",
|
"widgetTitle": "Sélectionner les patchs",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "कोई ऐप्लकैशन चुना हुआ नहीं",
|
"widgetSubtitle": "कोई ऐप्लकैशन चुना हुआ नहीं",
|
||||||
"noAppsLabel": "कोई एप्लिकेशन नहीं मिला।",
|
"noAppsLabel": "कोई एप्लिकेशन नहीं मिला।",
|
||||||
"currentVersion": "वर्तमान",
|
"currentVersion": "वर्तमान",
|
||||||
"recommendedVersion": "अनुशंसित"
|
"recommendedVersion": "अनुशंसित",
|
||||||
|
"anyVersion": "कोई"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "पैच चुने",
|
"widgetTitle": "पैच चुने",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Nincs kiválasztott alkalmazás",
|
"widgetSubtitle": "Nincs kiválasztott alkalmazás",
|
||||||
"noAppsLabel": "Nem találhatóak alkalmazások",
|
"noAppsLabel": "Nem találhatóak alkalmazások",
|
||||||
"currentVersion": "Jelenlegi",
|
"currentVersion": "Jelenlegi",
|
||||||
"recommendedVersion": "Ajánlott"
|
"recommendedVersion": "Ajánlott",
|
||||||
|
"anyVersion": "bármely"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Patchek kiválasztása",
|
"widgetTitle": "Patchek kiválasztása",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Tidak ada aplikasi yang dipilih",
|
"widgetSubtitle": "Tidak ada aplikasi yang dipilih",
|
||||||
"noAppsLabel": "Aplikasi tidak ditemukan",
|
"noAppsLabel": "Aplikasi tidak ditemukan",
|
||||||
"currentVersion": "Saat ini",
|
"currentVersion": "Saat ini",
|
||||||
"recommendedVersion": "Rekomendasi"
|
"recommendedVersion": "Rekomendasi",
|
||||||
|
"anyVersion": "semua"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Pilih Modifikasi",
|
"widgetTitle": "Pilih Modifikasi",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nessuna applicazione selezionata",
|
"widgetSubtitle": "Nessuna applicazione selezionata",
|
||||||
"noAppsLabel": "Nessuna applicazione trovata",
|
"noAppsLabel": "Nessuna applicazione trovata",
|
||||||
"currentVersion": "Attuale",
|
"currentVersion": "Attuale",
|
||||||
"recommendedVersion": "Consigliata"
|
"recommendedVersion": "Consigliata",
|
||||||
|
"anyVersion": "qualsiasi"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Seleziona patch",
|
"widgetTitle": "Seleziona patch",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "선택된 앱 없음",
|
"widgetSubtitle": "선택된 앱 없음",
|
||||||
"noAppsLabel": "앱이 발견되지 않음",
|
"noAppsLabel": "앱이 발견되지 않음",
|
||||||
"currentVersion": "현재 버전",
|
"currentVersion": "현재 버전",
|
||||||
"recommendedVersion": "권장 버전"
|
"recommendedVersion": "권장 버전",
|
||||||
|
"anyVersion": "모든 버전"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "패치를 선택하세요",
|
"widgetTitle": "패치를 선택하세요",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nepasirinkta programa",
|
"widgetSubtitle": "Nepasirinkta programa",
|
||||||
"noAppsLabel": "Nerastos jokios programos",
|
"noAppsLabel": "Nerastos jokios programos",
|
||||||
"currentVersion": "Dabartinė",
|
"currentVersion": "Dabartinė",
|
||||||
"recommendedVersion": "Rekomenduojama"
|
"recommendedVersion": "Rekomenduojama",
|
||||||
|
"anyVersion": "bet kokia"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Pasirinkite modifikacijas",
|
"widgetTitle": "Pasirinkite modifikacijas",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Tiada aplikasi dipilih",
|
"widgetSubtitle": "Tiada aplikasi dipilih",
|
||||||
"noAppsLabel": "Tiada aplikasi ditemui",
|
"noAppsLabel": "Tiada aplikasi ditemui",
|
||||||
"currentVersion": "Semasa",
|
"currentVersion": "Semasa",
|
||||||
"recommendedVersion": "Disyorkan"
|
"recommendedVersion": "Disyorkan",
|
||||||
|
"anyVersion": "mana-mana"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Pilih modifikasi",
|
"widgetTitle": "Pilih modifikasi",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nie wybrano aplikacji",
|
"widgetSubtitle": "Nie wybrano aplikacji",
|
||||||
"noAppsLabel": "Nie znaleziono aplikacji",
|
"noAppsLabel": "Nie znaleziono aplikacji",
|
||||||
"currentVersion": "Aktualna",
|
"currentVersion": "Aktualna",
|
||||||
"recommendedVersion": "Zalecana"
|
"recommendedVersion": "Zalecana",
|
||||||
|
"anyVersion": "dowolna"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Wybierz łatki",
|
"widgetTitle": "Wybierz łatki",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Nenhum aplicativo selecionado",
|
"widgetSubtitle": "Nenhum aplicativo selecionado",
|
||||||
"noAppsLabel": "Nenhum app foi encontrado",
|
"noAppsLabel": "Nenhum app foi encontrado",
|
||||||
"currentVersion": "Atual",
|
"currentVersion": "Atual",
|
||||||
"recommendedVersion": "Recomendado"
|
"recommendedVersion": "Recomendado",
|
||||||
|
"anyVersion": "todos"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Selecionar patches",
|
"widgetTitle": "Selecionar patches",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nenhuma aplicação selecionada",
|
"widgetSubtitle": "Nenhuma aplicação selecionada",
|
||||||
"noAppsLabel": "Não foram encontradas aplicações",
|
"noAppsLabel": "Não foram encontradas aplicações",
|
||||||
"currentVersion": "Atual",
|
"currentVersion": "Atual",
|
||||||
"recommendedVersion": "Recomendada"
|
"recommendedVersion": "Recomendada",
|
||||||
|
"anyVersion": "qualquer"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Selecionar modificações",
|
"widgetTitle": "Selecionar modificações",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nicio aplicație selectată",
|
"widgetSubtitle": "Nicio aplicație selectată",
|
||||||
"noAppsLabel": "Nu s-au găsit aplicații",
|
"noAppsLabel": "Nu s-au găsit aplicații",
|
||||||
"currentVersion": "Actual",
|
"currentVersion": "Actual",
|
||||||
"recommendedVersion": "Recomandat"
|
"recommendedVersion": "Recomandat",
|
||||||
|
"anyVersion": "oricare"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Selectați patch-urile",
|
"widgetTitle": "Selectați patch-urile",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Не выбрано приложение",
|
"widgetSubtitle": "Не выбрано приложение",
|
||||||
"noAppsLabel": "Приложения не найдены",
|
"noAppsLabel": "Приложения не найдены",
|
||||||
"currentVersion": "Текущая",
|
"currentVersion": "Текущая",
|
||||||
"recommendedVersion": "Рекомендуемая"
|
"recommendedVersion": "Рекомендуемая",
|
||||||
|
"anyVersion": "любая"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Выберите патчи",
|
"widgetTitle": "Выберите патчи",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Nie je vybraná žiadna aplikácia",
|
"widgetSubtitle": "Nie je vybraná žiadna aplikácia",
|
||||||
"noAppsLabel": "Nebola nájdená žiadna aplikácia",
|
"noAppsLabel": "Nebola nájdená žiadna aplikácia",
|
||||||
"currentVersion": "Aktuálne",
|
"currentVersion": "Aktuálne",
|
||||||
"recommendedVersion": "Odporúčané"
|
"recommendedVersion": "Odporúčané",
|
||||||
|
"anyVersion": "akákoľvek"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Vyberte záplaty",
|
"widgetTitle": "Vyberte záplaty",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Nijedna aplikacija nije izabrana",
|
"widgetSubtitle": "Nijedna aplikacija nije izabrana",
|
||||||
"noAppsLabel": "Nijedna aplikacija nije pronađena",
|
"noAppsLabel": "Nijedna aplikacija nije pronađena",
|
||||||
"currentVersion": "Trenutna",
|
"currentVersion": "Trenutna",
|
||||||
"recommendedVersion": "Preporučena"
|
"recommendedVersion": "Preporučena",
|
||||||
|
"anyVersion": "bilo koja"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Izaberite pečeve",
|
"widgetTitle": "Izaberite pečeve",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Ниједна апликација није изабрана",
|
"widgetSubtitle": "Ниједна апликација није изабрана",
|
||||||
"noAppsLabel": "Ниједна апликација није пронађена",
|
"noAppsLabel": "Ниједна апликација није пронађена",
|
||||||
"currentVersion": "Тренутна",
|
"currentVersion": "Тренутна",
|
||||||
"recommendedVersion": "Препоручена"
|
"recommendedVersion": "Препоручена",
|
||||||
|
"anyVersion": "било која"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Изаберите печеве",
|
"widgetTitle": "Изаберите печеве",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "Ingen app vald",
|
"widgetSubtitle": "Ingen app vald",
|
||||||
"noAppsLabel": "Inga applikationer hittades",
|
"noAppsLabel": "Inga applikationer hittades",
|
||||||
"currentVersion": "Nuvarande",
|
"currentVersion": "Nuvarande",
|
||||||
"recommendedVersion": "Rekommenderad"
|
"recommendedVersion": "Rekommenderad",
|
||||||
|
"anyVersion": "alla"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Välj patchar",
|
"widgetTitle": "Välj patchar",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Hakuna programu iliyochaguliwa",
|
"widgetSubtitle": "Hakuna programu iliyochaguliwa",
|
||||||
"noAppsLabel": "Hakuna programu zilizopatikana",
|
"noAppsLabel": "Hakuna programu zilizopatikana",
|
||||||
"currentVersion": "Sasa",
|
"currentVersion": "Sasa",
|
||||||
"recommendedVersion": "Imependekezwa"
|
"recommendedVersion": "Imependekezwa",
|
||||||
|
"anyVersion": "yoyote"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Chagua viraka",
|
"widgetTitle": "Chagua viraka",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Hiçbir uygulama seçilmedi",
|
"widgetSubtitle": "Hiçbir uygulama seçilmedi",
|
||||||
"noAppsLabel": "Hiçbir uygulama bulunamadı",
|
"noAppsLabel": "Hiçbir uygulama bulunamadı",
|
||||||
"currentVersion": "Şu anki",
|
"currentVersion": "Şu anki",
|
||||||
"recommendedVersion": "Önerilen"
|
"recommendedVersion": "Önerilen",
|
||||||
|
"anyVersion": "herhangi"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Yamaları Seçin",
|
"widgetTitle": "Yamaları Seçin",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "کوئی ایپلیکیشن منتخب نہیں",
|
"widgetSubtitle": "کوئی ایپلیکیشن منتخب نہیں",
|
||||||
"noAppsLabel": "کوئی ایپلیکیشن نہیں ملی",
|
"noAppsLabel": "کوئی ایپلیکیشن نہیں ملی",
|
||||||
"currentVersion": "موجودہ",
|
"currentVersion": "موجودہ",
|
||||||
"recommendedVersion": "تجویز کردہ"
|
"recommendedVersion": "تجویز کردہ",
|
||||||
|
"anyVersion": "کوئی بھی"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "پیچز منتخب کریں",
|
"widgetTitle": "پیچز منتخب کریں",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "Chưa chọn ứng dụng nào",
|
"widgetSubtitle": "Chưa chọn ứng dụng nào",
|
||||||
"noAppsLabel": "Không tìm thấy ứng dụng nào",
|
"noAppsLabel": "Không tìm thấy ứng dụng nào",
|
||||||
"currentVersion": "Hiện tại",
|
"currentVersion": "Hiện tại",
|
||||||
"recommendedVersion": "Đề xuất"
|
"recommendedVersion": "Đề xuất",
|
||||||
|
"anyVersion": "bất kỳ"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Chọn bản vá",
|
"widgetTitle": "Chọn bản vá",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "未选择应用程序",
|
"widgetSubtitle": "未选择应用程序",
|
||||||
"noAppsLabel": "未发现应用程序。",
|
"noAppsLabel": "未发现应用程序。",
|
||||||
"currentVersion": "当前",
|
"currentVersion": "当前",
|
||||||
"recommendedVersion": "推荐"
|
"recommendedVersion": "推荐",
|
||||||
|
"anyVersion": "任意"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "选择补丁",
|
"widgetTitle": "选择补丁",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
"widgetSubtitle": "沒有選擇應用程式",
|
"widgetSubtitle": "沒有選擇應用程式",
|
||||||
"noAppsLabel": "找不到應用程式",
|
"noAppsLabel": "找不到應用程式",
|
||||||
"currentVersion": "目前版本",
|
"currentVersion": "目前版本",
|
||||||
"recommendedVersion": "推薦版本"
|
"recommendedVersion": "推薦版本",
|
||||||
|
"anyVersion": "任何版本"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "選擇補丁",
|
"widgetTitle": "選擇補丁",
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"widgetSubtitle": "未選取應用程式",
|
"widgetSubtitle": "未選取應用程式",
|
||||||
"noAppsLabel": "找不到應用程式",
|
"noAppsLabel": "找不到應用程式",
|
||||||
"currentVersion": "目前",
|
"currentVersion": "目前",
|
||||||
"recommendedVersion": "建議"
|
"recommendedVersion": "建議",
|
||||||
|
"anyVersion": "任何"
|
||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "選取修補檔",
|
"widgetTitle": "選取修補檔",
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# 💼 Prerequisites
|
|
||||||
|
|
||||||
In order to use ReVanced Manager, certain requirements must be met.
|
|
||||||
|
|
||||||
## 🤝 Requirements
|
|
||||||
|
|
||||||
- An Android device running Android 8 or higher
|
|
||||||
- Any device architecture except ARMv7[^1]
|
|
||||||
|
|
||||||
[^1]: This constraint only applies to patches, that require patching APK resources which is why some patches may or may not work on ARMv7 architecture. You can find out, which architectures your device supports here: [⚙️ Configuring ReVanced Manager](2_4_settings.md#%E2%84%B9%EF%B8%8F-about).
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will guide you through patching an app.
|
|
||||||
|
|
||||||
Continue: [⬇️ Installation](1_installation.md)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# ⬇️ Installation
|
|
||||||
|
|
||||||
In order to use ReVanced on your Android device, ReVanced Manager must be installed.
|
|
||||||
|
|
||||||
## 🪜 Installation steps
|
|
||||||
|
|
||||||
1. Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest)
|
|
||||||
2. Install ReVanced Manager
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will guide you through patching an app.
|
|
||||||
|
|
||||||
Continue: [🪛 Usage](2_usage.md)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# 🧩 Patching apps
|
|
||||||
|
|
||||||
The following pages will guide you through using ReVanced Manager to patch apps.
|
|
||||||
|
|
||||||
## 🪜 Steps to patch apps
|
|
||||||
|
|
||||||
1. Navigate to the **Patcher** tab from the bottom navigation bar
|
|
||||||
2. Tap on the **Select an app** card
|
|
||||||
3. Choose an app to patch[^1]
|
|
||||||
> **Note**: The suggested version is visible in each app's card.
|
|
||||||
4. Tap on the **Select patches** card and select the patches you want to apply[^2]
|
|
||||||
> **Warning**: If you see a warning you can click on it for more information.
|
|
||||||
5. Tap on the **Done** then **Patch** button
|
|
||||||
> **Warning**: The patching process may take ~5 minutes. Exiting the app may increase the time it takes to patch.
|
|
||||||
6. Tap on the **Install** button
|
|
||||||
> **Note**: If you are rooted, you can mount the patched app on top of the original app.[^3]
|
|
||||||
> Optionally, you may export the patched app to storage using the options in the top right corner.
|
|
||||||
|
|
||||||
[^1]: Non-root users may be prompted to select an APK from storage, in which case you have to source the APK file yourself. ReVanced does not provide any APK files.
|
|
||||||
[^2]: It is suggested to use the default set of patches by tapping on the **Default** button above the list of patches.
|
|
||||||
[^3]: Mounting the patched app on top of the original app will only work if the installed app version matches the version of the app selected in step 3. above.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will bring you back to the usage page.
|
|
||||||
|
|
||||||
Continue: [🛠️ Usage](2_usage.md)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# 🧰 Managing patched apps
|
|
||||||
|
|
||||||
After patching an app, you may want to manage it. This page will guide you through managing patched apps.
|
|
||||||
|
|
||||||
## 🪜 Steps to manage patched apps
|
|
||||||
|
|
||||||
1. Tap on the **Dashboard** tab in the bottom navigation bar
|
|
||||||
2. Select the **Installed** chip
|
|
||||||
3. Tap on the **Info** button for the app you want to manage
|
|
||||||
4. Choose one of the options from the menu
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will bring you back to the usage page.
|
|
||||||
|
|
||||||
Continue: [🛠️ Usage](2_usage.md)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# 🔄 Updating ReVanced Manager
|
|
||||||
|
|
||||||
In order to keep up with the latest features and bug fixes, it is recommended to keep ReVanced Manager up to date.
|
|
||||||
|
|
||||||
## 🪜 Updating steps
|
|
||||||
|
|
||||||
1. Navigate to the **Dashboard** tab from the bottom navigation bar
|
|
||||||
2. Tap on the **Update** button in the **Updates** section
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will bring you back to the usage page.
|
|
||||||
|
|
||||||
Continue: [🛠️ Usage](2_usage.md)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# ⚙️ Configuring ReVanced Manager
|
|
||||||
|
|
||||||
ReVanced Manager has settings that can be configured to your liking.
|
|
||||||
|
|
||||||
## 🪛 Essential settings
|
|
||||||
|
|
||||||
- ### 🔗 API URL
|
|
||||||
|
|
||||||
Specify the URL of the API to use. This is used to fetch ReVanced Patches and update ReVanced Manager.
|
|
||||||
|
|
||||||
- ### 🧬 Sources
|
|
||||||
|
|
||||||
Override the API and change the source of ReVanced Patches.
|
|
||||||
|
|
||||||
- ### 🧪 Experimental ReVanced Patches support
|
|
||||||
|
|
||||||
Lift app version constraints from ReVanced Patches. This allows you to patch any version of an app, even if the patch is not explicitly compatible with it.
|
|
||||||
|
|
||||||
- ### 🧑🔬 Experimental universal support
|
|
||||||
|
|
||||||
This will show or hide ReVanced Patches, which are not meant for any app in particular but rather for all apps but may not work on all apps.
|
|
||||||
|
|
||||||
- ### 🔑 Export, import or delete keystore
|
|
||||||
|
|
||||||
Manage the keystore used to sign patched apps.
|
|
||||||
|
|
||||||
- ### 📄 Export, import or reset ReVanced Patches selection
|
|
||||||
|
|
||||||
Manage the ReVanced Patches selection. This is useful if you want to share your ReVanced Patches selection with others or reset it to the default selection.
|
|
||||||
|
|
||||||
- ### ℹ️ About
|
|
||||||
|
|
||||||
View information about your device and ReVanced Manager. This includes the version of ReVanced Manager and supported architectures of your device.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will bring you back to the usage page.
|
|
||||||
|
|
||||||
Continue: [🛠️ Usage](2_usage.md)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# 🛠️ Usage
|
|
||||||
|
|
||||||
The following pages will guide you through using ReVanced Manager to patch apps, manage patched apps, and update ReVanced Manager.
|
|
||||||
|
|
||||||
## 📖 Table of contents
|
|
||||||
|
|
||||||
1. [🧩 Patching apps](2_1_patching.md)
|
|
||||||
2. [🧰 Managing patched apps](2_2_managing.md)
|
|
||||||
3. [🔄 Updating ReVanced Manager](2_3_updating.md)
|
|
||||||
4. [⚙️ Configuring ReVanced Manager](2_4_settings.md)
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will guide you through troubleshooting ReVanced Manager.
|
|
||||||
|
|
||||||
Continue: [🛟 Troubleshooting](3_troubleshooting.md)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# 🛟 Troubleshooting
|
|
||||||
|
|
||||||
In case you encounter any issues while using ReVanced Manager, please refer to this page for possible solutions.
|
|
||||||
|
|
||||||
- 💉 Patching fails with an error
|
|
||||||
|
|
||||||
Make sure ReVanced Manager is up to date by following [🔄 Updating ReVanced Manager](2_3_updating.md) and select the **Default** button when choosing patches.
|
|
||||||
|
|
||||||
- 💥 App not installed as package conflicts with an existing package
|
|
||||||
|
|
||||||
An existing installation of the app you're trying to patch is conflicting with the patched app. Uninstall the existing app before installing the patched app.
|
|
||||||
|
|
||||||
- ❗️ Error code `135`, `139` or `1` when patching the app
|
|
||||||
|
|
||||||
Your device is not supported. Refer to the [Prerequisites](0_prerequisites.md) page for supported devices.
|
|
||||||
|
|
||||||
Alternatively, you can use [ReVanced CLI](https://github.com/revanced/revanced-cli) to patch the app.
|
|
||||||
|
|
||||||
- 🚫 Non-root install is not possible with the current patches selection
|
|
||||||
|
|
||||||
Select the **Default** button when choosing patches.
|
|
||||||
|
|
||||||
- 🚨 Patched app crashes on launch
|
|
||||||
|
|
||||||
Select the **Default** button when choosing patches.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will teach you how to build ReVanced Manager from source.
|
|
||||||
|
|
||||||
Continue: [🛠️ Building from source](4_building.md)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# 🛠️ Building from source
|
|
||||||
|
|
||||||
This page will guide you through building ReVanced Manager from source.
|
|
||||||
|
|
||||||
1. Setup the Flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
|
|
||||||
|
|
||||||
2. Clone the repository
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/revanced/revanced-manager.git && cd revanced-manager
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Create a GitHub personal access token with the `read:packages` scope [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced)
|
|
||||||
|
|
||||||
4. Add your GitHub username and the token to `~/.gradle/gradle.properties`
|
|
||||||
|
|
||||||
```properties
|
|
||||||
gpr.user = YourUsername
|
|
||||||
gpr.key = ghp_longrandomkey
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Get dependencies
|
|
||||||
|
|
||||||
```sh
|
|
||||||
flutter pub get
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Delete conflicting outputs
|
|
||||||
|
|
||||||
```sh
|
|
||||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**: Must be run every time you sync your local repository with the remote repository.
|
|
||||||
|
|
||||||
7. Build the APK
|
|
||||||
|
|
||||||
```sh
|
|
||||||
flutter build apk
|
|
||||||
```
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# 💊 ReVanced Manager
|
|
||||||
|
|
||||||
This documentation explains how to use [ReVanced Manager](https://github.com/revanced/revanced-manager).
|
|
||||||
|
|
||||||
## 📖 Table of contents
|
|
||||||
|
|
||||||
0. [💼 Prerequisites](0_prerequisites.md)
|
|
||||||
1. [⬇️ Installation](1_installation.md)
|
|
||||||
2. [🛠️ Usage](2_usage.md)
|
|
||||||
1. [🧩 Patching apps](2_1_patching.md)
|
|
||||||
2. [🧰 Managing patched apps](2_2_managing.md)
|
|
||||||
3. [🔄 Updating ReVanced Manager](2_3_updating.md)
|
|
||||||
4. [⚙️ Configuring ReVanced Manager](2_4_settings.md)
|
|
||||||
3. [🛟 Troubleshooting](3_troubleshooting.md)
|
|
||||||
4. [🛠 Building from source](4_building.md)
|
|
||||||
|
|
||||||
## ⏭️ Start here
|
|
||||||
|
|
||||||
The next page will tell you about the prerequisites for using ReVanced Manager.
|
|
||||||
|
|
||||||
Continue: [💼 Prerequisites](0_prerequisites.md)
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
|
||||||
package_name("app.revanced.manager.flutter") # e.g. com.krausefx.app
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# This file contains the fastlane.tools configuration
|
|
||||||
# You can find the documentation at https://docs.fastlane.tools
|
|
||||||
#
|
|
||||||
# For a list of all available actions, check out
|
|
||||||
#
|
|
||||||
# https://docs.fastlane.tools/actions
|
|
||||||
#
|
|
||||||
# For a list of all available plugins, check out
|
|
||||||
#
|
|
||||||
# https://docs.fastlane.tools/plugins/available-plugins
|
|
||||||
#
|
|
||||||
|
|
||||||
# Uncomment the line if you want fastlane to automatically update itself
|
|
||||||
# update_fastlane
|
|
||||||
|
|
||||||
default_platform(:android)
|
|
||||||
|
|
||||||
platform :android do
|
|
||||||
desc "Runs all the tests"
|
|
||||||
lane :test do
|
|
||||||
gradle(task: "test")
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "Submit a new Beta Build to Crashlytics Beta"
|
|
||||||
lane :beta do
|
|
||||||
gradle(task: "clean assembleRelease")
|
|
||||||
crashlytics
|
|
||||||
|
|
||||||
# sh "your_script.sh"
|
|
||||||
# You can also use other beta testing services here
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "Deploy a new version to the Google Play"
|
|
||||||
lane :deploy do
|
|
||||||
gradle(task: "clean assembleRelease")
|
|
||||||
upload_to_play_store
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
fastlane documentation
|
|
||||||
----
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
Make sure you have the latest version of the Xcode command line tools installed:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
xcode-select --install
|
|
||||||
```
|
|
||||||
|
|
||||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
|
||||||
|
|
||||||
# Available Actions
|
|
||||||
|
|
||||||
## Android
|
|
||||||
|
|
||||||
### android test
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android test
|
|
||||||
```
|
|
||||||
|
|
||||||
Runs all the tests
|
|
||||||
|
|
||||||
### android beta
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android beta
|
|
||||||
```
|
|
||||||
|
|
||||||
Submit a new Beta Build to Crashlytics Beta
|
|
||||||
|
|
||||||
### android deploy
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[bundle exec] fastlane android deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Deploy a new version to the Google Play
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
|
||||||
|
|
||||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
|
||||||
|
|
||||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ReVanced Manager is an Android application that uses ReVanced Patcher to add, remove, and modify existing functionalities in Android applications
|
|
||||||
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 694 KiB |
@@ -1 +0,0 @@
|
|||||||
Patch your favourite apps, right on your device.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ReVanced Manager
|
|
||||||
@@ -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,18 +267,6 @@ class ManagerAPI {
|
|||||||
return packageInfo.version;
|
return packageInfo.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getCurrentPatchesVersion() async {
|
|
||||||
if (isDefaultPatchesRepo()) {
|
|
||||||
patchesVersion = await getLatestPatchesVersion();
|
|
||||||
// print('Patches version: $patchesVersion');
|
|
||||||
} else {
|
|
||||||
// fetch from github
|
|
||||||
patchesVersion =
|
|
||||||
await _githubAPI.getLastestReleaseVersion(getPatchesRepo());
|
|
||||||
}
|
|
||||||
return patchesVersion ?? '0.0.0';
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<PatchedApplication>> getAppsToRemove(
|
Future<List<PatchedApplication>> getAppsToRemove(
|
||||||
List<PatchedApplication> patchedApps,
|
List<PatchedApplication> patchedApps,
|
||||||
) async {
|
) async {
|
||||||
@@ -400,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;
|
||||||
}
|
}
|
||||||
@@ -425,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(),
|
||||||
@@ -466,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) {
|
||||||
@@ -480,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-'),
|
||||||
@@ -144,25 +157,31 @@ class PatcherAPI {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getOriginalFilePath(String packageName) async {
|
Future<String> getOriginalFilePath(
|
||||||
|
String packageName,
|
||||||
|
String originalFilePath,
|
||||||
|
) async {
|
||||||
try {
|
try {
|
||||||
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||||
if (hasRootPermissions) {
|
if (hasRootPermissions) {
|
||||||
return await _rootAPI.getOriginalFilePath(packageName);
|
originalFilePath = await _rootAPI.getOriginalFilePath(
|
||||||
}
|
packageName,
|
||||||
} on Exception catch (e) {
|
originalFilePath,
|
||||||
if (kDebugMode) {
|
);
|
||||||
print(e);
|
|
||||||
}
|
}
|
||||||
|
return originalFilePath;
|
||||||
|
} on Exception catch (e, s) {
|
||||||
|
await Sentry.captureException(e, stackTrace: s);
|
||||||
|
return originalFilePath;
|
||||||
}
|
}
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> runPatcher(
|
Future<void> runPatcher(
|
||||||
String packageName,
|
String packageName,
|
||||||
String apkFilePath,
|
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 {
|
||||||
@@ -174,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();
|
||||||
@@ -191,30 +212,30 @@ class PatcherAPI {
|
|||||||
_outFile = File('${workDir.path}/out.apk');
|
_outFile = File('${workDir.path}/out.apk');
|
||||||
final Directory cacheDir = Directory('${workDir.path}/cache');
|
final Directory cacheDir = Directory('${workDir.path}/cache');
|
||||||
cacheDir.createSync();
|
cacheDir.createSync();
|
||||||
String originalFilePath = await getOriginalFilePath(packageName);
|
|
||||||
if (originalFilePath.isEmpty) {
|
|
||||||
originalFilePath = apkFilePath;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await patcherChannel.invokeMethod(
|
await patcherChannel.invokeMethod(
|
||||||
'runPatcher',
|
'runPatcher',
|
||||||
{
|
{
|
||||||
'patchBundleFilePath': patchBundleFile.path,
|
'patchBundleFilePath': patchBundleFile.path,
|
||||||
'originalFilePath': originalFilePath,
|
'originalFilePath': await getOriginalFilePath(
|
||||||
|
packageName,
|
||||||
|
originalFilePath,
|
||||||
|
),
|
||||||
'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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,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,53 +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>.broadcast();
|
|
||||||
|
|
||||||
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) {
|
|
||||||
disposeManagerUpdateProgress();
|
|
||||||
// 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,
|
||||||
@@ -192,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,10 +1,8 @@
|
|||||||
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 {
|
||||||
// TODO(ponces): remove in the future, keep it for now during migration.
|
final String _managerDirPath = '/data/local/tmp/revanced-manager';
|
||||||
final String _revancedOldDirPath = '/data/local/tmp/revanced-manager';
|
|
||||||
final String _revancedDirPath = '/data/adb/revanced';
|
|
||||||
final String _postFsDataDirPath = '/data/adb/post-fs-data.d';
|
final String _postFsDataDirPath = '/data/adb/post-fs-data.d';
|
||||||
final String _serviceDDirPath = '/data/adb/service.d';
|
final String _serviceDDirPath = '/data/adb/service.d';
|
||||||
|
|
||||||
@@ -12,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,62 +36,53 @@ class RootAPI {
|
|||||||
seLinux,
|
seLinux,
|
||||||
String filePath,
|
String filePath,
|
||||||
) async {
|
) async {
|
||||||
try {
|
if (permissions.isNotEmpty) {
|
||||||
if (permissions.isNotEmpty) {
|
await Root.exec(
|
||||||
await Root.exec(
|
cmd: 'chmod $permissions "$filePath"',
|
||||||
cmd: 'chmod $permissions "$filePath"',
|
);
|
||||||
);
|
}
|
||||||
}
|
if (ownerGroup.isNotEmpty) {
|
||||||
if (ownerGroup.isNotEmpty) {
|
await Root.exec(
|
||||||
await Root.exec(
|
cmd: 'chown $ownerGroup "$filePath"',
|
||||||
cmd: 'chown $ownerGroup "$filePath"',
|
);
|
||||||
);
|
}
|
||||||
}
|
if (seLinux.isNotEmpty) {
|
||||||
if (seLinux.isNotEmpty) {
|
await Root.exec(
|
||||||
await Root.exec(
|
cmd: 'chcon $seLinux "$filePath"',
|
||||||
cmd: 'chcon $seLinux "$filePath"',
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
} on Exception catch (e) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isAppInstalled(String packageName) async {
|
Future<bool> isAppInstalled(String packageName) async {
|
||||||
if (packageName.isNotEmpty) {
|
if (packageName.isNotEmpty) {
|
||||||
return fileExists('$_serviceDDirPath/$packageName.sh');
|
String? res = await Root.exec(
|
||||||
|
cmd: 'ls "$_managerDirPath/$packageName"',
|
||||||
|
);
|
||||||
|
if (res != null && res.isNotEmpty) {
|
||||||
|
res = await Root.exec(
|
||||||
|
cmd: 'ls "$_serviceDDirPath/$packageName.sh"',
|
||||||
|
);
|
||||||
|
return res != null && res.isNotEmpty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getInstalledApps() async {
|
Future<List<String>> getInstalledApps() async {
|
||||||
final List<String> apps = List.empty();
|
|
||||||
try {
|
try {
|
||||||
String? res = await Root.exec(
|
final String? res = await Root.exec(
|
||||||
cmd: 'ls "$_revancedDirPath"',
|
cmd: 'ls "$_managerDirPath"',
|
||||||
);
|
);
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
final List<String> list = res.split('\n');
|
final List<String> apps = res.split('\n');
|
||||||
list.removeWhere((pack) => pack.isEmpty);
|
apps.removeWhere((pack) => pack.isEmpty);
|
||||||
apps.addAll(list.map((pack) => pack.trim()).toList());
|
return apps.map((pack) => pack.trim()).toList();
|
||||||
}
|
|
||||||
// TODO(ponces): remove in the future, keep it for now during migration.
|
|
||||||
res = await Root.exec(
|
|
||||||
cmd: 'ls "$_revancedOldDirPath"',
|
|
||||||
);
|
|
||||||
if (res != null) {
|
|
||||||
final List<String> list = res.split('\n');
|
|
||||||
list.removeWhere((pack) => pack.isEmpty);
|
|
||||||
apps.addAll(list.map((pack) => pack.trim()).toList());
|
|
||||||
}
|
|
||||||
} on Exception catch (e) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
print(e);
|
|
||||||
}
|
}
|
||||||
|
} on Exception catch (e, s) {
|
||||||
|
await Sentry.captureException(e, stackTrace: s);
|
||||||
|
return List.empty();
|
||||||
}
|
}
|
||||||
return apps;
|
return List.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteApp(String packageName, String originalFilePath) async {
|
Future<void> deleteApp(String packageName, String originalFilePath) async {
|
||||||
@@ -107,12 +92,8 @@ class RootAPI {
|
|||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
||||||
);
|
);
|
||||||
// TODO(ponces): remove in the future, keep it for now during migration.
|
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'rm -rf "$_revancedOldDirPath/$packageName"',
|
cmd: 'rm -rf "$_managerDirPath/$packageName"',
|
||||||
);
|
|
||||||
await Root.exec(
|
|
||||||
cmd: 'rm -rf "$_revancedDirPath/$packageName"',
|
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
|
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
|
||||||
@@ -130,13 +111,13 @@ class RootAPI {
|
|||||||
try {
|
try {
|
||||||
await deleteApp(packageName, originalFilePath);
|
await deleteApp(packageName, originalFilePath);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'mkdir -p "$_revancedDirPath/$packageName"',
|
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
await setPermissions(
|
await setPermissions(
|
||||||
'0755',
|
'0755',
|
||||||
'shell:shell',
|
'shell:shell',
|
||||||
'',
|
'',
|
||||||
'$_revancedDirPath/$packageName',
|
'$_managerDirPath/$packageName',
|
||||||
);
|
);
|
||||||
await saveOriginalFilePath(packageName, originalFilePath);
|
await saveOriginalFilePath(packageName, originalFilePath);
|
||||||
await installServiceDScript(packageName);
|
await installServiceDScript(packageName);
|
||||||
@@ -144,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +134,7 @@ class RootAPI {
|
|||||||
Future<void> installServiceDScript(String packageName) async {
|
Future<void> installServiceDScript(String packageName) async {
|
||||||
final String content = '#!/system/bin/sh\n'
|
final String content = '#!/system/bin/sh\n'
|
||||||
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 3; done\n'
|
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 3; done\n'
|
||||||
'base_path=$_revancedDirPath/$packageName/base.apk\n'
|
'base_path=$_managerDirPath/$packageName/base.apk\n'
|
||||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||||
r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path';
|
r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path';
|
||||||
final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
||||||
@@ -177,7 +156,7 @@ class RootAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> installApk(String packageName, String patchedFilePath) async {
|
Future<void> installApk(String packageName, String patchedFilePath) async {
|
||||||
final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk';
|
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
||||||
);
|
);
|
||||||
@@ -190,7 +169,7 @@ class RootAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> mountApk(String packageName, String originalFilePath) async {
|
Future<void> mountApk(String packageName, String originalFilePath) async {
|
||||||
final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk';
|
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'am force-stop "$packageName"',
|
cmd: 'am force-stop "$packageName"',
|
||||||
);
|
);
|
||||||
@@ -209,18 +188,21 @@ class RootAPI {
|
|||||||
return res != null && res.isNotEmpty;
|
return res != null && res.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getOriginalFilePath(String packageName) async {
|
Future<String> getOriginalFilePath(
|
||||||
final String originalPath = '$_revancedDirPath/$packageName/original.apk';
|
String packageName,
|
||||||
final String oldOrigPath = '$_revancedOldDirPath/$packageName/original.apk';
|
String originalFilePath,
|
||||||
|
) async {
|
||||||
final bool isInstalled = await isAppInstalled(packageName);
|
final bool isInstalled = await isAppInstalled(packageName);
|
||||||
if (isInstalled && await isMounted(packageName)) {
|
if (isInstalled && await isMounted(packageName)) {
|
||||||
if (await fileExists(originalPath)) {
|
originalFilePath = '$_managerDirPath/$packageName/original.apk';
|
||||||
return originalPath;
|
await setPermissions(
|
||||||
} else if (await fileExists(oldOrigPath)) {
|
'0644',
|
||||||
return oldOrigPath;
|
'shell:shell',
|
||||||
}
|
'u:object_r:apk_data_file:s0',
|
||||||
|
originalFilePath,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return '';
|
return originalFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveOriginalFilePath(
|
Future<void> saveOriginalFilePath(
|
||||||
@@ -228,15 +210,15 @@ class RootAPI {
|
|||||||
String originalFilePath,
|
String originalFilePath,
|
||||||
) async {
|
) async {
|
||||||
final String originalRootPath =
|
final String originalRootPath =
|
||||||
'$_revancedDirPath/$packageName/original.apk';
|
'$_managerDirPath/$packageName/original.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'mkdir -p "$_revancedDirPath/$packageName"',
|
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
await setPermissions(
|
await setPermissions(
|
||||||
'0755',
|
'0755',
|
||||||
'shell:shell',
|
'shell:shell',
|
||||||
'',
|
'',
|
||||||
'$_revancedDirPath/$packageName',
|
'$_managerDirPath/$packageName',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'cp "$originalFilePath" "$originalRootPath"',
|
cmd: 'cp "$originalFilePath" "$originalRootPath"',
|
||||||
@@ -248,18 +230,4 @@ class RootAPI {
|
|||||||
originalFilePath,
|
originalFilePath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> fileExists(String path) async {
|
|
||||||
try {
|
|
||||||
final String? res = await Root.exec(
|
|
||||||
cmd: 'ls $path',
|
|
||||||
);
|
|
||||||
return res != null && res.isNotEmpty;
|
|
||||||
} on Exception catch (e) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||