Compare commits
162 Commits
Author | SHA1 | Date | |
---|---|---|---|
da0d88d86f | |||
b7347c312a | |||
c876f2f7e3 | |||
adad5fd8ff | |||
2aaa7ae8c9 | |||
1f64ea37bd | |||
cac1525da0 | |||
1ad906fedc | |||
20ffef39a3 | |||
185460c054 | |||
0079e74d77 | |||
a8e019482f | |||
f01b8e47aa | |||
e43dfb7599 | |||
6b8cfe2b49 | |||
7f7b14bae3 | |||
810b02d9fd | |||
62813145b2 | |||
4877058253 | |||
2a9fd3abb8 | |||
a244f7b598 | |||
5396457ad5 | |||
0186b6ea61 | |||
f6e99f7e88 | |||
c24a3828be | |||
2e38a4567a | |||
67c5d67a61 | |||
9592dde534 | |||
d030b0af70 | |||
4ccb9ac94d | |||
8af62b917c | |||
311f114132 | |||
d015bd03f7 | |||
a61b9de0fa | |||
ef1b283917 | |||
c677f00105 | |||
8d2f778dfe | |||
c549d102f6 | |||
39a9ee4e9d | |||
a27dc6ad1c | |||
8ccb75fc8d | |||
8fc86dbe02 | |||
359f052608 | |||
4150e2265c | |||
b803ce7435 | |||
289c6cd7a9 | |||
31fc7b74c2 | |||
3e565f25be | |||
e509be4e21 | |||
170fc537ac | |||
3fe5882145 | |||
a290791410 | |||
2ebd38ff68 | |||
cd987a5b19 | |||
7f1dab7ee1 | |||
bed3945aa5 | |||
15a32a18b7 | |||
d2e8e7dd5d | |||
ce12ec89c4 | |||
b286444ad9 | |||
941263102d | |||
e2ed296dc7 | |||
cfc866bef2 | |||
affba669ce | |||
7230152ab8 | |||
21fee7171f | |||
ad17995f28 | |||
ac830cbe7f | |||
65da6af3f9 | |||
3d90bf7588 | |||
62ef1c88fe | |||
d6918920b6 | |||
cdfb09fbfa | |||
bb681e31c9 | |||
c7483936ec | |||
0a1f2da33d | |||
f5aafdb7d6 | |||
c9adf1c492 | |||
4c9cb560e3 | |||
f0b028279c | |||
197770b68b | |||
37b583f560 | |||
dca2d4fe12 | |||
3b677f8ae3 | |||
0b952578d1 | |||
054afbbedd | |||
866a6e4a44 | |||
8ea7dd478b | |||
7839252934 | |||
fa4063220f | |||
d214a02abd | |||
d1c12edd1b | |||
ded1a44c37 | |||
790a6cd1e3 | |||
a79f883a0f | |||
d9c5a540a3 | |||
276f33b9ec | |||
ded59d2da0 | |||
62f7a820d8 | |||
7063ffa013 | |||
bf4dc3c095 | |||
c10e5848bf | |||
92a3b0d6e0 | |||
b475bd25c8 | |||
d318224a6f | |||
0074fee865 | |||
5617535a63 | |||
68ccefc59f | |||
6d60541626 | |||
a635e5b8d0 | |||
48a10440fe | |||
8e3ba88318 | |||
ab8fccc544 | |||
8319dc9164 | |||
6829d3cdea | |||
3ae4d69110 | |||
dc665f227e | |||
a83496568f | |||
12d25570af | |||
378c947654 | |||
bd39a3140e | |||
7d3ca3dec1 | |||
1cb556c8f8 | |||
8c8f96de1c | |||
318cd87a9a | |||
5d63d5c2d3 | |||
7d347fccc6 | |||
a54ca799b9 | |||
f5bc1a996f | |||
8591bc4d01 | |||
40888c07f3 | |||
1c965c3788 | |||
4df690c2a2 | |||
d6abb61e2b | |||
3434c862e9 | |||
ea8af926fa | |||
c3df48174c | |||
f1e60f96c4 | |||
cdd852678b | |||
bf518b5467 | |||
ffd53fab26 | |||
5aad7dad35 | |||
b1c1a9f4e1 | |||
d847a8e0b2 | |||
9668730b5d | |||
dc049cf26a | |||
82c2b2f128 | |||
5f81d65911 | |||
19f990c564 | |||
62467007b2 | |||
d7624e5e1f | |||
4505f10e50 | |||
3ce3df5e19 | |||
8d4e4ba6c9 | |||
d78868b462 | |||
01a681ad00 | |||
adfeb61eab | |||
8c3faac343 | |||
c81acce31c | |||
fe629ce77c | |||
5c27add2b2 | |||
ff90dae695 |
2
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@ -116,5 +116,5 @@ body:
|
||||
required: true
|
||||
- label: I filled out all of the requested information in this issue properly.
|
||||
required: true
|
||||
- label: The issue is related solely to the ReVanced Manager
|
||||
- label: The issue is related solely to the ReVanced Manager and not related to patching errors or patches
|
||||
required: true
|
||||
|
2
.github/config.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
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
Normal file
@ -0,0 +1,27 @@
|
||||
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
@ -1,55 +0,0 @@
|
||||
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@v2
|
||||
with:
|
||||
name: revanced-manager-${{ env.RELEASE_VERSION }}
|
||||
path: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
4
.github/workflows/crowdin.yml
vendored
@ -3,7 +3,7 @@ name: Sync Crowdin translations
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "flutter"
|
||||
- "dev"
|
||||
paths:
|
||||
- "assets/i18n/en_US.json"
|
||||
- ".github/workflows/crowdin.yml"
|
||||
@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Crowdin
|
||||
uses: crowdin/github-action@1.5.0
|
||||
uses: crowdin/github-action@v1
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: true
|
||||
|
82
.github/workflows/pull-request-build.yml
vendored
@ -1,41 +1,41 @@
|
||||
name: "Android CI PR Build"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
|
||||
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 }}
|
||||
run: flutter build apk
|
||||
- name: Add version to APK
|
||||
run: mv build/app/outputs/flutter-apk/app-release.apk revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: revanced-manager-${{ env.RELEASE_VERSION }}
|
||||
path: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
||||
name: PR Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
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 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: Upload build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: revanced-manager
|
||||
path: build/app/outputs/flutter-apk/app-release.apk
|
||||
|
12
.github/workflows/release-build.yml
vendored
@ -15,15 +15,11 @@ jobs:
|
||||
- name: Set up JDK 12
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '12'
|
||||
distribution: 'zulu'
|
||||
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 }}
|
||||
channel: "stable"
|
||||
- name: Set up Flutter
|
||||
run: flutter pub get
|
||||
- name: Generate files with Builder
|
||||
@ -51,4 +47,4 @@ jobs:
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
files: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
||||
files: revanced-manager-${{ env.RELEASE_VERSION }}.apk
|
68
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
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
Normal file
@ -0,0 +1,19 @@
|
||||
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,5 +134,11 @@ app.*.map.json
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
!/dev/ci/**/Gemfile.lock
|
||||
|
||||
Firebase related
|
||||
.firebase
|
||||
# Firebase related
|
||||
.firebase
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# FVM
|
||||
.fvm
|
75
.releaserc
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"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
CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
||||
|
@ -3,15 +3,11 @@
|
||||
The official ReVanced Manager based on Flutter.
|
||||
|
||||
## 🔽 Download
|
||||
To download the Alpha version of Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file.
|
||||
To download latest Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file.
|
||||
|
||||
## 📝 Prerequisites
|
||||
1. Android 8 or higher
|
||||
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
|
||||
For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose).
|
||||
@ -28,7 +24,7 @@ If you wish to translate ReVanced Manager, we're accepting translations on [Crow
|
||||
## 🛠️ Building Manager from source
|
||||
1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
|
||||
2. Clone the repository locally
|
||||
3. Add your github token in gradle.properties like [this](https://github.com/revanced/revanced-documentation/blob/main/docs/revanced-development/2_building_from_source.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
|
||||
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)
|
||||
|
@ -11,23 +11,154 @@ include: package:flutter_lints/flutter.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/utils/env_class.g.dart
|
||||
- lib/app/app.locator.dart
|
||||
- lib/app/app.router.dart
|
||||
- lib/models/patch.g.dart
|
||||
- lib/models/patched_application.g.dart
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
- always_declare_return_types
|
||||
- require_trailing_commas
|
||||
- always_put_control_body_on_new_line
|
||||
- always_require_non_null_named_parameters
|
||||
- always_use_package_imports # we do this commonly
|
||||
- annotate_overrides
|
||||
- avoid_bool_literals_in_conditional_expressions
|
||||
- avoid_double_and_int_checks
|
||||
- avoid_empty_else
|
||||
- avoid_equals_and_hash_code_on_mutable_classes
|
||||
- avoid_escaping_inner_quotes
|
||||
- avoid_field_initializers_in_const_classes
|
||||
- avoid_function_literals_in_foreach_calls
|
||||
- avoid_implementing_value_types
|
||||
- avoid_init_to_null
|
||||
- avoid_js_rounded_ints
|
||||
- avoid_null_checks_in_equality_operators
|
||||
- avoid_print
|
||||
- avoid_redundant_argument_values
|
||||
- avoid_relative_lib_imports
|
||||
- avoid_renaming_method_parameters
|
||||
- avoid_return_types_on_setters
|
||||
- avoid_returning_null
|
||||
- avoid_returning_null_for_future
|
||||
- avoid_returning_null_for_void
|
||||
- avoid_setters_without_getters
|
||||
- avoid_shadowing_type_parameters
|
||||
- avoid_single_cascade_in_expression_statements
|
||||
- avoid_type_to_string
|
||||
- avoid_types_as_parameter_names
|
||||
- avoid_unnecessary_containers
|
||||
- avoid_void_async
|
||||
- avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
|
||||
- await_only_futures
|
||||
- camel_case_extensions
|
||||
- camel_case_types
|
||||
- cancel_subscriptions
|
||||
- cast_nullable_to_non_nullable
|
||||
- close_sinks # not reliable enough
|
||||
- control_flow_in_finally
|
||||
- curly_braces_in_flow_control_structures
|
||||
- depend_on_referenced_packages
|
||||
- deprecated_consistency
|
||||
- directives_ordering
|
||||
- empty_catches
|
||||
- empty_constructor_bodies
|
||||
- empty_statements
|
||||
- eol_at_end_of_file
|
||||
- exhaustive_cases
|
||||
- file_names
|
||||
- flutter_style_todos
|
||||
- hash_and_equals
|
||||
- implementation_imports
|
||||
- iterable_contains_unrelated_type
|
||||
- leading_newlines_in_multiline_strings
|
||||
- library_names
|
||||
- library_prefixes
|
||||
- library_private_types_in_public_api
|
||||
- list_remove_unrelated_type
|
||||
- missing_whitespace_between_adjacent_strings
|
||||
- no_adjacent_strings_in_list
|
||||
- no_duplicate_case_values
|
||||
- no_logic_in_create_state
|
||||
- non_constant_identifier_names
|
||||
- noop_primitive_operations
|
||||
- null_check_on_nullable_type_parameter
|
||||
- null_closures
|
||||
- overridden_fields
|
||||
- package_api_docs
|
||||
- package_names
|
||||
- package_prefixed_library_names
|
||||
- prefer_adjacent_string_concatenation
|
||||
- prefer_asserts_in_initializer_lists
|
||||
- prefer_collection_literals
|
||||
- prefer_conditional_assignment
|
||||
- prefer_const_constructors
|
||||
- prefer_const_constructors_in_immutables
|
||||
- prefer_const_declarations
|
||||
- prefer_const_literals_to_create_immutables
|
||||
- prefer_contains
|
||||
- prefer_final_fields
|
||||
- prefer_final_in_for_each
|
||||
- prefer_final_locals
|
||||
- prefer_for_elements_to_map_fromIterable
|
||||
- prefer_foreach
|
||||
- prefer_function_declarations_over_variables
|
||||
- prefer_generic_function_type_aliases
|
||||
- prefer_if_elements_to_conditional_expressions
|
||||
- prefer_if_null_operators
|
||||
- prefer_initializing_formals
|
||||
- prefer_inlined_adds
|
||||
- prefer_interpolation_to_compose_strings
|
||||
- prefer_is_empty
|
||||
- prefer_is_not_empty
|
||||
- prefer_is_not_operator
|
||||
- prefer_iterable_whereType
|
||||
- prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018
|
||||
- prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
|
||||
- prefer_null_aware_operators
|
||||
- prefer_single_quotes
|
||||
- prefer_spread_collections
|
||||
- prefer_typing_uninitialized_variables
|
||||
- prefer_void_to_null
|
||||
- provide_deprecation_message
|
||||
- recursive_getters
|
||||
- sized_box_for_whitespace
|
||||
- slash_for_doc_comments
|
||||
- sort_child_properties_last
|
||||
- sort_constructors_first
|
||||
- sort_unnamed_constructors_first
|
||||
- test_types_in_equals
|
||||
- throw_in_finally
|
||||
- tighten_type_of_initializing_formals
|
||||
- type_init_formals
|
||||
- unnecessary_brace_in_string_interps
|
||||
- unnecessary_const
|
||||
- unnecessary_getters_setters
|
||||
- unnecessary_new
|
||||
- unnecessary_null_aware_assignments
|
||||
- unnecessary_null_checks
|
||||
- unnecessary_null_in_if_null_operators
|
||||
- unnecessary_nullable_for_final_variable_declarations
|
||||
- unnecessary_overrides
|
||||
- unnecessary_parenthesis
|
||||
- unnecessary_statements
|
||||
- unnecessary_string_escapes
|
||||
- unnecessary_string_interpolations
|
||||
- unnecessary_this
|
||||
- unrelated_type_equality_checks
|
||||
- unsafe_html
|
||||
- use_build_context_synchronously
|
||||
- use_full_hex_values_for_flutter_colors
|
||||
- use_function_type_syntax_for_parameters
|
||||
- use_if_null_to_convert_nulls_to_bools
|
||||
- use_is_even_rather_than_modulo
|
||||
- use_key_in_widget_constructors
|
||||
- use_late_for_private_fields_and_variables
|
||||
- use_named_constants
|
||||
- use_raw_strings
|
||||
- use_rethrow_when_possible
|
||||
- use_setters_to_change_properties
|
||||
- use_test_throws_matchers
|
||||
- valid_regexps
|
||||
- void_checks
|
||||
|
3
android/Gemfile
Normal file
@ -0,0 +1,3 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
@ -71,14 +71,10 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
// ReVanced
|
||||
implementation "app.revanced:revanced-patcher:6.3.0"
|
||||
implementation "app.revanced:revanced-patcher:11.0.1"
|
||||
|
||||
// Signing & aligning
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
implementation("com.android.tools.build:apksig:7.2.2")
|
||||
|
||||
// MicroG cronet
|
||||
implementation("org.microg:cronet-common:$cronetVersion")
|
||||
implementation("org.microg:cronet-native:$cronetVersion")
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
<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_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.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
@ -17,7 +19,8 @@
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:largeHeap="true"
|
||||
android:extractNativeLibs="true">
|
||||
android:extractNativeLibs="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
@ -28,8 +31,7 @@
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
android:resource="@style/NormalTheme"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
@ -43,8 +43,9 @@ class MainActivity : FlutterActivity() {
|
||||
val integrationsPath = call.argument<String>("integrationsPath")
|
||||
val selectedPatches = call.argument<List<String>>("selectedPatches")
|
||||
val cacheDirPath = call.argument<String>("cacheDirPath")
|
||||
val mergeIntegrations = call.argument<Boolean>("mergeIntegrations")
|
||||
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
||||
val keystorePassword = call.argument<String>("keystorePassword")
|
||||
|
||||
if (patchBundleFilePath != null &&
|
||||
originalFilePath != null &&
|
||||
inputFilePath != null &&
|
||||
@ -53,8 +54,8 @@ class MainActivity : FlutterActivity() {
|
||||
integrationsPath != null &&
|
||||
selectedPatches != null &&
|
||||
cacheDirPath != null &&
|
||||
mergeIntegrations != null &&
|
||||
keyStoreFilePath != null
|
||||
keyStoreFilePath != null &&
|
||||
keystorePassword != null
|
||||
) {
|
||||
runPatcher(
|
||||
result,
|
||||
@ -66,8 +67,8 @@ class MainActivity : FlutterActivity() {
|
||||
integrationsPath,
|
||||
selectedPatches,
|
||||
cacheDirPath,
|
||||
mergeIntegrations,
|
||||
keyStoreFilePath
|
||||
keyStoreFilePath,
|
||||
keystorePassword
|
||||
)
|
||||
} else {
|
||||
result.notImplemented()
|
||||
@ -88,8 +89,8 @@ class MainActivity : FlutterActivity() {
|
||||
integrationsPath: String,
|
||||
selectedPatches: List<String>,
|
||||
cacheDirPath: String,
|
||||
mergeIntegrations: Boolean,
|
||||
keyStoreFilePath: String
|
||||
keyStoreFilePath: String,
|
||||
keystorePassword: String
|
||||
) {
|
||||
val originalFile = File(originalFilePath)
|
||||
val inputFile = File(inputFilePath)
|
||||
@ -139,19 +140,17 @@ class MainActivity : FlutterActivity() {
|
||||
mapOf("progress" to 0.3, "header" to "", "log" to "")
|
||||
)
|
||||
}
|
||||
if (mergeIntegrations) {
|
||||
handler.post {
|
||||
installerChannel.invokeMethod(
|
||||
"update",
|
||||
mapOf(
|
||||
"progress" to 0.4,
|
||||
"header" to "Merging integrations...",
|
||||
"log" to "Merging integrations"
|
||||
)
|
||||
handler.post {
|
||||
installerChannel.invokeMethod(
|
||||
"update",
|
||||
mapOf(
|
||||
"progress" to 0.4,
|
||||
"header" to "Merging integrations...",
|
||||
"log" to "Merging integrations"
|
||||
)
|
||||
}
|
||||
patcher.addFiles(listOf(integrations)) {}
|
||||
)
|
||||
}
|
||||
patcher.addIntegrations(listOf(integrations)) {}
|
||||
|
||||
handler.post {
|
||||
installerChannel.invokeMethod(
|
||||
@ -174,7 +173,7 @@ class MainActivity : FlutterActivity() {
|
||||
javaClass.classLoader
|
||||
)
|
||||
).loadPatches().filter { patch ->
|
||||
patch.compatiblePackages!!.any { it.name == patcher.context.packageMetadata.packageName } &&
|
||||
(patch.compatiblePackages?.any { it.name == patcher.context.packageMetadata.packageName } == true || patch.compatiblePackages.isNullOrEmpty()) &&
|
||||
selectedPatches.any { it == patch.patchName }
|
||||
}
|
||||
} else {
|
||||
@ -196,7 +195,7 @@ class MainActivity : FlutterActivity() {
|
||||
}
|
||||
return@forEach
|
||||
}
|
||||
val msg = "$patch failed.\nError:\n" + res.exceptionOrNull()!!.printStackTrace()
|
||||
val msg = "Failed to apply $patch: " + "${res.exceptionOrNull()!!.message ?: res.exceptionOrNull()!!.cause!!::class.simpleName}"
|
||||
handler.post {
|
||||
installerChannel.invokeMethod(
|
||||
"update",
|
||||
@ -248,7 +247,7 @@ class MainActivity : FlutterActivity() {
|
||||
// Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
|
||||
|
||||
try {
|
||||
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
|
||||
Signer("ReVanced", keystorePassword).signApk(patchedFile, outFile, keyStoreFile)
|
||||
} catch (e: Exception) {
|
||||
//log to console
|
||||
print("Error signing apk: ${e.message}")
|
||||
@ -272,8 +271,8 @@ class MainActivity : FlutterActivity() {
|
||||
"update",
|
||||
mapOf(
|
||||
"progress" to -100.0,
|
||||
"header" to "Aborting...",
|
||||
"log" to "An error occurred! Aborting\nError:\n$stack"
|
||||
"header" to "Aborted...",
|
||||
"log" to "An error occurred! Aborted\nError:\n$stack"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 814 B |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.4 KiB |
30
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<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: 28 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 5.3 KiB |
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.4 KiB |
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1B1B1B</color>
|
||||
</resources>
|
||||
</resources>
|
@ -1,6 +1,5 @@
|
||||
buildscript {
|
||||
ext.cronetVersion = '102.5005.125'
|
||||
ext.kotlin_version = '1.7.20'
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
@ -32,6 +31,6 @@ subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#Mon May 09 12:07:41 MSK 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=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
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "موافق",
|
||||
"cancelButton": "إلغاء",
|
||||
"updateButton": "تحديث",
|
||||
"enabledLabel": "مفعّل",
|
||||
"disabledLabel": "معطّل",
|
||||
"yesButton": "نعم",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "لا توجد تطبيقات معدلة مثبتة",
|
||||
"installed": "مثبت",
|
||||
"updateDialogTitle": "تحديث المدير",
|
||||
"updateDialogText": "هل أنت متأكد من رغبتك في تنزيل وتحديث ReVanced Manager ؟",
|
||||
"updateChangelogTitle": "سجل التغييرات",
|
||||
"notificationTitle": "تم تنزيل التحديث",
|
||||
"notificationText": "أنقر لتثبيت التحديث",
|
||||
"downloadingMessage": "جاري تحميل التحديث...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "الكل",
|
||||
"none": "بدون",
|
||||
"loadPatchesSelection": "تحميل التعديلات المحددة",
|
||||
"noSavedPatches": "لا توجد تعديلات محفوظة للتطبيق المحدد\nاضغط على تم لحفظ التحديد الحالي",
|
||||
"noSavedPatches": "لا توجد تعديلات محفوظة للتطبيق المختار.\nاضغط على تم لحفظ الاختيار الحالي.",
|
||||
"noPatchesFound": "لم يتم العثور على تعديلات للتطبيق المحدد",
|
||||
"selectAllPatchesWarningContent": "أنت على وشك تحديد جميع التعديلات، بما في ذلك التعديلات غير الموصى بها والتي يمكن أن تسبب سلوكاً غير مرغوب فيه."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "قد يؤدي تحديد هذا التعديل إلى حدوث أخطاء في عملية التعديل.\n\nإصدار التطبيق: {packageVersion}\nالإصدارات المدعومة حالياً:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "التعديل غير مدعوم لهذا الإصدار من التطبيق. مكن التعديل التجريبي في الإعدادات للمتابعة."
|
||||
"unsupportedPatchVersion": "التعديل غير مدعوم لهذا الإصدار من التطبيق. فعِّل خِيار التعديل التجريبي في الإعدادات للمتابعة."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "المثبت",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "الإنجليزية",
|
||||
"sourcesLabel": "المصادر",
|
||||
"sourcesLabelHint": "تكوين مصادرك المخصصة",
|
||||
"hostRepositoryLabel": "مستودع API",
|
||||
"orgPatchesLabel": "تنظيم التعديلات",
|
||||
"sourcesPatchesLabel": "مصدر التعديلات",
|
||||
"orgIntegrationsLabel": "تنظيم الدمج",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "رابط API",
|
||||
"apiURLHint": "تكوين رابط API الخاص بك",
|
||||
"selectApiURL": "رابط API",
|
||||
"experimentalUniversalPatchesLabel": "دعم التعديلات العامة التجريبية",
|
||||
"experimentalUniversalPatchesHint": "عرض جميع التطبيقات المراد استخدامها مع التعديلات العامة، قد يكون تحميل قائمة التطبيقات أبطأ",
|
||||
"experimentalPatchesLabel": "دعم التعديلات التجريبية",
|
||||
"experimentalPatchesHint": "تمكين استخدام التعديلات غير المدعومة في أي إصدار للتطبيق",
|
||||
"enabledExperimentalPatches": "تم تمكين دعم التعديلات التجريبية",
|
||||
|
200
assets/i18n/bg_BG.json
Normal file
@ -0,0 +1,200 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Отказ",
|
||||
"updateButton": "Актуализация",
|
||||
"enabledLabel": "Включено",
|
||||
"disabledLabel": "Изключено",
|
||||
"yesButton": "Да",
|
||||
"noButton": "Не",
|
||||
"warning": "Внимание",
|
||||
"navigationView": {
|
||||
"dashboardTab": "Табло за управление",
|
||||
"patcherTab": "Пачър",
|
||||
"settingsTab": "Настройки"
|
||||
},
|
||||
"homeView": {
|
||||
"widgetTitle": "Табло за управление",
|
||||
"updatesSubtitle": "Актуализации",
|
||||
"patchedSubtitle": "Модифицирани приложения",
|
||||
"updatesAvailable": "Налични актуализации",
|
||||
"noUpdates": "Няма налични актуализации",
|
||||
"WIP": "В процес на разработка...",
|
||||
"noInstallations": "Няма инсталирани модифицирани приложения",
|
||||
"installed": "Инсталирани",
|
||||
"updateDialogTitle": "Обнови ReVanced Manager",
|
||||
"updateChangelogTitle": "Списък с промени",
|
||||
"notificationTitle": "Актуализацията е изтеглена",
|
||||
"notificationText": "Натиснете за да инсталирате актуализациите",
|
||||
"downloadingMessage": "Изтегляне на актуализация...",
|
||||
"installingMessage": "Инсталиране на актуализация...",
|
||||
"errorDownloadMessage": "Свалянето на актуализацията не беше успешно",
|
||||
"errorInstallMessage": "Инсталирането на актуализацията не беше успешно",
|
||||
"noConnection": "Няма връзка с интернет",
|
||||
"updatesDisabled": "Актуализацията на модифицирани приложения в момента не работи. Модифицирайте приложението наново."
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Модифицирай",
|
||||
"infoButton": "Информация",
|
||||
"changelogLabel": "Списък с промените"
|
||||
},
|
||||
"latestCommitCard": {
|
||||
"loadingLabel": "Зареждане...",
|
||||
"timeagoLabel": "Преди {time}",
|
||||
"patcherLabel": "Пачър: ",
|
||||
"managerLabel": "Мениджър: ",
|
||||
"updateButton": "Обнови ReVanced Manager"
|
||||
},
|
||||
"patcherView": {
|
||||
"widgetTitle": "Модификатор",
|
||||
"patchButton": "Модифицирай",
|
||||
"patchDialogText": "Избрали сте модификация за ресурси и разделен APK пакет, което може да доведе до грешки при пачването.\nСигурни ли сте, че искате да продължите?"
|
||||
},
|
||||
"appSelectorCard": {
|
||||
"widgetTitle": "Изберете приложение",
|
||||
"widgetTitleSelected": "Избрано приложение",
|
||||
"widgetSubtitle": "Няма избрано приложение",
|
||||
"noAppsLabel": "Няма намерени приложения",
|
||||
"currentVersion": "Текуща",
|
||||
"recommendedVersion": "Препоръчана",
|
||||
"anyVersion": "всяка"
|
||||
},
|
||||
"patchSelectorCard": {
|
||||
"widgetTitle": "Изберете модификации",
|
||||
"widgetTitleSelected": "Избрани модификации",
|
||||
"widgetSubtitle": "Първо изберете приложение",
|
||||
"widgetEmptySubtitle": "Няма избрани модификации"
|
||||
},
|
||||
"socialMediaCard": {
|
||||
"widgetTitle": "Социални мрежи",
|
||||
"widgetSubtitle": "Открийте ни онлайн!"
|
||||
},
|
||||
"appSelectorView": {
|
||||
"viewTitle": "Изберете приложение",
|
||||
"searchBarHint": "Търсене на приложение",
|
||||
"storageButton": "Хранилище",
|
||||
"errorMessage": "Избраното приложение не може да се използва"
|
||||
},
|
||||
"patchesSelectorView": {
|
||||
"viewTitle": "Изберете модификации",
|
||||
"searchBarHint": "Търсене на модификации",
|
||||
"doneButton": "Готово",
|
||||
"recommended": "Препоръчано",
|
||||
"all": "Всички",
|
||||
"none": "Нито един",
|
||||
"loadPatchesSelection": "Заредете избраните модификации",
|
||||
"noSavedPatches": "Няма запазени модификации за избраното приложение.\nНатиснете Готово за да запазите текущия избор.",
|
||||
"noPatchesFound": "Няма налични модификации за избраното приложение",
|
||||
"selectAllPatchesWarningContent": "Напът сте да изберете всички патчове, дори и тези, които не са препоръчителни, а това може да доведе до нежелани промени."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Изборът на тази модификация може да доведе до грешки.\n\nВерсия: {packageVersion}\nПоддържани версии: {supportedVersions}",
|
||||
"unsupportedPatchVersion": "Модификацията не се поддържа за тази версия на приложението. Активираите експерименталната опция в настройките за да продължите."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Инсталатор",
|
||||
"installButton": "Инсталиране",
|
||||
"installRootButton": "Инсталиране с root",
|
||||
"openButton": "Отвори",
|
||||
"shareButton": "Споделяне на файла",
|
||||
"notificationTitle": "ReVanced Мениджър модифицира",
|
||||
"notificationText": "Натиснете за да се върнете при инсталатора",
|
||||
"shareApkMenuOption": "Споделяне на APK",
|
||||
"exportApkMenuOption": "Експортиране на APK",
|
||||
"shareLogMenuOption": "Сподели логовете",
|
||||
"installErrorDialogTitle": "Грешка",
|
||||
"installErrorDialogText1": "Инсталиране с root не е възможно с настоящият избор на модификации.\nМодифицирайте приложението наново или изберете опция без root.",
|
||||
"installErrorDialogText2": "Инсталиране без root не е възможно с настоящият избор на модификации.\nМодифицирайте приложението наново или изберете опция с root, ако устройството ви има такъв.",
|
||||
"installErrorDialogText3": "Инсталиране с root не е възможно понеже АРК файла бе избран от хранилището.\nИзберете инсталирано приложение или опция без root.",
|
||||
"noExit": "Инсталаторът все още работи, не може да излезе..."
|
||||
},
|
||||
"settingsView": {
|
||||
"widgetTitle": "Настройки",
|
||||
"appearanceSectionTitle": "Облик",
|
||||
"teamSectionTitle": "Екип",
|
||||
"infoSectionTitle": "Информация",
|
||||
"advancedSectionTitle": "Разширени",
|
||||
"logsSectionTitle": "Логове",
|
||||
"darkThemeLabel": "Тъмен режим",
|
||||
"darkThemeHint": "Добре дошъл на тъмната страна",
|
||||
"dynamicThemeLabel": "Материална Тема",
|
||||
"dynamicThemeHint": "Насладете се на преживяване по-близо до устройството си",
|
||||
"languageLabel": "Език",
|
||||
"englishOption": "Английски",
|
||||
"sourcesLabel": "Източници",
|
||||
"sourcesLabelHint": "Конфигурирайте собствени източници",
|
||||
"hostRepositoryLabel": "API на хранилището",
|
||||
"orgPatchesLabel": "Организация на модификациите",
|
||||
"sourcesPatchesLabel": "Източник на модификациите",
|
||||
"orgIntegrationsLabel": "Организация на интеграциите",
|
||||
"sourcesIntegrationsLabel": "Източник на интеграциите",
|
||||
"sourcesResetDialogTitle": "Нулиране",
|
||||
"sourcesResetDialogText": "Искате ли да възстановите източниците до стойностите им по подразбиране?",
|
||||
"apiURLResetDialogText": "Искате ли да възстановите АРI URL до стойността му по подразбиране?",
|
||||
"contributorsLabel": "Хора, които допринесоха",
|
||||
"contributorsHint": "Списък с хората, допринесли за ReVanced",
|
||||
"logsLabel": "Логове",
|
||||
"logsHint": "Сподели логовете на мениджъра",
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Конфигурирайте собствен API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Поддръжка на експериментални модификации",
|
||||
"experimentalUniversalPatchesHint": "Показване на всички приложения за използване с универсални модификации, зареждането на списъкът с приложения може да е по-бавно",
|
||||
"experimentalPatchesLabel": "Поддръжка на експериментални модификации",
|
||||
"experimentalPatchesHint": "Разрешете използването на неподдържани модификации във всяка версия на приложението",
|
||||
"enabledExperimentalPatches": "Поддръжката на експериментални модификации е активирана",
|
||||
"exportSectionTitle": "Импортиране и експортиране",
|
||||
"aboutLabel": "Относно",
|
||||
"snackbarMessage": "Копирано",
|
||||
"sentryLabel": "Sentry логване",
|
||||
"sentryHint": "Пращайте анонимни логове за да ни помогнете да подобрим ReVanced Manager",
|
||||
"restartAppForChanges": "Рестартирайте приложението, за да се приложат промените",
|
||||
"deleteKeystoreLabel": "Изтриване на keystore",
|
||||
"deleteKeystoreHint": "Изтриване на keystore използван за подписване на приложението",
|
||||
"deletedKeystore": "Keystore изтрит",
|
||||
"deleteTempDirLabel": "Изтриване на временни файлове",
|
||||
"deleteTempDirHint": "Изтриване на неизползвани временни файлове",
|
||||
"deletedTempDir": "Временните файлове са изтрити",
|
||||
"exportPatchesLabel": "Експортиране на избраните модификации",
|
||||
"exportPatchesHint": "Експортиране на избраните модификации като JSON файл",
|
||||
"exportedPatches": "Избраните модификации са експортирани",
|
||||
"noExportFileFound": "Няма избрани модификации за експортиране",
|
||||
"importPatchesLabel": "Импортиране на избраните модификации",
|
||||
"importPatchesHint": "Импортиране на избраните модификации от JSON файл",
|
||||
"importedPatches": "Избраните модификации са импортирани",
|
||||
"resetStoredPatchesLabel": "Нулиране на модификациите",
|
||||
"resetStoredPatchesHint": "Нулиране на избраните запазени модификации",
|
||||
"resetStoredPatches": "Избраните модификации са нулирани",
|
||||
"jsonSelectorErrorMessage": "Избраният JSON файл не може да се изплозва",
|
||||
"deleteLogsLabel": "Изтриване на логовете",
|
||||
"deleteLogsHint": "Изтриване на събраните логове на мениджърът",
|
||||
"deletedLogs": "Логовете са изтрити"
|
||||
},
|
||||
"appInfoView": {
|
||||
"widgetTitle": "Информация за приложението",
|
||||
"openButton": "Отвори",
|
||||
"uninstallButton": "Деинсталирай",
|
||||
"patchButton": "Модифицирайте",
|
||||
"unpatchButton": "Премахнете модификации",
|
||||
"unpatchDialogText": "Сигурни ли сте че искате да премахнете модификациите?",
|
||||
"rootDialogTitle": "Грешка",
|
||||
"rootDialogText": "Приложението е инсталирано с superuser разрешения, но в момента ReVanced Manager няма разрешения. Моля, първо дайте superuser разрешения.",
|
||||
"packageNameLabel": "Име на пакета",
|
||||
"originalPackageNameLabel": "Оригинално име на пакета",
|
||||
"installTypeLabel": "Тип инсталация",
|
||||
"rootTypeLabel": "Вариант с root достъп",
|
||||
"nonRootTypeLabel": "Без root",
|
||||
"patchedDateLabel": "Дата на модификацията",
|
||||
"patchedDateHint": "на {date} в {time}",
|
||||
"appliedPatchesLabel": "Приложени модификации",
|
||||
"appliedPatchesHint": "{quantity} приложени модификации",
|
||||
"updateNotImplemented": "Тази функция все още не е внедрена"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "Хора, които допринесоха",
|
||||
"patcherContributors": "Допринесли към пачърът",
|
||||
"patchesContributors": "Допринесли към модификациите",
|
||||
"integrationsContributors": "Допринесли към интеграциите",
|
||||
"cliContributors": "Допринесли към CLI",
|
||||
"managerContributors": "Допринесли към мениджъра"
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Afbryd",
|
||||
"updateButton": "Opdatér",
|
||||
"enabledLabel": "Aktiveret",
|
||||
"disabledLabel": "Deaktiveret",
|
||||
"yesButton": "Ja",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Ingen patchede apps installeret",
|
||||
"installed": "Installeret",
|
||||
"updateDialogTitle": "Opdater Manager",
|
||||
"updateDialogText": "Er du sikker på, at du vil downloade og opdatere ReVanced Manager?",
|
||||
"updateChangelogTitle": "Ændringslog",
|
||||
"notificationTitle": "Opdatering downloadet",
|
||||
"notificationText": "Tryk for at installere opdateringen",
|
||||
"downloadingMessage": "Downloader opdatering...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "Alle",
|
||||
"none": "Ingen",
|
||||
"loadPatchesSelection": "Indlæs patches udvælgelse",
|
||||
"noSavedPatches": "Ingen gemte patches til den valgte app\nTryk på Udført for at gemme det aktuelle valg",
|
||||
"noSavedPatches": "Ingen gemte patches til den valgte app.\nTryk på Udført for at gemme det aktuelle valg.",
|
||||
"noPatchesFound": "Ingen patches fundet til den valgte app",
|
||||
"selectAllPatchesWarningContent": "Du er ved at vælge alle patches, der inkluderer ikke-anbefalede patches, og kan forårsage uønsket adfærd."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Valg af denne patch kan medføre patching-fejl.\n\nApp version: {packageVersion}\nUnderstøttede versioner:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Patch er ikke understøttet for denne app-version. Aktivér eksperimentel indstilling i indstillinger for at fortsætte."
|
||||
"unsupportedPatchVersion": "Patch er ikke understøttet for denne app-version. Aktivér den eksperimentelle indstilling i indstillinger for at fortsætte."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Installationsprogram",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Engelsk",
|
||||
"sourcesLabel": "Kilder",
|
||||
"sourcesLabelHint": "Konfigurér dine brugerdefinerede kilder",
|
||||
"hostRepositoryLabel": "Repository API",
|
||||
"orgPatchesLabel": "Organisation for Patches",
|
||||
"sourcesPatchesLabel": "Kilde til Patches",
|
||||
"orgIntegrationsLabel": "Organisation for Integrationer",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Konfigurer din brugerdefineret API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Understøttelse af eksperimentelle universale patches",
|
||||
"experimentalUniversalPatchesHint": "Vis alle applikationer der skal bruges med universelle patches, indlæsningslisten over apps kan være langsommere",
|
||||
"experimentalPatchesLabel": "Understøttelse af eksperimentelle patches",
|
||||
"experimentalPatchesHint": "Aktiver brugen af ikke-understøttede patches i enhver app version",
|
||||
"enabledExperimentalPatches": "Understøttelse af eksperimentelle patches aktiveret",
|
||||
@ -148,7 +152,7 @@
|
||||
"deleteKeystoreHint": "Slet keystore der bruges til at signere appen",
|
||||
"deletedKeystore": "Keystore slettet",
|
||||
"deleteTempDirLabel": "Slet midlertidige filer",
|
||||
"deleteTempDirHint": "Slet de ubrugte midlertidige filer",
|
||||
"deleteTempDirHint": "Slet ubrugte midlertidige filer",
|
||||
"deletedTempDir": "Midlertidige filer slettet",
|
||||
"exportPatchesLabel": "Eksportér valgte patches",
|
||||
"exportPatchesHint": "Eksportér valgte patches til en JSON-fil",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Abbrechen",
|
||||
"updateButton": "Aktualisieren",
|
||||
"enabledLabel": "Aktiviert",
|
||||
"disabledLabel": "Deaktiviert",
|
||||
"yesButton": "Ja",
|
||||
@ -21,11 +22,11 @@
|
||||
"noInstallations": "Keine gepatchten Apps installiert",
|
||||
"installed": "Installiert",
|
||||
"updateDialogTitle": "Manager aktualisieren",
|
||||
"updateDialogText": "Bist du sicher, dass du den ReVanced Manager herunterladen und aktualisieren möchtest?",
|
||||
"updateChangelogTitle": "Änderungsverlauf",
|
||||
"notificationTitle": "Update heruntergeladen",
|
||||
"notificationText": "Tippen um Update zu installieren",
|
||||
"downloadingMessage": "Aktualisierung wird heruntergeladen...",
|
||||
"installingMessage": "Aktualisierung wird installiert...",
|
||||
"installingMessage": "Update wird installiert...",
|
||||
"errorDownloadMessage": "Aktualisierung konnte nicht heruntergeladen werden",
|
||||
"errorInstallMessage": "Aktualisierung konnte nicht installiert werden",
|
||||
"noConnection": "Keine Internetverbindung",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "Alle",
|
||||
"none": "Keine",
|
||||
"loadPatchesSelection": "Patchauswahl laden",
|
||||
"noSavedPatches": "Keine gespeicherten Patches für die ausgewählte App\nDrücken Sie Fertig, um die aktuelle Auswahl zu speichern",
|
||||
"noSavedPatches": "Keine gespeicherten Patches für die ausgewählte App. Drücke „Fertig” zum Speichern der aktuellen Auswahl.",
|
||||
"noPatchesFound": "Keine Patches für die ausgewählte App gefunden",
|
||||
"selectAllPatchesWarningContent": "Du bist dabei alle Patches auszuwählen, dies beinhaltet nicht empfohlene Patches und kann zu unerwünschtem Verhalten führen."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Die Auswahl dieses Patches kann zu Fehlern beim Patchen führen.\n\nApp-Version: {packageVersion}\nUnterstützte Versionen:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Patch wird für diese App-Version nicht unterstützt. Aktiviere das experimentelle Feature in den Einstellungen, um fortzufahren."
|
||||
"unsupportedPatchVersion": "Der Patch unterstützt diese Version der App nicht. Aktiviere in den Einstellungen die experimentellen Funktionen, um fortzufahren."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Installer",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Englisch",
|
||||
"sourcesLabel": "Quellen",
|
||||
"sourcesLabelHint": "Konfiguriere deine eigenen Quellen",
|
||||
"hostRepositoryLabel": "Repository-API",
|
||||
"orgPatchesLabel": "Patches Organisation",
|
||||
"sourcesPatchesLabel": "Quelle für Patche",
|
||||
"orgIntegrationsLabel": "Integrationen Organisation",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API-URL",
|
||||
"apiURLHint": "Konfiguriere deine eigene API-URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Unterstützung für universelle Patches (experimentell)",
|
||||
"experimentalUniversalPatchesHint": "Zeigt alle Apps zur Verwendung mit universellen Patches, dadurch lädt die App-Liste evtl. langsamer",
|
||||
"experimentalPatchesLabel": "Experimentelle Patches aktiviert",
|
||||
"experimentalPatchesHint": "Erlaubt die Benutzung von nicht unterstützten Patches mit jeder App Version",
|
||||
"enabledExperimentalPatches": "Experimentelle Patches Unterstützung aktiviert",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "Εντάξει",
|
||||
"cancelButton": "Ακύρωση",
|
||||
"updateButton": "Ενημέρωση",
|
||||
"enabledLabel": "Ενεργό",
|
||||
"disabledLabel": "Ανενεργό",
|
||||
"yesButton": "Ναι",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Δεν έχουν εγκατασταθεί τροποποιημένες εφαρμογές",
|
||||
"installed": "Εγκατεστημένες",
|
||||
"updateDialogTitle": "Ενημέρωση του Manager",
|
||||
"updateDialogText": "Είστε βέβαιοι ότι θέλετε να κατεβάσετε και να ενημερώσετε το ReVanced Manager;",
|
||||
"updateChangelogTitle": "Τι νέο υπάρχει",
|
||||
"notificationTitle": "Η λήψη της ενημέρωσης ολοκληρώθηκε",
|
||||
"notificationText": "Πατήστε για την εγκατάσταση της ενημέρωσης",
|
||||
"downloadingMessage": "Λήψη ενημέρωσης...",
|
||||
@ -81,7 +82,7 @@
|
||||
"all": "Όλα",
|
||||
"none": "Κανένα",
|
||||
"loadPatchesSelection": "Φόρτωση επιλεγμένων τροποποιήσεων",
|
||||
"noSavedPatches": "Δεν υπάρχουν αποθηκευμένες τροποποιήσεις για την εφαρμογή που επιλέξατε.\nΠατήστε «Τέλος» για να αποθηκεύσετε τις τωρινές επιλογές σας",
|
||||
"noSavedPatches": "Δεν υπάρχουν αποθηκευμένες τροποποιήσεις για την εφαρμογή που επιλέξατε.\nΠατήστε «Τέλος» για να αποθηκεύσετε τις τωρινές επιλογές σας.",
|
||||
"noPatchesFound": "Δε βρέθηκαν τροποποιήσεις για την επιλεγμένη εφαρμογή",
|
||||
"selectAllPatchesWarningContent": "Πρόκειται να επιλέξτε όλες τις μη συνιστώμενες τροποποιήσεις, μπορεί να προκληθεί ανεπιθύμητη συμπεριφορά."
|
||||
},
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Αγγλικά",
|
||||
"sourcesLabel": "Πηγές",
|
||||
"sourcesLabelHint": "Διαμορφώστε τις δικές σας προσαρμοσμένες πηγές",
|
||||
"hostRepositoryLabel": "Αποθετήριο API",
|
||||
"orgPatchesLabel": "Οργάνωση τροποποιήσεων",
|
||||
"sourcesPatchesLabel": "Πηγή τροποποιήσεων",
|
||||
"orgIntegrationsLabel": "Οργάνωση ενσωματώσεων",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Διαμορφώστε το δικό σας προσαρμοσμένο API URL",
|
||||
"selectApiURL": "Διεύθυνση URL API",
|
||||
"experimentalUniversalPatchesLabel": "Υποστήριξη γενικευμένων πειραματικών τροποποιήσεων",
|
||||
"experimentalUniversalPatchesHint": "Εμφάνιση όλων των εφαρμογών (για χρήση τροποποιήσεων που λειτουργούν σε κάθε εφαρμογή), η φόρτωση της λίστας εφαρμογών μπορεί να καθυστερεί περισσότερο",
|
||||
"experimentalPatchesLabel": "Πειραματική υποστήριξη τροποποιήσεων",
|
||||
"experimentalPatchesHint": "Ενεργοποιήστε το για χρήση μη υποστηριζόμενων τροποποιήσεων, ανεξαρτήτως έκδοσης εφαρμογής",
|
||||
"enabledExperimentalPatches": "Πειραματική υποστήριξη τροποποιήσεων ενεργή",
|
||||
|
@ -4,6 +4,8 @@
|
||||
"updateButton": "Update",
|
||||
"enabledLabel": "Enabled",
|
||||
"disabledLabel": "Disabled",
|
||||
"installed":"Installed: {version}",
|
||||
"suggested":"Suggested: {version}",
|
||||
"yesButton": "Yes",
|
||||
"noButton": "No",
|
||||
"warning": "Warning",
|
||||
@ -13,6 +15,7 @@
|
||||
"settingsTab": "Settings"
|
||||
},
|
||||
"homeView": {
|
||||
"refreshSuccess": "Refreshed successfully",
|
||||
"widgetTitle": "Dashboard",
|
||||
"updatesSubtitle": "Updates",
|
||||
"patchedSubtitle": "Patched applications",
|
||||
@ -47,7 +50,8 @@
|
||||
"patcherView": {
|
||||
"widgetTitle": "Patcher",
|
||||
"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": {
|
||||
"widgetTitle": "Select an application",
|
||||
@ -55,7 +59,7 @@
|
||||
"widgetSubtitle": "No application selected",
|
||||
"noAppsLabel": "No applications found",
|
||||
"currentVersion": "Current",
|
||||
"recommendedVersion": "Recommended",
|
||||
"suggestedVersion": "Suggested",
|
||||
"anyVersion": "any"
|
||||
},
|
||||
"patchSelectorCard": {
|
||||
@ -71,20 +75,25 @@
|
||||
"appSelectorView": {
|
||||
"viewTitle": "Select an application",
|
||||
"searchBarHint": "Search applications",
|
||||
"selectFromStorageButton": "Select from 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": {
|
||||
"viewTitle": "Select patches",
|
||||
"searchBarHint": "Search patches",
|
||||
"doneButton": "Done",
|
||||
"recommended": "Recommended",
|
||||
"all": "All",
|
||||
"default": "Default",
|
||||
"defaultTooltip": "Select all default patches",
|
||||
"none": "None",
|
||||
"noneTooltip": "Deselect all patches",
|
||||
"loadPatchesSelection": "Load patches selection",
|
||||
"noSavedPatches": "No saved patches for the selected app.\nPress Done to save current selection.",
|
||||
"noPatchesFound": "No patches found for the selected app",
|
||||
"selectAllPatchesWarningContent": "You are about to select all patches, that includes unrecommended patches and can cause unwanted behavior."
|
||||
"selectAllPatchesWarningContent": "You are about to select all patches, that includes non-suggested patches and can cause unwanted behavior."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nSupported versions:\n{supportedVersions}",
|
||||
@ -137,17 +146,18 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Configure your custom API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Experimental universal patches support",
|
||||
"experimentalUniversalPatchesHint": "Display all applications to use with universal patches, loading list of apps may be slower",
|
||||
"experimentalPatchesLabel": "Experimental patches support",
|
||||
"experimentalPatchesHint": "Enable usage of unsupported patches in any app version",
|
||||
"enabledExperimentalPatches": "Experimental patches support enabled",
|
||||
"exportSectionTitle": "Import & export",
|
||||
"aboutLabel": "About",
|
||||
"snackbarMessage": "Copied to clipboard",
|
||||
"sentryLabel": "Sentry logging",
|
||||
"sentryHint": "Send anonymous logs to help us improve ReVanced Manager",
|
||||
"restartAppForChanges": "Restart the app to apply changes",
|
||||
"deleteKeystoreLabel": "Delete keystore",
|
||||
"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",
|
||||
"deleteTempDirLabel": "Delete temporary files",
|
||||
"deleteTempDirHint": "Delete unused temporary files",
|
||||
@ -165,7 +175,17 @@
|
||||
"jsonSelectorErrorMessage": "Unable to use selected JSON file",
|
||||
"deleteLogsLabel": "Delete 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": {
|
||||
"widgetTitle": "App info",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Cancelar",
|
||||
"updateButton": "Actualizar",
|
||||
"enabledLabel": "Activado",
|
||||
"disabledLabel": "Desactivado",
|
||||
"yesButton": "Sí",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "No hay aplicaciones parcheadas instaladas",
|
||||
"installed": "Instalado",
|
||||
"updateDialogTitle": "Actualizar Manager",
|
||||
"updateDialogText": "¿Está seguro de que desea descargar y actualizar ReVanced Manager?",
|
||||
"updateChangelogTitle": "Registro de cambios",
|
||||
"notificationTitle": "Actualización descargada",
|
||||
"notificationText": "Toca para instalar la actualización",
|
||||
"downloadingMessage": "Descargando actualización...",
|
||||
@ -81,7 +82,7 @@
|
||||
"all": "Todos",
|
||||
"none": "Ninguno",
|
||||
"loadPatchesSelection": "Cargar selección de parches",
|
||||
"noSavedPatches": "No hay parches guardados para la app seleccionada\nPresione Hecho para guardar la selección actual",
|
||||
"noSavedPatches": "No hay parches guardados para la app seleccionada\nPresione Hecho para guardar la selección actual.",
|
||||
"noPatchesFound": "No se encontraron parches para la aplicación seleccionada",
|
||||
"selectAllPatchesWarningContent": "Está a punto de seleccionar todos los parches incluyendo parches no recomendados, lo cual puede causar comportamientos inesperados."
|
||||
},
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Inglés",
|
||||
"sourcesLabel": "Fuentes",
|
||||
"sourcesLabelHint": "Configure sus fuentes personalizadas",
|
||||
"hostRepositoryLabel": "Repositorio API",
|
||||
"orgPatchesLabel": "Organización de los parches",
|
||||
"sourcesPatchesLabel": "Fuente de los parches",
|
||||
"orgIntegrationsLabel": "Organización de integraciones",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "URL de la API",
|
||||
"apiURLHint": "Configure su URL personalizada para la API",
|
||||
"selectApiURL": "URL de la API",
|
||||
"experimentalUniversalPatchesLabel": "Soporte experimental para parches universales",
|
||||
"experimentalUniversalPatchesHint": "Mostrar todas las aplicaciones a usar con parches universales, la carga de la lista de aplicaciones puede ser más lenta",
|
||||
"experimentalPatchesLabel": "Soporte para parches experimentales",
|
||||
"experimentalPatchesHint": "Habilite el uso de parches no compatibles en cualquier versión de la app",
|
||||
"enabledExperimentalPatches": "Soporte para parches experimentales activado",
|
||||
@ -148,7 +152,7 @@
|
||||
"deleteKeystoreHint": "Eliminar el keystore usado para firmar la app",
|
||||
"deletedKeystore": "Keystore eliminado",
|
||||
"deleteTempDirLabel": "Borrar archivos temporales",
|
||||
"deleteTempDirHint": "Elimina los archivos temporales no utilizados",
|
||||
"deleteTempDirHint": "Eliminar archivos temporales no utilizados",
|
||||
"deletedTempDir": "Archivos temporales eliminados",
|
||||
"exportPatchesLabel": "Exportar la selección de parches",
|
||||
"exportPatchesHint": "Exportar selección de parches a un archivo JSON",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Annuler",
|
||||
"updateButton": "Mise à jour",
|
||||
"enabledLabel": "Activé",
|
||||
"disabledLabel": "Désactivé",
|
||||
"yesButton": "Oui",
|
||||
@ -21,9 +22,9 @@
|
||||
"noInstallations": "Aucune application patchée n'est installée",
|
||||
"installed": "Installée",
|
||||
"updateDialogTitle": "Mettre à jour le Manager",
|
||||
"updateDialogText": "Êtes-vous sûr de vouloir télécharger et mettre à jour ReVanced Manager ?",
|
||||
"updateChangelogTitle": "Liste des changements",
|
||||
"notificationTitle": "Mise à jour téléchargée",
|
||||
"notificationText": "Appuyer pour installer la mise à jour",
|
||||
"notificationText": "Appuyez sur pour installer la mise à jour",
|
||||
"downloadingMessage": "Téléchargement de la mise à jour...",
|
||||
"installingMessage": "Installation de la mise à jour...",
|
||||
"errorDownloadMessage": "Impossible de télécharger la mise à jour",
|
||||
@ -51,7 +52,7 @@
|
||||
"appSelectorCard": {
|
||||
"widgetTitle": "Sélectionner une application",
|
||||
"widgetTitleSelected": "Application sélectionnée",
|
||||
"widgetSubtitle": "Aucune application n'a été sélectionnée",
|
||||
"widgetSubtitle": "Aucune application sélectionnée",
|
||||
"noAppsLabel": "Aucune application trouvée",
|
||||
"currentVersion": "Version actuelle",
|
||||
"recommendedVersion": "Version recommandée",
|
||||
@ -80,14 +81,14 @@
|
||||
"recommended": "Recommandé",
|
||||
"all": "Tout",
|
||||
"none": "Aucun",
|
||||
"loadPatchesSelection": "Charger les patches sélectionnés",
|
||||
"noSavedPatches": "Aucun patch enregistré pour l'application sélectionnée\nAppuyez sur Terminé pour enregistrer la sélection actuelle",
|
||||
"loadPatchesSelection": "Charger les patchs sélectionnés",
|
||||
"noSavedPatches": "Aucun patch enregistré pour l'application sélectionnée.\nAppuyez sur Terminé pour enregistrer la sélection actuelle.",
|
||||
"noPatchesFound": "Aucun patch n'a été trouvé pour l'application sélectionnée",
|
||||
"selectAllPatchesWarningContent": "Vous êtes sur le point de sélectionner tous les patchs, cela inclut des patchs non recommandés et peut causer des comportements indésirables."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Sélectionner ce patch pourrait entrainer des erreurs dans la modification.\n\nVersion de l'application: {packageVersion}\nVersions supportées:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Le patch n'est pas supporté pour cette version de l'application. Activez le commutateur expérimental dans les réglages pour continuer."
|
||||
"unsupportedDialogText": "La sélection de ce patch peut entraîner des erreurs.\n\nVersion de l'application : {packageVersion}\nVersions prises en charge :\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Le correctif n'est pas pris en charge pour cette version de l'application. Activez le bouton expérimental dans les paramètres pour continuer."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Installateur",
|
||||
@ -99,7 +100,7 @@
|
||||
"notificationText": "Appuyer pour revenir à l’installateur",
|
||||
"shareApkMenuOption": "Partager le fichier APK",
|
||||
"exportApkMenuOption": "Exporter l'APK",
|
||||
"shareLogMenuOption": "Partager le log",
|
||||
"shareLogMenuOption": "Partager le journal",
|
||||
"installErrorDialogTitle": "Erreur",
|
||||
"installErrorDialogText1": "L'installation Root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation non root.",
|
||||
"installErrorDialogText2": "L'installation Non-root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation en tant que Root.",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Anglais",
|
||||
"sourcesLabel": "Sources",
|
||||
"sourcesLabelHint": "Configurer vos sources personnalisées",
|
||||
"hostRepositoryLabel": "Dépôt de l'API",
|
||||
"orgPatchesLabel": "Organisation des patchs",
|
||||
"sourcesPatchesLabel": "Source des patches",
|
||||
"orgIntegrationsLabel": "Organisme d'intégration",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "URL de l'API",
|
||||
"apiURLHint": "Configurez l'URL de votre API personnalisée",
|
||||
"selectApiURL": "URL de l'API",
|
||||
"experimentalUniversalPatchesLabel": "Support expérimental des patchs universels",
|
||||
"experimentalUniversalPatchesHint": "Afficher toutes les applications à utiliser avec les patchs universels, le chargement de la liste des applications pourrait être lent",
|
||||
"experimentalPatchesLabel": "Support des patchs expérimentaux",
|
||||
"experimentalPatchesHint": "Activer l'utilisation des patchs non supportés dans n'importe quelle version de l'application",
|
||||
"enabledExperimentalPatches": "Support pour les patchs expérimentaux activé",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "Oke",
|
||||
"cancelButton": "Batal",
|
||||
"updateButton": "Perbarui",
|
||||
"enabledLabel": "Aktif",
|
||||
"disabledLabel": "Nonaktif",
|
||||
"yesButton": "Ya",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Tidak ada aplikasi termodifikasi yang terpasang",
|
||||
"installed": "Terpasang",
|
||||
"updateDialogTitle": "Perbarui Manager",
|
||||
"updateDialogText": "Apakah Anda yakin ingin mengunduh dan memperbarui ReVanced Manager?",
|
||||
"updateChangelogTitle": "Catatan perubahan",
|
||||
"notificationTitle": "Pembaruan sudah diunduh",
|
||||
"notificationText": "Ketuk untuk memasang pembaharuan",
|
||||
"downloadingMessage": "Mengunduh pembaruan...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "Semua",
|
||||
"none": "Tidak ada",
|
||||
"loadPatchesSelection": "Tidak ada modifikasi yang terpilih",
|
||||
"noSavedPatches": "Tidak ada modifikasi tersimpan untuk aplikasi terpilih",
|
||||
"noSavedPatches": "Tidak ada tambalan tersimpan untuk aplikasi terpilih.\nTekan \"Sudah\" untuk menyimpan seleksi saat ini.",
|
||||
"noPatchesFound": "Modifikasi tidak ditemukan untuk aplikasi terpilih",
|
||||
"selectAllPatchesWarningContent": "Anda akan memilih semua modifikasi, termasuk modifikasi yang tidak direkomendasikan dan dapat mengakibatkan hal yang tidak diinginkan."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Menggunakan modifikasi ini dapat menyebabkan error pada saat memodifikasi aplikasi\n\nVersi aplikasi: {packageVersion}\nVersi yang mendukung:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Modifikasi tidak didukung untuk versi aplikasi ini. Aktifkan eksperimental dalam pengaturan untuk melanjutkan."
|
||||
"unsupportedPatchVersion": "Patch tidak didukung untuk versi aplikasi ini. Aktifkan tombol eksperimental di pengaturan untuk melanjutkan."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Pemasang",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Inggris",
|
||||
"sourcesLabel": "Sumber",
|
||||
"sourcesLabelHint": "Konfigurasi sumber kustom Anda",
|
||||
"hostRepositoryLabel": "API Repositori",
|
||||
"orgPatchesLabel": "Organisasi Modifikasi",
|
||||
"sourcesPatchesLabel": "Sumber Modifikasi",
|
||||
"orgIntegrationsLabel": "Organisasi Intergrasi",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "URL API",
|
||||
"apiURLHint": "Konfigurasi URL API kustom Anda",
|
||||
"selectApiURL": "URL API",
|
||||
"experimentalUniversalPatchesLabel": "Dukungan tambalan universal eksperimental",
|
||||
"experimentalUniversalPatchesHint": "Menampilkan semua aplikasi yang digunakan tambalan universal, pemuatan daftar aplikasi akan membuat lambat",
|
||||
"experimentalPatchesLabel": "Dukungan Modifikasi Eksperimental",
|
||||
"experimentalPatchesHint": "Aktifkan untuk menggunakan modifikasi yang tidak didukung di versi aplikasi apa pun",
|
||||
"enabledExperimentalPatches": "Modifikasi eksperimental diaktifkan",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Annulla",
|
||||
"updateButton": "Aggiorna",
|
||||
"enabledLabel": "Attivo",
|
||||
"disabledLabel": "Disattivato",
|
||||
"yesButton": "Sì",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Non è stata installata nessuna applicazione modificata",
|
||||
"installed": "Installate",
|
||||
"updateDialogTitle": "Aggiorna Manager",
|
||||
"updateDialogText": "Sei sicuro di voler scaricare e aggiornare ReVanced Manager?",
|
||||
"updateChangelogTitle": "Changelog",
|
||||
"notificationTitle": "Aggiornamento scaricato",
|
||||
"notificationText": "Tocca per installare l'aggiornamento",
|
||||
"downloadingMessage": "Sto scaricando l'aggiornamento...",
|
||||
@ -81,7 +82,7 @@
|
||||
"all": "Tutte",
|
||||
"none": "Nessuna",
|
||||
"loadPatchesSelection": "Carica selezione patch",
|
||||
"noSavedPatches": "Nessuna patch salvata per l'app selezionata\nPremi Fatto per salvare la selezione corrente",
|
||||
"noSavedPatches": "Nessuna patch salvata per l'app selezionata.\nPremi Fatto per salvare la selezione corrente.",
|
||||
"noPatchesFound": "Nessuna patch trovata per l'applicazione selezionata",
|
||||
"selectAllPatchesWarningContent": "Stai per selezionare tutte le patch, incluse patch non consigliate che potrebbero causare comportamenti indesiderati."
|
||||
},
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "English",
|
||||
"sourcesLabel": "Sorgenti",
|
||||
"sourcesLabelHint": "Configura le tue sorgenti personalizzate",
|
||||
"hostRepositoryLabel": "Repository API",
|
||||
"orgPatchesLabel": "Organizzazione Patch",
|
||||
"sourcesPatchesLabel": "Sorgente Patch",
|
||||
"orgIntegrationsLabel": "Organizzazione Integrazioni",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "URL API",
|
||||
"apiURLHint": "Configura il tuo URL delle API personalizzato",
|
||||
"selectApiURL": "URL API",
|
||||
"experimentalUniversalPatchesLabel": "Supporto per patch universali sperimentali",
|
||||
"experimentalUniversalPatchesHint": "Visualizza tutte le applicazioni da utilizzare con le patch universali, il caricamento dell'elenco delle app potrebbe essere più lento",
|
||||
"experimentalPatchesLabel": "Supporto per patch sperimentali",
|
||||
"experimentalPatchesHint": "Abilita l'utilizzo di patch non supportate in qualsiasi versione dell'app",
|
||||
"enabledExperimentalPatches": "Supporto patch sperimentali abilitato",
|
||||
|
200
assets/i18n/lt_LT.json
Normal file
@ -0,0 +1,200 @@
|
||||
{
|
||||
"okButton": "Gerai",
|
||||
"cancelButton": "Atšaukti",
|
||||
"updateButton": "Atnaujinti",
|
||||
"enabledLabel": "Įjungta",
|
||||
"disabledLabel": "Išjungta",
|
||||
"yesButton": "Taip",
|
||||
"noButton": "Ne",
|
||||
"warning": "Įspėjimas",
|
||||
"navigationView": {
|
||||
"dashboardTab": "Valdymo skydas",
|
||||
"patcherTab": "Patcher",
|
||||
"settingsTab": "Nustatymai"
|
||||
},
|
||||
"homeView": {
|
||||
"widgetTitle": "Valdymo skydas",
|
||||
"updatesSubtitle": "Atnaujinimai",
|
||||
"patchedSubtitle": "Modifikuotos programos",
|
||||
"updatesAvailable": "Galimi atnaujinimai",
|
||||
"noUpdates": "Atnaujinimų nėra",
|
||||
"WIP": "Vykdomi darbai...",
|
||||
"noInstallations": "Nėra įdiegtų modifikuotų programų",
|
||||
"installed": "Įdiegta",
|
||||
"updateDialogTitle": "Atnaujinti Manager",
|
||||
"updateChangelogTitle": "Pakeitimų sąrašas",
|
||||
"notificationTitle": "Atnaujinimas atsiųstas",
|
||||
"notificationText": "Paspauskite, kad įdiegtumėte naujinimą",
|
||||
"downloadingMessage": "Atsiunčiamas atnaujinimas...",
|
||||
"installingMessage": "Įdiegiamas atnaujinimas...",
|
||||
"errorDownloadMessage": "Nepavyksta atsisiųsti atnaujinimo",
|
||||
"errorInstallMessage": "Nepavyksta įdiegti atnaujinimo",
|
||||
"noConnection": "Nėra interneto ryšio",
|
||||
"updatesDisabled": "Modifikuotų programų atnaujinimas šiuo metu išjungtas. Vėl modifikuokite programą."
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Modifikuoti",
|
||||
"infoButton": "Info",
|
||||
"changelogLabel": "Pakeitimai"
|
||||
},
|
||||
"latestCommitCard": {
|
||||
"loadingLabel": "Kraunama...",
|
||||
"timeagoLabel": "Prieš {time}",
|
||||
"patcherLabel": "Patcher: ",
|
||||
"managerLabel": "Manager: ",
|
||||
"updateButton": "Atnaujinti Manager"
|
||||
},
|
||||
"patcherView": {
|
||||
"widgetTitle": "Patcher",
|
||||
"patchButton": "Modifikuoti",
|
||||
"patchDialogText": "Jūs pasirinkote resource modifikaciją ir padalinta APK instaliacija buvo aptikta, tai modifikavimo klaidos gali atsitikti.\nAr esate tikri kad norite tęsti modifikuoti padalytą APK?"
|
||||
},
|
||||
"appSelectorCard": {
|
||||
"widgetTitle": "Pasirinkite programą",
|
||||
"widgetTitleSelected": "Pasirinkta programa",
|
||||
"widgetSubtitle": "Nepasirinkta programa",
|
||||
"noAppsLabel": "Nerastos jokios programos",
|
||||
"currentVersion": "Dabartinė",
|
||||
"recommendedVersion": "Rekomenduojama",
|
||||
"anyVersion": "bet kokia"
|
||||
},
|
||||
"patchSelectorCard": {
|
||||
"widgetTitle": "Pasirinkite modifikacijas",
|
||||
"widgetTitleSelected": "Pasirinktos modifikacijos",
|
||||
"widgetSubtitle": "Pirmiausia pasirinkite programą",
|
||||
"widgetEmptySubtitle": "Nepasirinkta jokių modifikacijų"
|
||||
},
|
||||
"socialMediaCard": {
|
||||
"widgetTitle": "Socialiniai tinklai",
|
||||
"widgetSubtitle": "Mes esame internete!"
|
||||
},
|
||||
"appSelectorView": {
|
||||
"viewTitle": "Pasirinkite programą",
|
||||
"searchBarHint": "Ieškoti programų",
|
||||
"storageButton": "Saugykla",
|
||||
"errorMessage": "Neina naudoti parinktos programos"
|
||||
},
|
||||
"patchesSelectorView": {
|
||||
"viewTitle": "Pasirinkti modifikacijas",
|
||||
"searchBarHint": "Ieškoti modifikacijų",
|
||||
"doneButton": "Atlikta",
|
||||
"recommended": "Rekomenduojama",
|
||||
"all": "Visi",
|
||||
"none": "Nėra",
|
||||
"loadPatchesSelection": "Įkelti modifikacijų pasirinkimą",
|
||||
"noSavedPatches": "Nėra išsaugotų pasirinktos programos modifikacijų.\nPaspauskite Atlikta, kad išsaugotumėte dabartinį pasirinkimą.",
|
||||
"noPatchesFound": "Nerasta modifikacijų pasirinktai programai",
|
||||
"selectAllPatchesWarningContent": "Jūs pasirinksite visas modifikacijas, tai įskaito ir nerekomenduojamas modifikacijas ir tai gali sukelti nepageidaujama elgesį."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Šios modifikacijos pasirinkimas gali sukelti modifikavimo klaidų.\n\nProgramos versija: {packageVersion}\npalaikomos versijos:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Modifikacija nepalaikoma šios programos versijai. Norėdami tęsti, nustatymuose įjunkite eksperimentinį jungiklį."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Įdiegėjas",
|
||||
"installButton": "Įdiegti",
|
||||
"installRootButton": "Įdiegti kaip Root",
|
||||
"openButton": "Atidaryti",
|
||||
"shareButton": "Bendrinti failą",
|
||||
"notificationTitle": "ReVanced Manager modifikuoja programą",
|
||||
"notificationText": "Paspauskite sugrįžti į įdiegėją",
|
||||
"shareApkMenuOption": "Bendrinti APK",
|
||||
"exportApkMenuOption": "Eksportuoti APK",
|
||||
"shareLogMenuOption": "Bendrinti įrašus",
|
||||
"installErrorDialogTitle": "Klaida",
|
||||
"installErrorDialogText1": "Root instaliacija negalima su dabartinėm pasirinktom modifikacijom.\nPer modifikuok programą arba pasirink ne root instaliaciją.",
|
||||
"installErrorDialogText2": "Ne root instaliacija negalima su dabartinėm pasirinktom modifikacijom.\nPer modifikuok programą arba pasirink root instaliaciją jei tavo įrenginys turi root.",
|
||||
"installErrorDialogText3": "Root instaliacija negalima nes originalus APK buvo pasirinktas iš saugyklos.\nPasirink jau instaliuotą programą arba pasirink ne root instaliaciją.",
|
||||
"noExit": "Diegimo programa vis dar veikia, negalima išeiti..."
|
||||
},
|
||||
"settingsView": {
|
||||
"widgetTitle": "Nustatymai",
|
||||
"appearanceSectionTitle": "Išvaizda",
|
||||
"teamSectionTitle": "Komanda",
|
||||
"infoSectionTitle": "Info",
|
||||
"advancedSectionTitle": "Išplėstiniai nustatymai",
|
||||
"logsSectionTitle": "Įrašai",
|
||||
"darkThemeLabel": "Tamsus rėžimas",
|
||||
"darkThemeHint": "Sveikas atvykęs į tamsiąją pusę",
|
||||
"dynamicThemeLabel": "Material You",
|
||||
"dynamicThemeHint": "Mėgaukis patirtimi artimiau tavo įrenginiui",
|
||||
"languageLabel": "Kalba",
|
||||
"englishOption": "Anglų",
|
||||
"sourcesLabel": "Šaltiniai",
|
||||
"sourcesLabelHint": "Sukonfigūruoti tavo nurodytus šaltinius",
|
||||
"hostRepositoryLabel": "API saugykla",
|
||||
"orgPatchesLabel": "Modifikacijų organizacija",
|
||||
"sourcesPatchesLabel": "Modifikacijų šaltinis",
|
||||
"orgIntegrationsLabel": "Integracijų organizacija",
|
||||
"sourcesIntegrationsLabel": "Integracijų šaltinis",
|
||||
"sourcesResetDialogTitle": "Nustatyti iš naujo",
|
||||
"sourcesResetDialogText": "Ar esi tikras kad nori iš naujo nustatyti šaltinius į jų numatytas reikšmes?",
|
||||
"apiURLResetDialogText": "Ar esi tikras kad nori iš naujo nustatyti API URL adresą į numatytą reikšmę?",
|
||||
"contributorsLabel": "Prisidėjusieji žmonės",
|
||||
"contributorsHint": "Žmonės prisidėję prie ReVanced",
|
||||
"logsLabel": "Įrašai",
|
||||
"logsHint": "Bendrinti Manager įrašus",
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Sukonfigūruoti tavo nurodytus API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Eksperimentinių universalių modifikacijų palaikymas",
|
||||
"experimentalUniversalPatchesHint": "Rodyti visas programas, skirtas naudoti su universaliom modifikacijom, krovimas programų sąrašo gali būti lėtesnis",
|
||||
"experimentalPatchesLabel": "Eksperimentinių modifikacijų palaikymas",
|
||||
"experimentalPatchesHint": "Įgalinti naudoti nepalaikomas modifikacijas bet kurioje programos versijoje",
|
||||
"enabledExperimentalPatches": "Eksperimentinių modifikacijų palaikymas įjungtas",
|
||||
"exportSectionTitle": "Importuoti ir eksportuoti",
|
||||
"aboutLabel": "Apie",
|
||||
"snackbarMessage": "Nukopijuota į iškarpinę",
|
||||
"sentryLabel": "Klaidų rinkimo įrankio įrašai",
|
||||
"sentryHint": "Siųsti anoniminius įrašus, kad padėtumėte mums tobulinti ReVanced Manager",
|
||||
"restartAppForChanges": "Iš naujo paleiskite programą, kad modifikacijos įsigaliotų",
|
||||
"deleteKeystoreLabel": "Ištrinti keystore",
|
||||
"deleteKeystoreHint": "Ištrinti keystore kuris naudojamas pasirašyti programą",
|
||||
"deletedKeystore": "Keystore ištrintas",
|
||||
"deleteTempDirLabel": "Ištrinti laikinus failus",
|
||||
"deleteTempDirHint": "Ištrinti nenaudojamus laikinus failus",
|
||||
"deletedTempDir": "Laikini failai ištrinti",
|
||||
"exportPatchesLabel": "Eksportuoti modifikacijų pasirinkimą",
|
||||
"exportPatchesHint": "Eksportuoti modifikacijų pasirinkimą į JSON failą",
|
||||
"exportedPatches": "Modifikacijų pasirinkimai eksportuoti",
|
||||
"noExportFileFound": "Nėra pasirinktų modifikacijų eksportuoti",
|
||||
"importPatchesLabel": "Įkelti modifikacijų pasirinkimą",
|
||||
"importPatchesHint": "Įkelti modifikacijų pasirinkimą iš JSON failo",
|
||||
"importedPatches": "Modifikacijų pasirinkimai įkelti",
|
||||
"resetStoredPatchesLabel": "Atstatyti modifikacijas",
|
||||
"resetStoredPatchesHint": "Atstatyti išsaugotą modifikacijų pasirinkimą",
|
||||
"resetStoredPatches": "Modifikacijų pasirinkimas buvo atstatytas",
|
||||
"jsonSelectorErrorMessage": "Neina naudoti pasirinkto JSON failo",
|
||||
"deleteLogsLabel": "Ištrinti įrašus",
|
||||
"deleteLogsHint": "Ištrinti surinktus Manager įrašus",
|
||||
"deletedLogs": "Įrašai ištrinti"
|
||||
},
|
||||
"appInfoView": {
|
||||
"widgetTitle": "Programos informacija",
|
||||
"openButton": "Atidaryti",
|
||||
"uninstallButton": "Išdiegti",
|
||||
"patchButton": "Modifikuoti",
|
||||
"unpatchButton": "Išimti modifikacijas",
|
||||
"unpatchDialogText": "Ar esi tikras kad nori išimti modifikacijas iš šios programos?",
|
||||
"rootDialogTitle": "Klaida",
|
||||
"rootDialogText": "Programa buvo įdiegta su supervartotojo leidimais, bet ReVanced Manager neturi leidimų.\nPirmiausia suteikite supervartotojo leidimus.",
|
||||
"packageNameLabel": "Paketo pavadinimas",
|
||||
"originalPackageNameLabel": "Originalus paketo pavadinimas",
|
||||
"installTypeLabel": "Įdiegimo tipas",
|
||||
"rootTypeLabel": "Root",
|
||||
"nonRootTypeLabel": "Ne root",
|
||||
"patchedDateLabel": "Modifikavimo data",
|
||||
"patchedDateHint": "{date} {time}",
|
||||
"appliedPatchesLabel": "Uždėtos modifikacijos",
|
||||
"appliedPatchesHint": "{quantity} uždėtos modifikacijos",
|
||||
"updateNotImplemented": "Ši funkcija dar neįgyvendinta"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "Prisidėjusieji žmonės",
|
||||
"patcherContributors": "Prisidėjusieji prie Patcher",
|
||||
"patchesContributors": "Prisidėjusieji prie Patcher",
|
||||
"integrationsContributors": "Prisidėjusieji prie integracijų",
|
||||
"cliContributors": "Prisidėjusieji prie CLI",
|
||||
"managerContributors": "Prisidėjusieji prie Manager"
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Anuluj",
|
||||
"updateButton": "Aktualizuj",
|
||||
"enabledLabel": "Włączone",
|
||||
"disabledLabel": "Wyłączone",
|
||||
"yesButton": "Tak",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Nie zainstalowano żadnych zmodyfikowanych aplikacji",
|
||||
"installed": "Zainstalowane",
|
||||
"updateDialogTitle": "Zaktualizuj Managera",
|
||||
"updateDialogText": "Czy na pewno chcesz pobrać i zaktualizować ReVanced Manager?",
|
||||
"updateChangelogTitle": "Lista zmian",
|
||||
"notificationTitle": "Pobrano aktualizacje",
|
||||
"notificationText": "Kliknij, aby zainstalować aktualizację",
|
||||
"downloadingMessage": "Pobieranie aktualizacji...",
|
||||
@ -29,7 +30,7 @@
|
||||
"errorDownloadMessage": "Nie udało się pobrać aktualizacji",
|
||||
"errorInstallMessage": "Nie udało się zainstalować aktualizacji",
|
||||
"noConnection": "Brak połączenia z internetem",
|
||||
"updatesDisabled": "Aktualizowanie nowej wersji aplikacji jest obecnie niemożliwe. Spróbuj zaktualizować aplikację jeszcze raz."
|
||||
"updatesDisabled": "Aktualizowanie załatanej aplikacji jest obecnie niemożliwe. Spróbuj załatać aplikację jeszcze raz."
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Łataj",
|
||||
@ -75,33 +76,33 @@
|
||||
},
|
||||
"patchesSelectorView": {
|
||||
"viewTitle": "Wybierz łatki",
|
||||
"searchBarHint": "Wyszukaj aktualizacje",
|
||||
"searchBarHint": "Wyszukaj łatki",
|
||||
"doneButton": "Gotowe",
|
||||
"recommended": "Rekomendowane",
|
||||
"recommended": "Zalecane",
|
||||
"all": "Wszystkie",
|
||||
"none": "Żadne",
|
||||
"loadPatchesSelection": "Wczytaj wybór aktualizacji",
|
||||
"noSavedPatches": "Brak zapisanych aktualizacji dla wybranej aplikacji\nNaciśnij Gotowe, aby zapisać bieżący wybór",
|
||||
"loadPatchesSelection": "Załaduj wybrane łatki",
|
||||
"noSavedPatches": "Brak zapisanych łatek dla wybranej aplikacji.\nNaciśnij Gotowe, aby zapisać bieżący wybór.",
|
||||
"noPatchesFound": "Nie znaleziono żadnych łatek dla wybranej aplikacji",
|
||||
"selectAllPatchesWarningContent": "Zamierzasz wybrać wszystkie łatki, włącznie z tymi niezalecanymi, które mogą powodować niepożądane zachowania."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Wybranie tej łatki może spowodować błędy podczas modyfikowania.\n\nWersja aplikacji: {packageVersion}\nAktualnie wspierana wersja:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Łatka nie jest obsługiwana dla tej wersji aplikacji. Włącz eksperymentalne przełączanie w ustawieniach, aby kontynuować."
|
||||
"unsupportedPatchVersion": "Łatka nie jest obsługiwana dla tej wersji aplikacji. Włącz opcje eksperymentalne w ustawieniach, aby kontynuować."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Instalator",
|
||||
"installButton": "Zainstaluj",
|
||||
"installRootButton": "Instaluj jako Root",
|
||||
"installRootButton": "Zainstaluj jako Root",
|
||||
"openButton": "Otwórz",
|
||||
"shareButton": "Udostępnij plik",
|
||||
"notificationTitle": "ReVanced Manager jest aktualizowany",
|
||||
"notificationTitle": "ReVanced Manager patchuje",
|
||||
"notificationText": "Dotknij, aby powrócić do instalatora",
|
||||
"shareApkMenuOption": "Udostępnij plik APK",
|
||||
"exportApkMenuOption": "Eksportuj APK",
|
||||
"shareLogMenuOption": "Udostępnij logi",
|
||||
"installErrorDialogTitle": "Błąd",
|
||||
"installErrorDialogText1": "Instalacja za pomocą roota nie jest możliwa przy obecnym wyborze aktualizacji.\nZaktualizuj aplikacje ponownie lub wybierz instalacje bez roota.",
|
||||
"installErrorDialogText1": "Instalacja za pomocą roota nie jest możliwa przy obecnym wyborze łatek.\nZałataj aplikacje ponownie lub wybierz instalacje bez roota.",
|
||||
"installErrorDialogText2": "Instalacja bez roota nie jest możliwa przy obecnym wyborze łatek.\nZałataj aplikacje ponownie lub wybierz instalacje za pomocą roota, jeśli masz zrootowane urządzenie.",
|
||||
"installErrorDialogText3": "Instalacja jako root nie jest możliwa, ponieważ oryginalny plik APK został wybrany z pamięci.\nWybierz zainstalowaną aplikację lub wybierz instalację bez używania roota.",
|
||||
"noExit": "Instalator jest nadal uruchomiony, nie można zakończyć jego działania..."
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Angielski",
|
||||
"sourcesLabel": "Źródła",
|
||||
"sourcesLabelHint": "Skonfiguruj własne źródła",
|
||||
"hostRepositoryLabel": "Repozytorium API",
|
||||
"orgPatchesLabel": "Organizacja łatek",
|
||||
"sourcesPatchesLabel": "Źródło łatek",
|
||||
"orgIntegrationsLabel": "Organizacja integracji",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "Adres API",
|
||||
"apiURLHint": "Ustaw własny adres API",
|
||||
"selectApiURL": "Adres API",
|
||||
"experimentalUniversalPatchesLabel": "Wsparcie dla uniwersalnych eksperymentalnych łatek",
|
||||
"experimentalUniversalPatchesHint": "Wyświetl wszystkie aplikacje do użycia z uniwersalnymi łatkami, ładowanie listy aplikacji może być wolniejsze",
|
||||
"experimentalPatchesLabel": "Wsparcie dla eksperymentalnych łatek",
|
||||
"experimentalPatchesHint": "Włącz użycie nieobsługiwanych łatek w dowolnej wersji aplikacji",
|
||||
"enabledExperimentalPatches": "Włączone wsparcie dla eksperymentalnych łatek",
|
||||
@ -150,16 +154,16 @@
|
||||
"deleteTempDirLabel": "Usuń pliki tymczasowe",
|
||||
"deleteTempDirHint": "Usuń nieużywane pliki tymczasowe",
|
||||
"deletedTempDir": "Pliki tymczasowe zostały usunięte",
|
||||
"exportPatchesLabel": "Eksportuj wybrane aktualizacje",
|
||||
"exportPatchesHint": "Eksportuj wybrane aktualizacje do pliku JSON",
|
||||
"exportedPatches": "Wyeksportowano wybrane aktualizacje",
|
||||
"noExportFileFound": "Brak wybranych aktualizacji do wyeksportowania",
|
||||
"importPatchesLabel": "Importuj wybrane aktualizacje",
|
||||
"importPatchesHint": "Importuj wybrane aktualizacje z pliku JSON",
|
||||
"importedPatches": "Zaimportowano wybrane aktualizacje",
|
||||
"resetStoredPatchesLabel": "Zresetuj aktualizacje",
|
||||
"resetStoredPatchesHint": "Zresetuj wybrane aktualizacje",
|
||||
"resetStoredPatches": "Wybrane aktualizacje zostały zresetowane",
|
||||
"exportPatchesLabel": "Eksportuj wybrane łatki",
|
||||
"exportPatchesHint": "Eksportuj wybrane łatki do pliku JSON",
|
||||
"exportedPatches": "Wyeksportowano wybór łatek",
|
||||
"noExportFileFound": "Brak wybranych łatek do wyeksportowania",
|
||||
"importPatchesLabel": "Importuj wybrane łatki",
|
||||
"importPatchesHint": "Importuj wybrane łatki z pliku JSON",
|
||||
"importedPatches": "Zaimportowano wybrane łatki",
|
||||
"resetStoredPatchesLabel": "Resetuj łatki",
|
||||
"resetStoredPatchesHint": "Zresetuj wybrane przechowywane łatki",
|
||||
"resetStoredPatches": "Wybrane łatki zostały zresetowane",
|
||||
"jsonSelectorErrorMessage": "Nie można użyć wybranego pliku JSON",
|
||||
"deleteLogsLabel": "Usuń logi",
|
||||
"deleteLogsHint": "Usuń logi zebrane przez menadżera",
|
||||
@ -178,17 +182,17 @@
|
||||
"originalPackageNameLabel": "Oryginalna nazwa pakietu",
|
||||
"installTypeLabel": "Typ instalacji",
|
||||
"rootTypeLabel": "Root",
|
||||
"nonRootTypeLabel": "Bez Roota",
|
||||
"nonRootTypeLabel": "Bez roota",
|
||||
"patchedDateLabel": "Data modyfikacji",
|
||||
"patchedDateHint": "{date} o godzinie {time}",
|
||||
"appliedPatchesLabel": "Zastosowane aktualizacje",
|
||||
"appliedPatchesHint": "Zastosowano {quantity} aktualizacji",
|
||||
"appliedPatchesLabel": "Zastosowane łatki",
|
||||
"appliedPatchesHint": "Zastosowano {quantity} łatek",
|
||||
"updateNotImplemented": "Ta funkcja nie została jeszcze zaimplementowana"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "Współtwórcy",
|
||||
"patcherContributors": "Współtwórcy patcher'a",
|
||||
"patchesContributors": "Współtwórcy aktualizacji",
|
||||
"patchesContributors": "Współtwórcy łatek",
|
||||
"integrationsContributors": "Współtwórcy integracji",
|
||||
"cliContributors": "Współtwórcy CLI",
|
||||
"managerContributors": "Współtwórcy menedżera"
|
||||
|
200
assets/i18n/pt_PT.json
Normal file
@ -0,0 +1,200 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Cancelar",
|
||||
"updateButton": "Atualizar",
|
||||
"enabledLabel": "Ativado",
|
||||
"disabledLabel": "Desativado",
|
||||
"yesButton": "Sim",
|
||||
"noButton": "Não",
|
||||
"warning": "Aviso",
|
||||
"navigationView": {
|
||||
"dashboardTab": "Painel de controlo",
|
||||
"patcherTab": "Patcher",
|
||||
"settingsTab": "Definições"
|
||||
},
|
||||
"homeView": {
|
||||
"widgetTitle": "Painel de controlo",
|
||||
"updatesSubtitle": "Atualizações",
|
||||
"patchedSubtitle": "Aplicações Modificadas",
|
||||
"updatesAvailable": "Atualizações disponíveis",
|
||||
"noUpdates": "Nenhuma atualização disponível",
|
||||
"WIP": "Em desenvolvimento...",
|
||||
"noInstallations": "Nenhuma aplicação modificada instalada",
|
||||
"installed": "Instalado",
|
||||
"updateDialogTitle": "Atualizar Manager",
|
||||
"updateChangelogTitle": "Alterações",
|
||||
"notificationTitle": "Atualização transferida",
|
||||
"notificationText": "Toque para instalar a atualização",
|
||||
"downloadingMessage": "A transferir a atualização...",
|
||||
"installingMessage": "A instalar atualização...",
|
||||
"errorDownloadMessage": "Não é possível transferir a atualização",
|
||||
"errorInstallMessage": "Não foi possível instalar a atualização",
|
||||
"noConnection": "Sem ligação à Internet",
|
||||
"updatesDisabled": "Atualizar uma aplicação modificada está atualmente desabilitado. Volta a modificar a aplicação."
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Modificar",
|
||||
"infoButton": "Informação",
|
||||
"changelogLabel": "Alterações"
|
||||
},
|
||||
"latestCommitCard": {
|
||||
"loadingLabel": "A carregar...",
|
||||
"timeagoLabel": "há {time}",
|
||||
"patcherLabel": "Patcher: ",
|
||||
"managerLabel": "Manager: ",
|
||||
"updateButton": "Atualizar Manager"
|
||||
},
|
||||
"patcherView": {
|
||||
"widgetTitle": "Patcher",
|
||||
"patchButton": "Modificar",
|
||||
"patchDialogText": "Selecionou uma modificação de recurso e uma instalação dividida de APK foi detetada, logo podem ocorrer erros de modificação.\nTem a certeza que deseja prosseguir?"
|
||||
},
|
||||
"appSelectorCard": {
|
||||
"widgetTitle": "Selecione uma aplicação",
|
||||
"widgetTitleSelected": "Aplicação selecionada",
|
||||
"widgetSubtitle": "Nenhuma aplicação selecionada",
|
||||
"noAppsLabel": "Não foram encontradas aplicações",
|
||||
"currentVersion": "Atual",
|
||||
"recommendedVersion": "Recomendada",
|
||||
"anyVersion": "qualquer"
|
||||
},
|
||||
"patchSelectorCard": {
|
||||
"widgetTitle": "Selecionar modificações",
|
||||
"widgetTitleSelected": "Modificações selecionadas",
|
||||
"widgetSubtitle": "Selecione uma aplicação primeiro",
|
||||
"widgetEmptySubtitle": "Nenhuma modificação selecionada"
|
||||
},
|
||||
"socialMediaCard": {
|
||||
"widgetTitle": "Redes sociais",
|
||||
"widgetSubtitle": "Estamos online!"
|
||||
},
|
||||
"appSelectorView": {
|
||||
"viewTitle": "Selecione uma aplicação",
|
||||
"searchBarHint": "Procurar aplicações",
|
||||
"storageButton": "Armazenamento",
|
||||
"errorMessage": "Não é possível usar a aplicação selecionada"
|
||||
},
|
||||
"patchesSelectorView": {
|
||||
"viewTitle": "Selecionar modificações",
|
||||
"searchBarHint": "Procurar modificações",
|
||||
"doneButton": "Concluído",
|
||||
"recommended": "Recomendadas",
|
||||
"all": "Todas",
|
||||
"none": "Nenhuma",
|
||||
"loadPatchesSelection": "Carregar seleção de modificações",
|
||||
"noSavedPatches": "Nenhuma correção salva para o aplicativo selecionado.\nPressione \"Concluir\" para salvar a seleção atual.",
|
||||
"noPatchesFound": "Nenhuma modificação encontrada para a aplicação selecionada",
|
||||
"selectAllPatchesWarningContent": "Está prestes a selecionar todos os patches que incluem correções não recomendadas e podem causar comportamentos indesejados."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Selecionar esta modificação pode resultar em erros.\n\nVersão da aplicação: {packageVersion}\nVersões suportadas:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Modificar não é suportado para esta versão da aplicação. Ativa a opção experimental nas definições para proceder."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Instalador",
|
||||
"installButton": "Instalar",
|
||||
"installRootButton": "Instalar como Root",
|
||||
"openButton": "Abrir",
|
||||
"shareButton": "Partilhar ficheiro",
|
||||
"notificationTitle": "O ReVanced Manager está a fazer as modificações",
|
||||
"notificationText": "Toca para voltar ao instalador",
|
||||
"shareApkMenuOption": "Partilhar APK",
|
||||
"exportApkMenuOption": "Exportar APK",
|
||||
"shareLogMenuOption": "Partilhar registo",
|
||||
"installErrorDialogTitle": "Erro",
|
||||
"installErrorDialogText1": "A instalação com root não é possível com a seleção de modificações atuais.\nVolta a fazer a modificação da tua aplicação ou escolhe uma instalação sem root.",
|
||||
"installErrorDialogText2": "A instalação sem root não é possível com a seleção de modificações atuais.\nVolta a fazer a modificação da tua aplicação ou escolhe uma instalação com root se o teu dispositivo estiver rooted.",
|
||||
"installErrorDialogText3": "Não é possível instalar com root, pois o APK original foi selecionado do armazenamento.\nSelecione uma aplicação instalada ou escolha uma instalação sem root.",
|
||||
"noExit": "O instalador ainda está em execução, não é possível sair..."
|
||||
},
|
||||
"settingsView": {
|
||||
"widgetTitle": "Definições",
|
||||
"appearanceSectionTitle": "Aparência",
|
||||
"teamSectionTitle": "Equipa",
|
||||
"infoSectionTitle": "Informação",
|
||||
"advancedSectionTitle": "Opções avançadas",
|
||||
"logsSectionTitle": "Registos",
|
||||
"darkThemeLabel": "Modo escuro",
|
||||
"darkThemeHint": "Bem-vindo ao lado negro",
|
||||
"dynamicThemeLabel": "Material You",
|
||||
"dynamicThemeHint": "Aproveite uma experiência mais próxima do tema do seu dispositivo",
|
||||
"languageLabel": "Idioma",
|
||||
"englishOption": "Inglês",
|
||||
"sourcesLabel": "Fontes",
|
||||
"sourcesLabelHint": "Configure as suas fontes personalizadas",
|
||||
"hostRepositoryLabel": "API do Repositório",
|
||||
"orgPatchesLabel": "Organização de Modificações",
|
||||
"sourcesPatchesLabel": "Fonte das Modificações",
|
||||
"orgIntegrationsLabel": "Organização de Integrações",
|
||||
"sourcesIntegrationsLabel": "Fonte das Integrações",
|
||||
"sourcesResetDialogTitle": "Repor",
|
||||
"sourcesResetDialogText": "Tem certeza que deseja redefinir as fontes personalizadas para os seus valores padrão?",
|
||||
"apiURLResetDialogText": "Tem certeza de que deseja repor o URL da API para o seu valor padrão?",
|
||||
"contributorsLabel": "Contribuidores",
|
||||
"contributorsHint": "Uma lista de contribuidores do ReVanced",
|
||||
"logsLabel": "Registos",
|
||||
"logsHint": "Compartilhar registos do Manager",
|
||||
"apiURLLabel": "URL da API",
|
||||
"apiURLHint": "Configura a tua URL de API personalizada",
|
||||
"selectApiURL": "URL da API",
|
||||
"experimentalUniversalPatchesLabel": "Suporte experimental para modificações universais",
|
||||
"experimentalUniversalPatchesHint": "Mostrar todas as aplicações a usar com as modificações universais, a lista de carregamento de apps pode ser mais lenta",
|
||||
"experimentalPatchesLabel": "Suporte para modificações experimentais",
|
||||
"experimentalPatchesHint": "Ativa o uso de modificações não suportadas em qualquer versão das aplicações",
|
||||
"enabledExperimentalPatches": "Suporte para modificações experimentais ativo",
|
||||
"exportSectionTitle": "Importar e exportar",
|
||||
"aboutLabel": "Sobre",
|
||||
"snackbarMessage": "Copiado para a área de transferência",
|
||||
"sentryLabel": "Registo do coletor de erros",
|
||||
"sentryHint": "Enviar registos anónimos para nos ajudar a melhorar o ReVanced Manager",
|
||||
"restartAppForChanges": "Reinicia a aplicação para aplicar as alterações",
|
||||
"deleteKeystoreLabel": "Excluir keystore",
|
||||
"deleteKeystoreHint": "Excluir a keystore usada para assinar a aplicação",
|
||||
"deletedKeystore": "Keystore excluída",
|
||||
"deleteTempDirLabel": "Apagar arquivos temporários",
|
||||
"deleteTempDirHint": "Apagar arquivos temporários não utilizados",
|
||||
"deletedTempDir": "Arquivos temporários apagados",
|
||||
"exportPatchesLabel": "Exportar seleção de modificações",
|
||||
"exportPatchesHint": "Exporta a seleção de modificações para um arquivo JSON",
|
||||
"exportedPatches": "Seleção de modificações exportada",
|
||||
"noExportFileFound": "Nenhuma seleção de modificações para exportar",
|
||||
"importPatchesLabel": "Importar seleção de modificações",
|
||||
"importPatchesHint": "Importa a seleção de modificações de um arquivo JSON",
|
||||
"importedPatches": "Seleção de modificações importada",
|
||||
"resetStoredPatchesLabel": "Redefinir modificações",
|
||||
"resetStoredPatchesHint": "Redefinir a seleção de modificações armazenada",
|
||||
"resetStoredPatches": "A seleção de modificações foi redefinida",
|
||||
"jsonSelectorErrorMessage": "Não é possível usar o arquivo JSON selecionado",
|
||||
"deleteLogsLabel": "Eliminar registos",
|
||||
"deleteLogsHint": "Exclui registos do manager coletados",
|
||||
"deletedLogs": "Registos excluídos"
|
||||
},
|
||||
"appInfoView": {
|
||||
"widgetTitle": "Informações da aplicação",
|
||||
"openButton": "Abrir",
|
||||
"uninstallButton": "Desinstalar",
|
||||
"patchButton": "Modificar",
|
||||
"unpatchButton": "Desalterar",
|
||||
"unpatchDialogText": "Tens a certeza que queres desalterar esta app?",
|
||||
"rootDialogTitle": "Erro",
|
||||
"rootDialogText": "A aplicação foi instalada com permissões de superutilizador, mas atualmente o ReVanced Manager não tem permissões.\nPor favor, conceda permissões de superutilizador primeiro.",
|
||||
"packageNameLabel": "Nome do pacote",
|
||||
"originalPackageNameLabel": "Nome original do pacote",
|
||||
"installTypeLabel": "Tipo de instalação",
|
||||
"rootTypeLabel": "Root",
|
||||
"nonRootTypeLabel": "Sem root",
|
||||
"patchedDateLabel": "Data da Modificação",
|
||||
"patchedDateHint": "{date} às {time}",
|
||||
"appliedPatchesLabel": "Modificações aplicadas",
|
||||
"appliedPatchesHint": "{quantity} modificação/ões aplicada/s",
|
||||
"updateNotImplemented": "Este recurso ainda não foi implementado"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "Contribuidores",
|
||||
"patcherContributors": "Contribuidores do Modificador",
|
||||
"patchesContributors": "Contribuidores das Modificações",
|
||||
"integrationsContributors": "Contribuidores das Integrações",
|
||||
"cliContributors": "Contribuidores do CLI",
|
||||
"managerContributors": "Contribuidores do Manager"
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Anulează",
|
||||
"updateButton": "Actualizare",
|
||||
"enabledLabel": "Activat",
|
||||
"disabledLabel": "Dezactivat",
|
||||
"yesButton": "Da",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Nu sunt instalate aplicații patch-uite",
|
||||
"installed": "Instalat",
|
||||
"updateDialogTitle": "Manager Actualizări",
|
||||
"updateDialogText": "Sunteți sigur că doriți să descărcați și să actualizați ReVanced Manager?",
|
||||
"updateChangelogTitle": "Istoric schimbări",
|
||||
"notificationTitle": "Actualizare descărcată",
|
||||
"notificationText": "Apăsați pentru a instala actualizarea",
|
||||
"downloadingMessage": "Se descarcă actualizarea...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "Toate",
|
||||
"none": "Niciunul",
|
||||
"loadPatchesSelection": "Încărcați selecția de patch-uri",
|
||||
"noSavedPatches": "Nu există patch-uri salvate pentru aplicația selectată\nApăsați Terminat pentru a salva selecția curentă",
|
||||
"noSavedPatches": "Nu există patch-uri salvate pentru aplicația selectată.\nApăsați Terminat pentru a salva selecția curentă.",
|
||||
"noPatchesFound": "Nu s-au găsit patch-uri pentru aplicația selectată",
|
||||
"selectAllPatchesWarningContent": "Urmează să selectați toate patch-urile, acestea incluzând patch-uri nerecomandate care pot cauza funcționarea necorespunzătoare a aplicației."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Selectarea acestui patch poate rezulta în erori de patch-uire.\n\nVersiunea aplicației: {packageVersion}\nVersiuni compatibile:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Patch-ul nu este compatibil cu versiunea curentă a aplicației.\nActivați comutatorul experimental din setări pentru a continua."
|
||||
"unsupportedPatchVersion": "Patch-ul nu este compatibil cu această versiune a aplicației. Activați comutatorul experimental din setări pentru a continua."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Instalator",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Engleză",
|
||||
"sourcesLabel": "Surse",
|
||||
"sourcesLabelHint": "Configurați-vă sursele personalizate",
|
||||
"hostRepositoryLabel": "Repository API",
|
||||
"orgPatchesLabel": "Organizarea patch-urilor",
|
||||
"sourcesPatchesLabel": "Sursă patch-uri",
|
||||
"orgIntegrationsLabel": "Organizare integrări",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Configurați-vă API URL-ul personalizat",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Suport pentru patch-uri experimentale universale",
|
||||
"experimentalUniversalPatchesHint": "Afișează toate aplicațiile de utilizat cu patch-uri universale, lista de aplicații poate fi mai lentă",
|
||||
"experimentalPatchesLabel": "Suport pentru patch-uri experimentale",
|
||||
"experimentalPatchesHint": "Activați utilizarea de patch-uri nesuportate în orice versiune a aplicației",
|
||||
"enabledExperimentalPatches": "Suport pentru patch-uri experimentale",
|
||||
@ -148,7 +152,7 @@
|
||||
"deleteKeystoreHint": "Ștergeți keystore-ul utilizat pentru a însemna aplicația",
|
||||
"deletedKeystore": "Keystore șters",
|
||||
"deleteTempDirLabel": "Ștergeți fișierele temporare",
|
||||
"deleteTempDirHint": "Ștergeți fișierele temporare neutilizate",
|
||||
"deleteTempDirHint": "Șterge fișierele temporare neutilizate",
|
||||
"deletedTempDir": "Fișierele temporare au fost șterse",
|
||||
"exportPatchesLabel": "Exportă selecția patch-urilor",
|
||||
"exportPatchesHint": "Exportă selecția patch-urilor într-un fișier JSON",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "ОК",
|
||||
"cancelButton": "Отмена",
|
||||
"updateButton": "Обновить",
|
||||
"enabledLabel": "Включено",
|
||||
"disabledLabel": "Выключено",
|
||||
"yesButton": "Да",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Не установлено пропатченных приложений",
|
||||
"installed": "Установлено",
|
||||
"updateDialogTitle": "Обновить Manager",
|
||||
"updateDialogText": "Вы уверены, что хотите загрузить и обновить ReVanced Manager?",
|
||||
"updateChangelogTitle": "Список изменений",
|
||||
"notificationTitle": "Обновление загружено",
|
||||
"notificationText": "Нажмите, чтобы установить обновление",
|
||||
"downloadingMessage": "Скачивается обновление...",
|
||||
@ -77,17 +78,17 @@
|
||||
"viewTitle": "Выберите патчи",
|
||||
"searchBarHint": "Искать патчи",
|
||||
"doneButton": "Готово",
|
||||
"recommended": "Рекомендуемые",
|
||||
"recommended": "Рекомендуется",
|
||||
"all": "Все",
|
||||
"none": "Никакие",
|
||||
"loadPatchesSelection": "Загрузить выбор патчей",
|
||||
"noSavedPatches": "Нет сохранённых патчей для выбранного приложения\nНажмите «Готово» для сохранения текущего выбора",
|
||||
"noSavedPatches": "Нет сохранённых патчей для выбранного приложения.\nНажмите «Готово» для сохранения текущего выбора.",
|
||||
"noPatchesFound": "Для выбранного приложения не найдены патчи",
|
||||
"selectAllPatchesWarningContent": "Вы собираетесь выбрать все патчи, в том числе и нерекомендуемые патчи, которые могут вызвать нежелательное поведение."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Выбор этого патча может привести к ошибкам во время патча.\n\nВерсия приложения: {packageVersion}\nПоддерживаемые версии:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Патч не поддерживается этой версией приложения. Для продолжения включите опцию экспериментальной поддержки в настройках."
|
||||
"unsupportedPatchVersion": "Патч не поддерживается для этой версии приложения. Чтобы продолжить, включите опцию экспериментальной поддержки в настройках."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Установщик",
|
||||
@ -101,7 +102,7 @@
|
||||
"exportApkMenuOption": "Экспорт APK",
|
||||
"shareLogMenuOption": "Отправить лог",
|
||||
"installErrorDialogTitle": "Ошибка",
|
||||
"installErrorDialogText1": "Root-установка невозможна с выбранными патчами.\nПерепатчите приложение, или выберите non-root установку.",
|
||||
"installErrorDialogText1": "Root-установка невозможна с выбранными патчами.\nПерепатчите приложение или выберите non-root установку.",
|
||||
"installErrorDialogText2": "Non-root установка невозможна с выбранными патчами.\nПерепатчите приложение, или выберите root-установку, если на вашем устройстве есть root.",
|
||||
"installErrorDialogText3": "Root-установка невозможна, так как оригинальный APK был выбран из хранилища.\nВыберите установленное приложение, или выберите non-root установку.",
|
||||
"noExit": "Установщик все еще запущен, выход невозможен..."
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Английский",
|
||||
"sourcesLabel": "Источники",
|
||||
"sourcesLabelHint": "Настройте свои источники",
|
||||
"hostRepositoryLabel": "API репозитория",
|
||||
"orgPatchesLabel": "Организация патчей",
|
||||
"sourcesPatchesLabel": "Репозиторий патчей",
|
||||
"orgIntegrationsLabel": "Организация интеграций",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API-ссылка",
|
||||
"apiURLHint": "Настройте свою API-ссылку",
|
||||
"selectApiURL": "API-ссылка",
|
||||
"experimentalUniversalPatchesLabel": "Экспериментальная поддержка универсальных патчей",
|
||||
"experimentalUniversalPatchesHint": "Отображать все приложения, поддерживающие универсальные патчи; загрузка списка приложений может быть медленнее",
|
||||
"experimentalPatchesLabel": "Экспериментальная поддержка патчей",
|
||||
"experimentalPatchesHint": "Разрешить использование несовместимых патчей в любой версии приложения",
|
||||
"enabledExperimentalPatches": "Экспериментальная поддержка патчей включена",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "OK",
|
||||
"cancelButton": "Zrušiť",
|
||||
"updateButton": "Aktualizovať",
|
||||
"enabledLabel": "Zapnuté",
|
||||
"disabledLabel": "Vypnuté",
|
||||
"yesButton": "Áno",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Žiadne zaplátané aplikácie nie sú nainštalované",
|
||||
"installed": "Nainštalované",
|
||||
"updateDialogTitle": "Aktualizovať Manažéra",
|
||||
"updateDialogText": "Ste si istý, že chcete stiahnuť a aktualizovať ReVanced manažér?",
|
||||
"updateChangelogTitle": "Zoznam zmien",
|
||||
"notificationTitle": "Aktualizácia bola stiahnutá",
|
||||
"notificationText": "Klepnutím nainštalujete aktualizáciu",
|
||||
"downloadingMessage": "Sťahovanie aktualizácie...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "Všetky",
|
||||
"none": "Žiadne",
|
||||
"loadPatchesSelection": "Načítať výber záplat",
|
||||
"noSavedPatches": "Žiadne uložené záplaty pre vybranú aplikáciu\nStlačením tlačidla Hotovo uložíte aktuálny výber",
|
||||
"noSavedPatches": "Žiadne uložené záplaty pre vybranú aplikáciu\nStlačením tlačidla Hotovo uložíte aktuálny výber.",
|
||||
"noPatchesFound": "Neboli nájdené žiadne záplaty pre zvolenú aplikáciu",
|
||||
"selectAllPatchesWarningContent": "Týmto vyberiete všetky záplaty, aj tie ktoré nie sú odporúčané. Toto môže viesť k nežiadúcim účinkom."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Výber tejto záplaty môže spôsobiť chyby.\n\nVerzia aplikácie: {packageVersion}\nPodporované verzie:\n{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Záplata nie je pre túto verziu aplikácie podporovaná. Ak chcete pokračovať, povoľte v nastaveniach prepínač experimental."
|
||||
"unsupportedPatchVersion": "Záplata nie je pre túto verziu aplikácie podporovaná. Ak chcete pokračovať, povoľte v nastaveniach experimentálny prepínač."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Inštalátor",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Anglicky",
|
||||
"sourcesLabel": "Zdroje",
|
||||
"sourcesLabelHint": "Nastavte vlastné zdroje",
|
||||
"hostRepositoryLabel": "API repozitára",
|
||||
"orgPatchesLabel": "Autor záplaty",
|
||||
"sourcesPatchesLabel": "Zdroj záplaty",
|
||||
"orgIntegrationsLabel": "Autor integrácie",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Nastaviť vlastnú API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Experimentálna podpora univerzálnych záplat",
|
||||
"experimentalUniversalPatchesHint": "Zobrazenie všetkých aplikácií na použitie s univerzálnymi záplatami, načítanie zoznamu aplikácií môže byť pomalšie",
|
||||
"experimentalPatchesLabel": "Podpora experimentálnych záplat",
|
||||
"experimentalPatchesHint": "Povoliť používanie nepodporovaných záplat v akejkoľvek verzii aplikácie",
|
||||
"enabledExperimentalPatches": "Podpora experimentálnych záplat povolená",
|
||||
|
200
assets/i18n/sw_KE.json
Normal file
@ -0,0 +1,200 @@
|
||||
{
|
||||
"okButton": "Sawa",
|
||||
"cancelButton": "Ghairi",
|
||||
"updateButton": "Sasisho",
|
||||
"enabledLabel": "Imewashwa",
|
||||
"disabledLabel": "Imezimwa",
|
||||
"yesButton": "Ndiyo",
|
||||
"noButton": "Hapana",
|
||||
"warning": "Onyo",
|
||||
"navigationView": {
|
||||
"dashboardTab": "Dashibodi",
|
||||
"patcherTab": "Kirekebishaji",
|
||||
"settingsTab": "Mpangilio"
|
||||
},
|
||||
"homeView": {
|
||||
"widgetTitle": "Dashibodi",
|
||||
"updatesSubtitle": "Sasisho",
|
||||
"patchedSubtitle": "Programu zilizo na viraka",
|
||||
"updatesAvailable": "Sasisho Zinapatikana",
|
||||
"noUpdates": "Masasisho Hayapatikani",
|
||||
"WIP": "Kazi inaendelea...",
|
||||
"noInstallations": "Hakuna programu zilizo na viraka zilizosakinishwa",
|
||||
"installed": "Imesakinishwa",
|
||||
"updateDialogTitle": "Kidhibiti cha Usasishaji",
|
||||
"updateChangelogTitle": "Changelog",
|
||||
"notificationTitle": "Sasisho limepakuliwa",
|
||||
"notificationText": "Gusa ili kusakinisha sasisho",
|
||||
"downloadingMessage": "Inapakua sasisho...",
|
||||
"installingMessage": "Inasakinisha sasisho...",
|
||||
"errorDownloadMessage": "Imeshindwa kupakua sasisho",
|
||||
"errorInstallMessage": "Imeshindwa kusakinisha sasisho",
|
||||
"noConnection": "Hakuna muunganisho wa mtandao",
|
||||
"updatesDisabled": "Kusasisha programu iliyobanwa kumezimwa kwa sasa. Rekebisha programu tena."
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Rekebisha",
|
||||
"infoButton": "Taarifa",
|
||||
"changelogLabel": "Changelog"
|
||||
},
|
||||
"latestCommitCard": {
|
||||
"loadingLabel": "Inapakia...",
|
||||
"timeagoLabel": "{time} iliyopita",
|
||||
"patcherLabel": "Kirekebishaji: ",
|
||||
"managerLabel": "Meneja: ",
|
||||
"updateButton": "Kidhibiti cha Usasishaji"
|
||||
},
|
||||
"patcherView": {
|
||||
"widgetTitle": "Kirekebishaji",
|
||||
"patchButton": "Rekebisha",
|
||||
"patchDialogText": "Umechagua kiraka cha rasilimali na usakinishaji umegunduliwa ya split APK, kwa hivyo makosa ya kuweka viraka yanaweza kutokea.\nJe, una uhakika unataka kuendelea?"
|
||||
},
|
||||
"appSelectorCard": {
|
||||
"widgetTitle": "Chagua programu",
|
||||
"widgetTitleSelected": "Programu imechaguliwa",
|
||||
"widgetSubtitle": "Hakuna programu iliyochaguliwa",
|
||||
"noAppsLabel": "Hakuna programu zilizopatikana",
|
||||
"currentVersion": "Sasa",
|
||||
"recommendedVersion": "Imependekezwa",
|
||||
"anyVersion": "yoyote"
|
||||
},
|
||||
"patchSelectorCard": {
|
||||
"widgetTitle": "Chagua viraka",
|
||||
"widgetTitleSelected": "Viraka vilivyochaguliwa",
|
||||
"widgetSubtitle": "Chagua programu kwanza",
|
||||
"widgetEmptySubtitle": "Hakuna viraka vilivyochaguliwa"
|
||||
},
|
||||
"socialMediaCard": {
|
||||
"widgetTitle": "Mtandao wa kijamii",
|
||||
"widgetSubtitle": "Tuko mtandaoni!"
|
||||
},
|
||||
"appSelectorView": {
|
||||
"viewTitle": "Chagua programu",
|
||||
"searchBarHint": "Tafuta programu",
|
||||
"storageButton": "Hifadhi",
|
||||
"errorMessage": "Haiwezi kutumia programu iliyochaguliwa"
|
||||
},
|
||||
"patchesSelectorView": {
|
||||
"viewTitle": "Chagua viraka",
|
||||
"searchBarHint": "Tafuta viraka",
|
||||
"doneButton": "Imekamilika",
|
||||
"recommended": "Imependekezwa",
|
||||
"all": "Yote",
|
||||
"none": "Hakuna",
|
||||
"loadPatchesSelection": "Ingiza Uteuzi wa viraka",
|
||||
"noSavedPatches": "Hakuna viraka vilivyohifadhiwa kwa programu iliyochaguliwa.\nBonyeza Nimemaliza ili kuhifadhi chaguo la sasa.",
|
||||
"noPatchesFound": "Hakuna viraka vilivyopatikana kwa programu iliyochaguliwa",
|
||||
"selectAllPatchesWarningContent": "Unakaribia kuchagua viraka vyote, vinavyojumuisha viraka visivyopendekezwa na vinaweza kusababisha tabia isiyotakikana."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "Toleo la programu: {packageVersion}\nMatoleo yanayotumika:{supportedVersions}",
|
||||
"unsupportedPatchVersion": "Kiraka hakitumiki kwa toleo hili la programu. Washa ugeuzaji wa majaribio katika mipangilio ili kuendelea."
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "Kisakinishi",
|
||||
"installButton": "Sakinisha",
|
||||
"installRootButton": "Sakinisha kama mtumiaji mkuu",
|
||||
"openButton": "Fungua",
|
||||
"shareButton": "Gawana faili",
|
||||
"notificationTitle": "Revanced Manager inatia viraka",
|
||||
"notificationText": "Gusa ili urudi kwa kisakinishi",
|
||||
"shareApkMenuOption": "Gawa APK",
|
||||
"exportApkMenuOption": "Hamisha APK",
|
||||
"shareLogMenuOption": "Gawa jarida",
|
||||
"installErrorDialogTitle": "Hitilafu",
|
||||
"installErrorDialogText1": "Usakinishaji kama mtumiaji mkuu hauwezekani kwa uteuzi wa viraka vya sasa.",
|
||||
"installErrorDialogText2": "Usakinishaji usio wa msimamizi hauwezekani kwa uteuzi wa sasa wa viraka.",
|
||||
"installErrorDialogText3": "Usakinishaji kama mtumiaji mkuu hauwezekani kwansababu APK ule wa asili ilichaguliwa kutoka kwa hifadhi.\nChagua programu iliyosakinishwa au chagua usakinishaji usio wa msimamizi.",
|
||||
"noExit": "Kisakinishi bado kinafanya kazi, hakiwezi kutoka..."
|
||||
},
|
||||
"settingsView": {
|
||||
"widgetTitle": "Mipangilio",
|
||||
"appearanceSectionTitle": "Mwonekano",
|
||||
"teamSectionTitle": "Timu",
|
||||
"infoSectionTitle": "Taarifa",
|
||||
"advancedSectionTitle": "Ziada",
|
||||
"logsSectionTitle": "Kumbukumbu",
|
||||
"darkThemeLabel": "Hali ya giza",
|
||||
"darkThemeHint": "Karibu katika upande wa giza",
|
||||
"dynamicThemeLabel": "Material You",
|
||||
"dynamicThemeHint": "Furahia matumizi karibu na kifaa chako",
|
||||
"languageLabel": "Lugha",
|
||||
"englishOption": "Kiingereza",
|
||||
"sourcesLabel": "Vyanzo",
|
||||
"sourcesLabelHint": "Sanidi vyanzo vyako maalum",
|
||||
"hostRepositoryLabel": "HIfadhi ya API",
|
||||
"orgPatchesLabel": "Shirika la viraka",
|
||||
"sourcesPatchesLabel": "Chanzo cha viraka",
|
||||
"orgIntegrationsLabel": "Shirika la ujumuishaji",
|
||||
"sourcesIntegrationsLabel": "Chanzo cha ujumuishaji",
|
||||
"sourcesResetDialogTitle": "Anzisha",
|
||||
"sourcesResetDialogText": "Je, una uhakika unataka kuweka upya vyanzo maalum kwa thamani zao chaguomsingi?",
|
||||
"apiURLResetDialogText": "Je, una uhakika unataka kuweka upya URL ya API hadi thamani yake chaguomsingi?",
|
||||
"contributorsLabel": "Wachangiaji",
|
||||
"contributorsHint": "Orodha ya wachangiaji wa Revanced",
|
||||
"logsLabel": "Kumbukumbu",
|
||||
"logsHint": "Gawa kumbukumbu za meneja",
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Sanidi URL yako ya API maalum",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Usaidizi wa viraka vya majaribio kwa wote",
|
||||
"experimentalUniversalPatchesHint": "Onyesha programu zote za kutumia na viraka vya ulimwengu wote, orodha ya upakiaji inaweza kuwa polepole",
|
||||
"experimentalPatchesLabel": "Usaidizi wa viraka vya majaribio",
|
||||
"experimentalPatchesHint": "Washa matumizi ya viraka visivyotumika katika toleo lolote la programu",
|
||||
"enabledExperimentalPatches": "Usaidizi wa viraka vya majaribio umewashwa",
|
||||
"exportSectionTitle": "Kuagiza na kuuza nje",
|
||||
"aboutLabel": "Kuhusu",
|
||||
"snackbarMessage": "Imenakiliwa kwenye ubao wa kunakili",
|
||||
"sentryLabel": "Sentry logging",
|
||||
"sentryHint": "Tuma kumbukumbu bila majina ili utusaidie kuboresha Revanced Manager",
|
||||
"restartAppForChanges": "Anzisha tena programu ili kutekeleza mabadiliko",
|
||||
"deleteKeystoreLabel": "Futa Duka la Ufunguo",
|
||||
"deleteKeystoreHint": "Futa Duka la Ufunguo iliyotumika kusaini programu",
|
||||
"deletedKeystore": "Duka la ufunguo imefutwa",
|
||||
"deleteTempDirLabel": "Futa faili za muda",
|
||||
"deleteTempDirHint": "Futa faili za muda ambazo hazijatumiwa",
|
||||
"deletedTempDir": "Faili za muda zimefutwa",
|
||||
"exportPatchesLabel": "Hamisha uteuzi wa viraka",
|
||||
"exportPatchesHint": "Hamisha uteuzi wa viraka kwenye faili ya JSON",
|
||||
"exportedPatches": "Uteuzi wa viraka umehamishwa",
|
||||
"noExportFileFound": "Hakuna viraka vilivyochaguliwa kuhamishwa",
|
||||
"importPatchesLabel": "Ingiza uteuzi wa viraka",
|
||||
"importPatchesHint": "Ingiza uteuzi wa viraka kutoka kwa faili ya JSON",
|
||||
"importedPatches": "Uteuzi wa viraka umeingizwa",
|
||||
"resetStoredPatchesLabel": "Weka upya viraka",
|
||||
"resetStoredPatchesHint": "Weka upya uteuzi wa viraka vilivyohifadhiwa",
|
||||
"resetStoredPatches": "Uteuzi wa viraka umewekwa upya",
|
||||
"jsonSelectorErrorMessage": "Imeshindwa kutumia faili ya JSON iliyochaguliwa",
|
||||
"deleteLogsLabel": "Futa kumbukumbu",
|
||||
"deleteLogsHint": "Futa kumbukumbu za wasimamizi zilizokusanywa",
|
||||
"deletedLogs": "Kumbukumbu zimefutwa"
|
||||
},
|
||||
"appInfoView": {
|
||||
"widgetTitle": "Maelezo ya programu",
|
||||
"openButton": "Fungua",
|
||||
"uninstallButton": "Sanidua",
|
||||
"patchButton": "Rekebisha",
|
||||
"unpatchButton": "Ondoa kibandiko",
|
||||
"unpatchDialogText": "Je, una uhakika unataka kubandua programu hii?",
|
||||
"rootDialogTitle": "Hitilafu",
|
||||
"rootDialogText": "Programu ilisakinishwa kwa ruhusa za mtumiaji mkuu, lakini kwa sasa ReVanced Manager haina ruhusa..\nTafadhali toa ruhusa za mtumiaji mkuu kwanza.",
|
||||
"packageNameLabel": "Jina la kifurushi",
|
||||
"originalPackageNameLabel": "Jina halisi la kifurushi",
|
||||
"installTypeLabel": "Aina ya usakinishaji",
|
||||
"rootTypeLabel": "Root",
|
||||
"nonRootTypeLabel": "Sio mtumiaji mkuu",
|
||||
"patchedDateLabel": "Tarehe iliyowekwa viraka",
|
||||
"patchedDateHint": "{date} katika {time}",
|
||||
"appliedPatchesLabel": "Viraka vilivyotumika",
|
||||
"appliedPatchesHint": "{quantity} viraka vilivyotumika",
|
||||
"updateNotImplemented": "Kipengele hiki bado hakijatekelezwa"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "Wachangiaji",
|
||||
"patcherContributors": "Wachangiaji wa Kirekebishaji",
|
||||
"patchesContributors": "Wachangiaji wa Viraka",
|
||||
"integrationsContributors": "Wachangiaji wa ujumuishaji",
|
||||
"cliContributors": "Wachangiaji wa CLI",
|
||||
"managerContributors": "Wachangiaji wa meneja"
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "Tamam",
|
||||
"cancelButton": "İptal",
|
||||
"updateButton": "Güncelleme",
|
||||
"enabledLabel": "Etkinleştirildi",
|
||||
"disabledLabel": "Devre dışı",
|
||||
"yesButton": "Evet",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Yamalanmış uygulama bulunmamaktadır",
|
||||
"installed": "Yüklendi",
|
||||
"updateDialogTitle": "Manager'ı güncelle",
|
||||
"updateDialogText": "ReVanced Manager'ın güncellemesini indirmek ve yüklemek istediğinize emin misiniz?",
|
||||
"updateChangelogTitle": "Değişiklik Notları",
|
||||
"notificationTitle": "Güncelleme indirildi",
|
||||
"notificationText": "Güncellemeyi yüklemek için dokunun",
|
||||
"downloadingMessage": "Güncelleme indiriliyor...",
|
||||
@ -40,7 +41,7 @@
|
||||
"loadingLabel": "Yükleniyor...",
|
||||
"timeagoLabel": "{time} önce",
|
||||
"patcherLabel": "Yamalayıcı: ",
|
||||
"managerLabel": "Manager: ",
|
||||
"managerLabel": "Yönetici: ",
|
||||
"updateButton": "Manager'ı güncelle"
|
||||
},
|
||||
"patcherView": {
|
||||
@ -81,7 +82,7 @@
|
||||
"all": "Hepsi",
|
||||
"none": "Hiçbiri",
|
||||
"loadPatchesSelection": "Yama seçimlerini yükle",
|
||||
"noSavedPatches": "Seçilen uygulama için kaydedilmiş yama yok\nMevcut seçimi kaydetmek için Tamam'a tıklayın",
|
||||
"noSavedPatches": "Seçilen uygulama için kaydedilmiş yama yok.\nMevcut seçimi kaydetmek için Tamam'a tıklayın.",
|
||||
"noPatchesFound": "Seçili uygulama için yama bulunamadı",
|
||||
"selectAllPatchesWarningContent": "Tavsiye edilmeyenler de dahil bütün yamaları seçmek üzeresiniz, bu istenmeyen davranışlara sebep olabilir."
|
||||
},
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "İngilizce",
|
||||
"sourcesLabel": "Kaynaklar",
|
||||
"sourcesLabelHint": "Özel kaynaklarınızı yapılandırın",
|
||||
"hostRepositoryLabel": "Depo API",
|
||||
"orgPatchesLabel": "Yama organizasyonu",
|
||||
"sourcesPatchesLabel": "Yama kaynağı",
|
||||
"orgIntegrationsLabel": "Entegrasyon organizasyonu",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "Özel API URL'inizi yapılandırın",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "Deneysel evrensel yama desteği",
|
||||
"experimentalUniversalPatchesHint": "Evrensel yamalarla kullanılacak tüm uygulamaları görüntüleyin, uygulamaların yüklenmesi daha yavaş olabilir",
|
||||
"experimentalPatchesLabel": "Deneysel yama desteği",
|
||||
"experimentalPatchesHint": "Herhangi bir uygulama sürümünde desteklenmeyen yamaları kullanmayı etkinleştir",
|
||||
"enabledExperimentalPatches": "Deneysel yama desteği etkin",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "Đồng ý",
|
||||
"cancelButton": "Hủy",
|
||||
"updateButton": "Cập nhật",
|
||||
"enabledLabel": "Đã bật",
|
||||
"disabledLabel": "Đã tắt",
|
||||
"yesButton": "Có",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "Không có ứng dụng đã vá nào được cài đặt",
|
||||
"installed": "Đã cài đặt",
|
||||
"updateDialogTitle": "Update Manager",
|
||||
"updateDialogText": "Bạn có muốn tải về và cập nhật ReVanced Manager?",
|
||||
"updateChangelogTitle": "Nhật ký thay đổi",
|
||||
"notificationTitle": "Cập nhật đã được tải xong",
|
||||
"notificationText": "Nhấn để cài đặt bản cập nhật",
|
||||
"downloadingMessage": "Đang tải xuống bản cập nhật...",
|
||||
@ -81,7 +82,7 @@
|
||||
"all": "Tất cả",
|
||||
"none": "Không có",
|
||||
"loadPatchesSelection": "Nạp các bản vá được chọn",
|
||||
"noSavedPatches": "Không có bản vá cho ứng dụng được chọn\nNhấn Hoàn tất để lưu lựa chọn hiện tại",
|
||||
"noSavedPatches": "Không có bản vá cho ứng dụng được chọn\nNhấn Hoàn tất để lưu lựa chọn hiện tại.",
|
||||
"noPatchesFound": "Không tìm thấy bản vá cho ứng dụng đã chọn",
|
||||
"selectAllPatchesWarningContent": "Bạn sắp lựa chọn tất cả bản vá, bao gồm những bản vá không được khuyến nghị và có thể gây ra những hành vi không mong muốn."
|
||||
},
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "Tiếng Anh",
|
||||
"sourcesLabel": "Nguồn",
|
||||
"sourcesLabelHint": "Cấu hình nguồn tùy chỉnh của bạn",
|
||||
"hostRepositoryLabel": "API Kho lưu trữ",
|
||||
"orgPatchesLabel": "Tác giả bản vá",
|
||||
"sourcesPatchesLabel": "Nguồn bản vá",
|
||||
"orgIntegrationsLabel": "Tác giá bản tích hợp",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "Địa chỉ URL của API",
|
||||
"apiURLHint": "Cấu hình API URL tùy chỉnh của bạn",
|
||||
"selectApiURL": "Địa chỉ URL của API",
|
||||
"experimentalUniversalPatchesLabel": "Hỗ trợ các bản vá thử nghiệm phổ biến",
|
||||
"experimentalUniversalPatchesHint": "Hiển thị tất cả các ứng dụng dùng với các bản vá phổ biến, việc tải các danh sách ứng dụng có thể chậm hơn",
|
||||
"experimentalPatchesLabel": "Hỗ trợ các bản vá thử nghiệm",
|
||||
"experimentalPatchesHint": "Bật sử dụng các bản vá không được hỗ trợ trong bất kỳ phiên bản nào của ứng dụng",
|
||||
"enabledExperimentalPatches": "Đã bật hỗ trợ các bản vá thử nghiệm",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"okButton": "確定",
|
||||
"cancelButton": "取消",
|
||||
"updateButton": "更新",
|
||||
"enabledLabel": "已啟用",
|
||||
"disabledLabel": "已停用",
|
||||
"yesButton": "是",
|
||||
@ -21,7 +22,7 @@
|
||||
"noInstallations": "沒有安裝已修補的應用程式",
|
||||
"installed": "已安裝",
|
||||
"updateDialogTitle": "更新 Manager",
|
||||
"updateDialogText": "確定要下載並更新 ReVanced Manager 嗎?",
|
||||
"updateChangelogTitle": "更新日誌",
|
||||
"notificationTitle": "已下載更新",
|
||||
"notificationText": "輕觸以安裝更新",
|
||||
"downloadingMessage": "正在下載更新...",
|
||||
@ -81,13 +82,13 @@
|
||||
"all": "全部",
|
||||
"none": "無",
|
||||
"loadPatchesSelection": "載入修補檔選取",
|
||||
"noSavedPatches": "選取的應用程式尚無已儲存的修補檔\n按下 [完成] 以儲存目前選取",
|
||||
"noSavedPatches": "選取的應用程式尚無已儲存的修補檔。\n按下 [完成] 以儲存目前選取。",
|
||||
"noPatchesFound": "找不到適合所選應用程式的修補檔",
|
||||
"selectAllPatchesWarningContent": "即將選取所有修補檔——之中包含不推薦的修補檔,且可能造成非預期的結果。"
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedDialogText": "選取此修補檔可能導致修補錯誤。\n應用程式版本: {packageVersion}\n支援的版本: {supportedVersions}",
|
||||
"unsupportedPatchVersion": "不支援此應用程式的版本的修補作業。請在設定中啟用實驗性選項以繼續進行。"
|
||||
"unsupportedPatchVersion": "修補檔不支援這個版本的應用程式。需啟用設定中的實驗性選項才可繼續下一步。"
|
||||
},
|
||||
"installerView": {
|
||||
"widgetTitle": "安裝程式",
|
||||
@ -121,6 +122,7 @@
|
||||
"englishOption": "English",
|
||||
"sourcesLabel": "來源",
|
||||
"sourcesLabelHint": "設定自訂來源",
|
||||
"hostRepositoryLabel": "儲存庫 API",
|
||||
"orgPatchesLabel": "修補檔組織",
|
||||
"sourcesPatchesLabel": "修補檔來源",
|
||||
"orgIntegrationsLabel": "整合組織",
|
||||
@ -135,6 +137,8 @@
|
||||
"apiURLLabel": "API URL",
|
||||
"apiURLHint": "設定自訂 API URL",
|
||||
"selectApiURL": "API URL",
|
||||
"experimentalUniversalPatchesLabel": "支援實驗性通用型修補檔",
|
||||
"experimentalUniversalPatchesHint": "顯示所有可使用通用型修補檔的應用程式,載入應用程式清單的時長可能會變得更慢",
|
||||
"experimentalPatchesLabel": "支援實驗性修補檔",
|
||||
"experimentalPatchesHint": "啟用以在任何應用程式版本中使用不支援的修補檔",
|
||||
"enabledExperimentalPatches": "已啟用支援實驗性修補檔",
|
||||
@ -183,7 +187,7 @@
|
||||
"patchedDateHint": "{date} {time}",
|
||||
"appliedPatchesLabel": "已套用修補檔",
|
||||
"appliedPatchesHint": "已套用 {quantity} 個修補檔",
|
||||
"updateNotImplemented": "這項功能還沒有實現"
|
||||
"updateNotImplemented": "這項功能尚未實作"
|
||||
},
|
||||
"contributorsView": {
|
||||
"widgetTitle": "貢獻者",
|
||||
|
16
docs/0_prerequisites.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 💼 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 application.
|
||||
|
||||
Continue: [⬇️ Installation](1_installation.md)
|
14
docs/1_installation.md
Normal file
@ -0,0 +1,14 @@
|
||||
# ⬇️ Installation
|
||||
|
||||
In order to use ReVanced on your Android device, ReVanced Manager must be installed.
|
||||
|
||||
## 🪜 Installation steps
|
||||
|
||||
- Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest)
|
||||
- Install ReVanced Manager
|
||||
|
||||
## ⏭️ What's next
|
||||
|
||||
The next page will guide you through patching an application.
|
||||
|
||||
Continue: [🪛 Usage](2_usage.md)
|
29
docs/2_1_patching.md
Normal file
@ -0,0 +1,29 @@
|
||||
# 🧩 Patching applications
|
||||
|
||||
The following pages will guide you through using ReVanced Manager to patch applications.
|
||||
|
||||
## 🪜 Steps to patch applications
|
||||
|
||||
1. Navigate to the **Patcher** tab from the bottom navigation bar
|
||||
2. Tap on the **Select an application** card
|
||||
3. Choose an application to patch[^1]
|
||||
> **Note**: The application version suggested by ReVanced Manager is visible in each application's card. This is important in step 5. below, as most patches are compatible with this version.
|
||||
4. Tap on the **Select patches** card and select the patches you want to apply[^2]
|
||||
> **Warning**: If you see a warning icon next to a patch, it means that the patch is not compatible with the application version you selected in step 3. above. In this case, you should either go back and select a different application version or deselect the patch.
|
||||
5. Tap on the **Done** button to confirm your selection
|
||||
6. Tap on the **Patch** button to start patching
|
||||
> **Warning**: The patching process can take anywhere from 2 to 5 minutes depending on your device. Refrain from exiting ReVanced Manager while the process is still running, otherwise the patching time will increase significantly.
|
||||
7. After patching is complete, tap on the **Install** button to install the patched application
|
||||
> **Note**: If you have root access on your device, you may have the option to mount the patched application on top of the original application.[^4]
|
||||
> Optionally, you may tap on the three vertical dots in the top right to export the patched application to storage.
|
||||
|
||||
[^1]: You may be prompted to select an application from storage in case you have no root access on your device. In this case, select the APK file from your device storage.[^3]
|
||||
[^2]: It is suggested to use the default set of patches by tapping on the **Default** chip above the list of patches.
|
||||
[^3]: You can obtain `.apk` files from sites such as [APKMirror](https://www.apkmirror.com/).
|
||||
[^4]: Mounting the patched application on top of the original application will only work if the installed application version matches the version of the application selected in step 3. above.
|
||||
|
||||
## ⏭️ What's next
|
||||
|
||||
The next page will bring you back to the usage page.
|
||||
|
||||
Continue: [🛠️ Usage](2_usage.md)
|
16
docs/2_2_managing.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 🧰 Managing patched applications
|
||||
|
||||
After patching an application, you may want to manage it. This page will guide you through managing patched applications.
|
||||
|
||||
## 🪜 Steps to manage patched applications
|
||||
|
||||
1. Tap on the **Dashboard** tab in the bottom navigation bar
|
||||
2. Select the **Installed** chip
|
||||
3. Tap on the **Info** button for the application 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)
|
14
docs/2_3_updating.md
Normal file
@ -0,0 +1,14 @@
|
||||
# 🔄 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)
|
39
docs/2_4_settings.md
Normal file
@ -0,0 +1,39 @@
|
||||
# ⚙️ 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 application version constraints from ReVanced Patches. This allows you to patch any version of an application, 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 application in particular but rather for all applications but may not work on all applications.
|
||||
|
||||
- ### 🔑 Export, import or delete keystore
|
||||
|
||||
Manage the keystore used to sign patched applications.
|
||||
|
||||
- ### 📄 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)
|
16
docs/2_usage.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 🛠️ Usage
|
||||
|
||||
The following pages will guide you through using ReVanced Manager to patch applications, manage patched applications, and update ReVanced Manager.
|
||||
|
||||
## 📖 Table of contents
|
||||
|
||||
1. [🧩 Patching applications](2_1_patching.md)
|
||||
2. [🧰 Managing patched applications](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)
|
27
docs/3_troubleshooting.md
Normal file
@ -0,0 +1,27 @@
|
||||
# 🛟 Troubleshooting
|
||||
|
||||
In case you encounter any issues while using ReVanced Manager, please refer to this page for possible solutions.
|
||||
|
||||
- ### 💥 App not installed as package conflicts with an existing package
|
||||
|
||||
An existing installation of the application you're trying to patch is conflicting with the patched application. Uninstall the existing application before installing the patched application.
|
||||
|
||||
- ### ❗️Error code `135`, `139` or `1` when patching the application
|
||||
|
||||
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 application.
|
||||
|
||||
- ### 🚫 Non-root install is not possible with the current patches selection
|
||||
|
||||
Select the **Default** chip when choosing patches.
|
||||
|
||||
- ### 🚨 Patched application crashes on launch
|
||||
|
||||
Select the **Default** chip 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)
|
40
docs/4_building.md
Normal file
@ -0,0 +1,40 @@
|
||||
# 🛠️ 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
|
||||
```
|
15
docs/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# 💊 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 applications](2_1_patching.md)
|
||||
2. [🧰 Managing patched applications](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)
|
2
fastlane/Appfile
Normal file
@ -0,0 +1,2 @@
|
||||
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
|
38
fastlane/Fastfile
Normal file
@ -0,0 +1,38 @@
|
||||
# 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
|
48
fastlane/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
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
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1 @@
|
||||
ReVanced Manager is an Android application that uses ReVanced Patcher to add, remove, and modify existing functionalities in Android applications
|
BIN
fastlane/metadata/android/en-US/images/featureGraphic.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg
Normal file
After Width: | Height: | Size: 694 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
Patch your favourite apps, right on your device.
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
@ -0,0 +1 @@
|
||||
ReVanced Manager
|
20
fastlane/report.xml
Normal file
@ -1,4 +1,3 @@
|
||||
import 'package:revanced_manager/services/crowdin_api.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
@ -38,7 +37,6 @@ import 'package:stacked_services/stacked_services.dart';
|
||||
LazySingleton(classType: PatcherAPI),
|
||||
LazySingleton(classType: RevancedAPI),
|
||||
LazySingleton(classType: GithubAPI),
|
||||
LazySingleton(classType: CrowdinAPI),
|
||||
LazySingleton(classType: Toast),
|
||||
],
|
||||
)
|
||||
|
@ -1,18 +1,17 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.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/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_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/views/navigation/navigation_view.dart';
|
||||
import 'package:revanced_manager/utils/environment.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:stacked_themes/stacked_themes.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:timezone/data/latest.dart' as tz;
|
||||
|
||||
late SharedPreferences prefs;
|
||||
@ -21,38 +20,14 @@ Future main() async {
|
||||
await setupLocator();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await locator<ManagerAPI>().initialize();
|
||||
String apiUrl = locator<ManagerAPI>().getApiUrl();
|
||||
final String apiUrl = locator<ManagerAPI>().getApiUrl();
|
||||
await locator<RevancedAPI>().initialize(apiUrl);
|
||||
await locator<CrowdinAPI>().initialize();
|
||||
bool isSentryEnabled = locator<ManagerAPI>().isSentryEnabled();
|
||||
String repoUrl = locator<ManagerAPI>().getRepoUrl();
|
||||
final String repoUrl = locator<ManagerAPI>().getRepoUrl();
|
||||
locator<GithubAPI>().initialize(repoUrl);
|
||||
await locator<PatcherAPI>().initialize();
|
||||
tz.initializeTimeZones();
|
||||
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());
|
||||
}
|
||||
|
||||
@ -65,7 +40,7 @@ class MyApp extends StatelessWidget {
|
||||
// String replaceLocale = rawLocale.replaceAll('_', '-');
|
||||
// List<String> localeList = replaceLocale.split('-');
|
||||
// Locale locale = Locale(localeList[0], localeList[1]);
|
||||
Locale locale = const Locale('en', 'US');
|
||||
const Locale locale = Locale('en', 'US');
|
||||
|
||||
return DynamicThemeBuilder(
|
||||
title: 'ReVanced Manager',
|
||||
@ -79,8 +54,9 @@ class MyApp extends StatelessWidget {
|
||||
useCountryCode: true,
|
||||
),
|
||||
missingTranslationHandler: (key, locale) {
|
||||
print(
|
||||
'--> Missing translation: key: $key, languageCode: ${locale?.languageCode}');
|
||||
log(
|
||||
'--> Missing translation: key: $key, languageCode: ${locale?.languageCode}',
|
||||
);
|
||||
},
|
||||
),
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
|
@ -5,13 +5,6 @@ part 'patch.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Patch {
|
||||
final String name;
|
||||
final String description;
|
||||
final String version;
|
||||
final bool excluded;
|
||||
final List<String> dependencies;
|
||||
final List<Package> compatiblePackages;
|
||||
|
||||
Patch({
|
||||
required this.name,
|
||||
required this.description,
|
||||
@ -22,6 +15,12 @@ class Patch {
|
||||
});
|
||||
|
||||
factory Patch.fromJson(Map<String, dynamic> json) => _$PatchFromJson(json);
|
||||
final String name;
|
||||
final String description;
|
||||
final String version;
|
||||
final bool excluded;
|
||||
final List<String> dependencies;
|
||||
final List<Package> compatiblePackages;
|
||||
|
||||
Map<String, dynamic> toJson() => _$PatchToJson(this);
|
||||
|
||||
@ -37,9 +36,6 @@ class Patch {
|
||||
|
||||
@JsonSerializable()
|
||||
class Package {
|
||||
final String name;
|
||||
final List<String> versions;
|
||||
|
||||
Package({
|
||||
required this.name,
|
||||
required this.versions,
|
||||
@ -47,6 +43,8 @@ class Package {
|
||||
|
||||
factory Package.fromJson(Map<String, dynamic> json) =>
|
||||
_$PackageFromJson(json);
|
||||
final String name;
|
||||
final List<String> versions;
|
||||
|
||||
Map toJson() => _$PackageToJson(this);
|
||||
}
|
||||
|
@ -6,23 +6,6 @@ part 'patched_application.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PatchedApplication {
|
||||
String name;
|
||||
String packageName;
|
||||
String originalPackageName;
|
||||
String version;
|
||||
final String apkFilePath;
|
||||
@JsonKey(
|
||||
fromJson: decodeBase64,
|
||||
toJson: encodeBase64,
|
||||
)
|
||||
Uint8List icon;
|
||||
DateTime patchDate;
|
||||
bool isRooted;
|
||||
bool isFromStorage;
|
||||
bool hasUpdates;
|
||||
List<String> appliedPatches;
|
||||
List<String> changelog;
|
||||
|
||||
PatchedApplication({
|
||||
required this.name,
|
||||
required this.packageName,
|
||||
@ -40,6 +23,22 @@ class PatchedApplication {
|
||||
|
||||
factory PatchedApplication.fromJson(Map<String, dynamic> json) =>
|
||||
_$PatchedApplicationFromJson(json);
|
||||
String name;
|
||||
String packageName;
|
||||
String originalPackageName;
|
||||
String version;
|
||||
final String apkFilePath;
|
||||
@JsonKey(
|
||||
fromJson: decodeBase64,
|
||||
toJson: encodeBase64,
|
||||
)
|
||||
Uint8List icon;
|
||||
DateTime patchDate;
|
||||
bool isRooted;
|
||||
bool isFromStorage;
|
||||
bool hasUpdates;
|
||||
List<String> appliedPatches;
|
||||
List<String> changelog;
|
||||
|
||||
Map<String, dynamic> toJson() => _$PatchedApplicationToJson(this);
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
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 {
|
||||
var 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',
|
||||
),
|
||||
),
|
||||
);
|
||||
List targetLanguages =
|
||||
await response.data['data'][0]['data']['targetLanguages'];
|
||||
|
||||
return targetLanguages;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,24 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
|
||||
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:sentry_dio/sentry_dio.dart';
|
||||
|
||||
@lazySingleton
|
||||
class GithubAPI {
|
||||
late Dio _dio = Dio();
|
||||
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
|
||||
final Options _cacheOptions = buildCacheOptions(
|
||||
const Duration(hours: 6),
|
||||
|
||||
final _cacheOptions = CacheOptions(
|
||||
store: MemCacheStore(),
|
||||
maxStale: const Duration(days: 1),
|
||||
priority: CachePriority.high,
|
||||
);
|
||||
|
||||
final Map<String, String> repoAppPath = {
|
||||
'com.google.android.youtube': 'youtube',
|
||||
'com.google.android.apps.youtube.music': 'music',
|
||||
@ -28,38 +30,44 @@ class GithubAPI {
|
||||
'com.spotify.music': 'spotify',
|
||||
};
|
||||
|
||||
void initialize(String repoUrl) async {
|
||||
Future<void> initialize(String repoUrl) async {
|
||||
try {
|
||||
_dio = Dio(BaseOptions(
|
||||
baseUrl: repoUrl,
|
||||
));
|
||||
|
||||
_dio.interceptors.add(_dioCacheManager.interceptor);
|
||||
_dio.addSentry(
|
||||
captureFailedRequests: true,
|
||||
_dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: repoUrl,
|
||||
),
|
||||
);
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
|
||||
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> clearAllCache() async {
|
||||
try {
|
||||
await _dioCacheManager.clearAll();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
await _cacheOptions.store!.clean();
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getLatestRelease(String repoName) async {
|
||||
Future<Map<String, dynamic>?> getLatestRelease(
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
var response = await _dio.get(
|
||||
final response = await _dio.get(
|
||||
'/repos/$repoName/releases',
|
||||
options: _cacheOptions,
|
||||
);
|
||||
return response.data[0];
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -69,37 +77,41 @@ class GithubAPI {
|
||||
String repoName,
|
||||
DateTime since,
|
||||
) async {
|
||||
String path =
|
||||
final String path =
|
||||
'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}';
|
||||
try {
|
||||
var response = await _dio.get(
|
||||
final response = await _dio.get(
|
||||
'/repos/$repoName/commits',
|
||||
queryParameters: {
|
||||
'path': path,
|
||||
'since': since.toIso8601String(),
|
||||
},
|
||||
options: _cacheOptions,
|
||||
);
|
||||
List<dynamic> commits = response.data;
|
||||
final List<dynamic> commits = response.data;
|
||||
return commits
|
||||
.map(
|
||||
(commit) => (commit['commit']['message']).split('\n')[0] +
|
||||
(commit) => commit['commit']['message'].split('\n')[0] +
|
||||
' - ' +
|
||||
commit['commit']['author']['name'] +
|
||||
'\n' as String,
|
||||
)
|
||||
.toList();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
return List.empty();
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
Future<File?> getLatestReleaseFile(String extension, String repoName) async {
|
||||
Future<File?> getLatestReleaseFile(
|
||||
String extension,
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
Map<String, dynamic>? release = await getLatestRelease(repoName);
|
||||
final Map<String, dynamic>? release = await getLatestRelease(repoName);
|
||||
if (release != null) {
|
||||
Map<String, dynamic>? asset =
|
||||
final Map<String, dynamic>? asset =
|
||||
(release['assets'] as List<dynamic>).firstWhereOrNull(
|
||||
(asset) => (asset['name'] as String).endsWith(extension),
|
||||
);
|
||||
@ -109,9 +121,10 @@ class GithubAPI {
|
||||
);
|
||||
}
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
return null;
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -119,29 +132,34 @@ class GithubAPI {
|
||||
Future<List<Patch>> getPatches(String repoName) async {
|
||||
List<Patch> patches = [];
|
||||
try {
|
||||
File? f = await getLatestReleaseFile('.json', repoName);
|
||||
final File? f = await getLatestReleaseFile('.json', repoName);
|
||||
if (f != null) {
|
||||
List<dynamic> list = jsonDecode(f.readAsStringSync());
|
||||
final List<dynamic> list = jsonDecode(f.readAsStringSync());
|
||||
patches = list.map((patch) => Patch.fromJson(patch)).toList();
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
return List.empty();
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
return patches;
|
||||
}
|
||||
|
||||
Future<String> getLastestReleaseVersion(String repoName) async {
|
||||
try {
|
||||
Map<String, dynamic>? release = await getLatestRelease(repoName);
|
||||
final Map<String, dynamic>? release = await getLatestRelease(repoName);
|
||||
if (release != null) {
|
||||
return release['tag_name'];
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
return '';
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
@ -10,7 +11,7 @@ import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
import 'package:revanced_manager/services/revanced_api.dart';
|
||||
import 'package:revanced_manager/services/root_api.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:revanced_manager/utils/check_for_supported_patch.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@lazySingleton
|
||||
@ -20,8 +21,12 @@ class ManagerAPI {
|
||||
final RootAPI _rootAPI = RootAPI();
|
||||
final String patcherRepo = 'revanced-patcher';
|
||||
final String cliRepo = 'revanced-cli';
|
||||
late String storedPatchesFile = '/selected-patches.json';
|
||||
late SharedPreferences _prefs;
|
||||
bool isRooted = false;
|
||||
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 defaultRepoUrl = 'https://api.github.com';
|
||||
String defaultPatcherRepo = 'revanced/revanced-patcher';
|
||||
@ -29,9 +34,14 @@ class ManagerAPI {
|
||||
String defaultIntegrationsRepo = 'revanced/revanced-integrations';
|
||||
String defaultCliRepo = 'revanced/revanced-cli';
|
||||
String defaultManagerRepo = 'revanced/revanced-manager';
|
||||
String? patchesVersion = '';
|
||||
bool isDefaultPatchesRepo() {
|
||||
return getPatchesRepo() == 'revanced/revanced-patches';
|
||||
}
|
||||
|
||||
Future<void> initialize() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
isRooted = await _rootAPI.isRooted();
|
||||
storedPatchesFile =
|
||||
(await getApplicationDocumentsDirectory()).path + storedPatchesFile;
|
||||
}
|
||||
@ -98,12 +108,12 @@ class ManagerAPI {
|
||||
await _prefs.setBool('useDarkTheme', value);
|
||||
}
|
||||
|
||||
bool isSentryEnabled() {
|
||||
return _prefs.getBool('sentryEnabled') ?? true;
|
||||
bool areUniversalPatchesEnabled() {
|
||||
return _prefs.getBool('universalPatchesEnabled') ?? false;
|
||||
}
|
||||
|
||||
Future<void> setSentryStatus(bool value) async {
|
||||
await _prefs.setBool('sentryEnabled', value);
|
||||
Future<void> enableUniversalPatchesStatus(bool value) async {
|
||||
await _prefs.setBool('universalPatchesEnabled', value);
|
||||
}
|
||||
|
||||
bool areExperimentalPatchesEnabled() {
|
||||
@ -114,6 +124,14 @@ class ManagerAPI {
|
||||
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 {
|
||||
final Directory dir = Directory('/data/local/tmp/revanced-manager');
|
||||
if (await dir.exists()) {
|
||||
@ -123,29 +141,34 @@ class ManagerAPI {
|
||||
|
||||
Future<void> deleteKeystore() async {
|
||||
final File keystore = File(
|
||||
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore');
|
||||
keystoreFile,
|
||||
);
|
||||
if (await keystore.exists()) {
|
||||
await keystore.delete();
|
||||
}
|
||||
}
|
||||
|
||||
List<PatchedApplication> getPatchedApps() {
|
||||
List<String> apps = _prefs.getStringList('patchedApps') ?? [];
|
||||
final List<String> apps = _prefs.getStringList('patchedApps') ?? [];
|
||||
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
|
||||
}
|
||||
|
||||
Future<void> setPatchedApps(List<PatchedApplication> patchedApps) async {
|
||||
Future<void> setPatchedApps(
|
||||
List<PatchedApplication> patchedApps,
|
||||
) async {
|
||||
if (patchedApps.length > 1) {
|
||||
patchedApps.sort((a, b) => a.name.compareTo(b.name));
|
||||
}
|
||||
await _prefs.setStringList('patchedApps',
|
||||
patchedApps.map((a) => json.encode(a.toJson())).toList());
|
||||
await _prefs.setStringList(
|
||||
'patchedApps',
|
||||
patchedApps.map((a) => json.encode(a.toJson())).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> savePatchedApp(PatchedApplication app) async {
|
||||
List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
final List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
patchedApps.removeWhere((a) => a.packageName == app.packageName);
|
||||
ApplicationWithIcon? installed = await DeviceApps.getApp(
|
||||
final ApplicationWithIcon? installed = await DeviceApps.getApp(
|
||||
app.packageName,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
@ -159,17 +182,19 @@ class ManagerAPI {
|
||||
}
|
||||
|
||||
Future<void> deletePatchedApp(PatchedApplication app) async {
|
||||
List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
final List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
patchedApps.removeWhere((a) => a.packageName == app.packageName);
|
||||
await setPatchedApps(patchedApps);
|
||||
}
|
||||
|
||||
void clearAllData() async {
|
||||
Future<void> clearAllData() async {
|
||||
try {
|
||||
_revancedAPI.clearAllCache();
|
||||
_githubAPI.clearAllCache();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,21 +204,23 @@ class ManagerAPI {
|
||||
|
||||
Future<List<Patch>> getPatches() async {
|
||||
try {
|
||||
String repoName = getPatchesRepo();
|
||||
final String repoName = getPatchesRepo();
|
||||
if (repoName == defaultPatchesRepo) {
|
||||
return await _revancedAPI.getPatches();
|
||||
} else {
|
||||
return await _githubAPI.getPatches(repoName);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> downloadPatches() async {
|
||||
try {
|
||||
String repoName = getPatchesRepo();
|
||||
final String repoName = getPatchesRepo();
|
||||
if (repoName == defaultPatchesRepo) {
|
||||
return await _revancedAPI.getLatestReleaseFile(
|
||||
'.jar',
|
||||
@ -202,15 +229,17 @@ class ManagerAPI {
|
||||
} else {
|
||||
return await _githubAPI.getLatestReleaseFile('.jar', repoName);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> downloadIntegrations() async {
|
||||
try {
|
||||
String repoName = getIntegrationsRepo();
|
||||
final String repoName = getIntegrationsRepo();
|
||||
if (repoName == defaultIntegrationsRepo) {
|
||||
return await _revancedAPI.getLatestReleaseFile(
|
||||
'.apk',
|
||||
@ -219,22 +248,33 @@ class ManagerAPI {
|
||||
} else {
|
||||
return await _githubAPI.getLatestReleaseFile('.apk', repoName);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> downloadManager() async {
|
||||
return await _revancedAPI.getLatestReleaseFile('.apk', defaultManagerRepo);
|
||||
return await _revancedAPI.getLatestReleaseFile(
|
||||
'.apk',
|
||||
defaultManagerRepo,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> getLatestPatcherReleaseTime() async {
|
||||
return await _revancedAPI.getLatestReleaseTime('.gz', defaultPatcherRepo);
|
||||
return await _revancedAPI.getLatestReleaseTime(
|
||||
'.gz',
|
||||
defaultPatcherRepo,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> getLatestManagerReleaseTime() async {
|
||||
return await _revancedAPI.getLatestReleaseTime('.apk', defaultManagerRepo);
|
||||
return await _revancedAPI.getLatestReleaseTime(
|
||||
'.apk',
|
||||
defaultManagerRepo,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> getLatestManagerVersion() async {
|
||||
@ -252,16 +292,28 @@ class ManagerAPI {
|
||||
}
|
||||
|
||||
Future<String> getCurrentManagerVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
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(
|
||||
List<PatchedApplication> patchedApps,
|
||||
) async {
|
||||
List<PatchedApplication> toRemove = [];
|
||||
for (PatchedApplication app in patchedApps) {
|
||||
bool isRemove = await isAppUninstalled(app);
|
||||
final List<PatchedApplication> toRemove = [];
|
||||
for (final PatchedApplication app in patchedApps) {
|
||||
final bool isRemove = await isAppUninstalled(app);
|
||||
if (isRemove) {
|
||||
toRemove.add(app);
|
||||
}
|
||||
@ -272,13 +324,13 @@ class ManagerAPI {
|
||||
Future<List<PatchedApplication>> getUnsavedApps(
|
||||
List<PatchedApplication> patchedApps,
|
||||
) async {
|
||||
List<PatchedApplication> unsavedApps = [];
|
||||
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
final List<PatchedApplication> unsavedApps = [];
|
||||
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
if (hasRootPermissions) {
|
||||
List<String> installedApps = await _rootAPI.getInstalledApps();
|
||||
for (String packageName in installedApps) {
|
||||
final List<String> installedApps = await _rootAPI.getInstalledApps();
|
||||
for (final String packageName in installedApps) {
|
||||
if (!patchedApps.any((app) => app.packageName == packageName)) {
|
||||
ApplicationWithIcon? application = await DeviceApps.getApp(
|
||||
final ApplicationWithIcon? application = await DeviceApps.getApp(
|
||||
packageName,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
@ -299,15 +351,13 @@ class ManagerAPI {
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Application> userApps = await DeviceApps.getInstalledApplications(
|
||||
includeSystemApps: false,
|
||||
includeAppIcons: false,
|
||||
);
|
||||
for (Application app in userApps) {
|
||||
final List<Application> userApps =
|
||||
await DeviceApps.getInstalledApplications();
|
||||
for (final Application app in userApps) {
|
||||
if (app.packageName.startsWith('app.revanced') &&
|
||||
!app.packageName.startsWith('app.revanced.manager.') &&
|
||||
!patchedApps.any((uapp) => uapp.packageName == app.packageName)) {
|
||||
ApplicationWithIcon? application = await DeviceApps.getApp(
|
||||
final ApplicationWithIcon? application = await DeviceApps.getApp(
|
||||
app.packageName,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
@ -321,7 +371,6 @@ class ManagerAPI {
|
||||
apkFilePath: application.apkFilePath,
|
||||
icon: application.icon,
|
||||
patchDate: DateTime.now(),
|
||||
isRooted: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -331,25 +380,29 @@ class ManagerAPI {
|
||||
}
|
||||
|
||||
Future<void> reAssessSavedApps() async {
|
||||
List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
List<PatchedApplication> unsavedApps = await getUnsavedApps(patchedApps);
|
||||
final List<PatchedApplication> patchedApps = getPatchedApps();
|
||||
final List<PatchedApplication> unsavedApps =
|
||||
await getUnsavedApps(patchedApps);
|
||||
patchedApps.addAll(unsavedApps);
|
||||
List<PatchedApplication> toRemove = await getAppsToRemove(patchedApps);
|
||||
final List<PatchedApplication> toRemove =
|
||||
await getAppsToRemove(patchedApps);
|
||||
patchedApps.removeWhere((a) => toRemove.contains(a));
|
||||
for (PatchedApplication app in patchedApps) {
|
||||
for (final PatchedApplication app in patchedApps) {
|
||||
app.hasUpdates =
|
||||
await hasAppUpdates(app.originalPackageName, app.patchDate);
|
||||
app.changelog =
|
||||
await getAppChangelog(app.originalPackageName, app.patchDate);
|
||||
if (!app.hasUpdates) {
|
||||
String? currentInstalledVersion =
|
||||
final String? currentInstalledVersion =
|
||||
(await DeviceApps.getApp(app.packageName))?.versionName;
|
||||
if (currentInstalledVersion != null) {
|
||||
String currentSavedVersion = app.version;
|
||||
int currentInstalledVersionInt = int.parse(
|
||||
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''));
|
||||
int currentSavedVersionInt =
|
||||
int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
|
||||
final String currentSavedVersion = app.version;
|
||||
final int currentInstalledVersionInt = int.parse(
|
||||
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''),
|
||||
);
|
||||
final int currentSavedVersionInt = int.parse(
|
||||
currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''),
|
||||
);
|
||||
if (currentInstalledVersionInt > currentSavedVersionInt) {
|
||||
app.hasUpdates = true;
|
||||
}
|
||||
@ -361,9 +414,9 @@ class ManagerAPI {
|
||||
|
||||
Future<bool> isAppUninstalled(PatchedApplication app) async {
|
||||
bool existsRoot = false;
|
||||
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
||||
final bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
||||
if (app.isRooted) {
|
||||
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
if (hasRootPermissions) {
|
||||
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
|
||||
}
|
||||
@ -372,8 +425,11 @@ class ManagerAPI {
|
||||
return !existsNonRoot;
|
||||
}
|
||||
|
||||
Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
|
||||
List<String> commits = await _githubAPI.getCommits(
|
||||
Future<bool> hasAppUpdates(
|
||||
String packageName,
|
||||
DateTime patchDate,
|
||||
) async {
|
||||
final List<String> commits = await _githubAPI.getCommits(
|
||||
packageName,
|
||||
getPatchesRepo(),
|
||||
patchDate,
|
||||
@ -382,7 +438,9 @@ class ManagerAPI {
|
||||
}
|
||||
|
||||
Future<List<String>> getAppChangelog(
|
||||
String packageName, DateTime patchDate) async {
|
||||
String packageName,
|
||||
DateTime patchDate,
|
||||
) async {
|
||||
List<String> newCommits = await _githubAPI.getCommits(
|
||||
packageName,
|
||||
getPatchesRepo(),
|
||||
@ -408,38 +466,59 @@ class ManagerAPI {
|
||||
return app != null && app.isSplit;
|
||||
}
|
||||
|
||||
Future<void> setSelectedPatches(String app, List<String> patches) async {
|
||||
Future<void> setSelectedPatches(
|
||||
String app,
|
||||
List<String> patches,
|
||||
) async {
|
||||
final File selectedPatchesFile = File(storedPatchesFile);
|
||||
Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
|
||||
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
|
||||
if (patches.isEmpty) {
|
||||
patchesMap.remove(app);
|
||||
} else {
|
||||
patchesMap[app] = patches;
|
||||
}
|
||||
if (selectedPatchesFile.existsSync()) {
|
||||
selectedPatchesFile.createSync(recursive: true);
|
||||
}
|
||||
selectedPatchesFile.writeAsString(jsonEncode(patchesMap));
|
||||
}
|
||||
|
||||
Future<List<String>> getSelectedPatches(String app) async {
|
||||
Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
|
||||
if (patchesMap.isNotEmpty) {
|
||||
final List<String> patches =
|
||||
List.from(patchesMap.putIfAbsent(app, () => List.empty()));
|
||||
return patches;
|
||||
// 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 List.empty();
|
||||
return defaultPatches;
|
||||
}
|
||||
|
||||
Future<List<String>> getSelectedPatches(String app) async {
|
||||
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
|
||||
final List<String> defaultPatches = await getDefaultPatches();
|
||||
return List.from(patchesMap.putIfAbsent(app, () => defaultPatches));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> readSelectedPatchesFile() async {
|
||||
final File selectedPatchesFile = File(storedPatchesFile);
|
||||
if (selectedPatchesFile.existsSync()) {
|
||||
String string = selectedPatchesFile.readAsStringSync();
|
||||
if (string.trim().isEmpty) return {};
|
||||
return json.decode(string);
|
||||
if (!selectedPatchesFile.existsSync()) {
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
final String string = selectedPatchesFile.readAsStringSync();
|
||||
if (string.trim().isEmpty) {
|
||||
return {};
|
||||
}
|
||||
return jsonDecode(string);
|
||||
}
|
||||
|
||||
Future<void> resetLastSelectedPatches() async {
|
||||
|
@ -1,7 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_installer/app_installer.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cr_file_saver/file_saver.dart';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
@ -10,9 +13,7 @@ import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:revanced_manager/services/manager_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:cr_file_saver/file_saver.dart';
|
||||
|
||||
@lazySingleton
|
||||
class PatcherAPI {
|
||||
@ -24,11 +25,12 @@ class PatcherAPI {
|
||||
late Directory _tmpDir;
|
||||
late File _keyStoreFile;
|
||||
List<Patch> _patches = [];
|
||||
Map filteredPatches = <String, List<Patch>>{};
|
||||
File? _outFile;
|
||||
|
||||
Future<void> initialize() async {
|
||||
await _loadPatches();
|
||||
Directory appCache = await getTemporaryDirectory();
|
||||
final Directory appCache = await getTemporaryDirectory();
|
||||
_dataDir = await getExternalStorageDirectory() ?? appCache;
|
||||
_tmpDir = Directory('${appCache.path}/patcher');
|
||||
_keyStoreFile = File('${_dataDir.path}/revanced-manager.keystore');
|
||||
@ -46,19 +48,43 @@ class PatcherAPI {
|
||||
if (_patches.isEmpty) {
|
||||
_patches = await _managerAPI.getPatches();
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
_patches = List.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
|
||||
List<ApplicationWithIcon> filteredApps = [];
|
||||
for (Patch patch in _patches) {
|
||||
for (Package package in patch.compatiblePackages) {
|
||||
Future<List<ApplicationWithIcon>> getFilteredInstalledApps(
|
||||
bool showUniversalPatches,
|
||||
) async {
|
||||
final List<ApplicationWithIcon> filteredApps = [];
|
||||
final bool allAppsIncluded =
|
||||
_patches.any((patch) => patch.compatiblePackages.isEmpty) &&
|
||||
showUniversalPatches;
|
||||
if (allAppsIncluded) {
|
||||
final allPackages = await DeviceApps.getInstalledApplications(
|
||||
includeAppIcons: true,
|
||||
onlyAppsWithLaunchIntent: true,
|
||||
);
|
||||
for (final pkg in allPackages) {
|
||||
if (!filteredApps.any((app) => app.packageName == pkg.packageName)) {
|
||||
final appInfo = await DeviceApps.getApp(
|
||||
pkg.packageName,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
if (appInfo != null) {
|
||||
filteredApps.add(appInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final Patch patch in _patches) {
|
||||
for (final Package package in patch.compatiblePackages) {
|
||||
try {
|
||||
if (!filteredApps.any((app) => app.packageName == package.name)) {
|
||||
ApplicationWithIcon? app = await DeviceApps.getApp(
|
||||
final ApplicationWithIcon? app = await DeviceApps.getApp(
|
||||
package.name,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
@ -66,49 +92,43 @@ class PatcherAPI {
|
||||
filteredApps.add(app);
|
||||
}
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
continue;
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredApps;
|
||||
}
|
||||
|
||||
Future<List<Patch>> getFilteredPatches(String packageName) async {
|
||||
return _patches
|
||||
.where((patch) =>
|
||||
!patch.name.contains('settings') &&
|
||||
patch.compatiblePackages.any((pack) => pack.name == packageName))
|
||||
.toList();
|
||||
List<Patch> getFilteredPatches(String packageName) {
|
||||
if (!filteredPatches.keys.contains(packageName)) {
|
||||
final List<Patch> patches = _patches
|
||||
.where(
|
||||
(patch) =>
|
||||
patch.compatiblePackages.isEmpty ||
|
||||
!patch.name.contains('settings') &&
|
||||
patch.compatiblePackages
|
||||
.any((pack) => pack.name == packageName),
|
||||
)
|
||||
.toList();
|
||||
filteredPatches[packageName] = patches;
|
||||
}
|
||||
return filteredPatches[packageName];
|
||||
}
|
||||
|
||||
Future<List<Patch>> getAppliedPatches(List<String> appliedPatches) async {
|
||||
Future<List<Patch>> getAppliedPatches(
|
||||
List<String> appliedPatches,
|
||||
) async {
|
||||
return _patches
|
||||
.where((patch) => appliedPatches.contains(patch.name))
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool dependencyNeedsIntegrations(String name) {
|
||||
return name.contains('integrations') ||
|
||||
_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 {
|
||||
Future<bool> needsResourcePatching(
|
||||
List<Patch> selectedPatches,
|
||||
) async {
|
||||
return selectedPatches.any(
|
||||
(patch) => patch.dependencies.any(
|
||||
(dep) => dep.contains('resource-'),
|
||||
@ -129,7 +149,7 @@ class PatcherAPI {
|
||||
String originalFilePath,
|
||||
) async {
|
||||
try {
|
||||
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
if (hasRootPermissions) {
|
||||
originalFilePath = await _rootAPI.getOriginalFilePath(
|
||||
packageName,
|
||||
@ -137,8 +157,10 @@ class PatcherAPI {
|
||||
);
|
||||
}
|
||||
return originalFilePath;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return originalFilePath;
|
||||
}
|
||||
}
|
||||
@ -148,11 +170,10 @@ class PatcherAPI {
|
||||
String originalFilePath,
|
||||
List<Patch> selectedPatches,
|
||||
) async {
|
||||
bool mergeIntegrations = await needsIntegrations(selectedPatches);
|
||||
bool includeSettings = await needsSettingsPatch(selectedPatches);
|
||||
final bool includeSettings = await needsSettingsPatch(selectedPatches);
|
||||
if (includeSettings) {
|
||||
try {
|
||||
Patch? settingsPatch = _patches.firstWhereOrNull(
|
||||
final Patch? settingsPatch = _patches.firstWhereOrNull(
|
||||
(patch) =>
|
||||
patch.name.contains('settings') &&
|
||||
patch.compatiblePackages.any((pack) => pack.name == packageName),
|
||||
@ -160,24 +181,22 @@ class PatcherAPI {
|
||||
if (settingsPatch != null) {
|
||||
selectedPatches.add(settingsPatch);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
// ignore
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
File? patchBundleFile = await _managerAPI.downloadPatches();
|
||||
File? integrationsFile;
|
||||
if (mergeIntegrations) {
|
||||
integrationsFile = await _managerAPI.downloadIntegrations();
|
||||
}
|
||||
final File? patchBundleFile = await _managerAPI.downloadPatches();
|
||||
final File? integrationsFile = await _managerAPI.downloadIntegrations();
|
||||
if (patchBundleFile != null) {
|
||||
_dataDir.createSync();
|
||||
_tmpDir.createSync();
|
||||
Directory workDir = _tmpDir.createTempSync('tmp-');
|
||||
File inputFile = File('${workDir.path}/base.apk');
|
||||
File patchedFile = File('${workDir.path}/patched.apk');
|
||||
final Directory workDir = _tmpDir.createTempSync('tmp-');
|
||||
final File inputFile = File('${workDir.path}/base.apk');
|
||||
final File patchedFile = File('${workDir.path}/patched.apk');
|
||||
_outFile = File('${workDir.path}/out.apk');
|
||||
Directory cacheDir = Directory('${workDir.path}/cache');
|
||||
final Directory cacheDir = Directory('${workDir.path}/cache');
|
||||
cacheDir.createSync();
|
||||
try {
|
||||
await patcherChannel.invokeMethod(
|
||||
@ -191,16 +210,17 @@ class PatcherAPI {
|
||||
'inputFilePath': inputFile.path,
|
||||
'patchedFilePath': patchedFile.path,
|
||||
'outFilePath': _outFile!.path,
|
||||
'integrationsPath': mergeIntegrations ? integrationsFile!.path : '',
|
||||
'integrationsPath': integrationsFile!.path,
|
||||
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
||||
'cacheDirPath': cacheDir.path,
|
||||
'mergeIntegrations': mergeIntegrations,
|
||||
'keyStoreFilePath': _keyStoreFile.path,
|
||||
'keystorePassword': _managerAPI.getKeystorePassword(),
|
||||
},
|
||||
);
|
||||
} on Exception catch (e, s) {
|
||||
print(e);
|
||||
throw await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,7 +229,7 @@ class PatcherAPI {
|
||||
if (_outFile != null) {
|
||||
try {
|
||||
if (patchedApp.isRooted) {
|
||||
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||
if (hasRootPermissions) {
|
||||
return _rootAPI.installApp(
|
||||
patchedApp.packageName,
|
||||
@ -219,83 +239,85 @@ class PatcherAPI {
|
||||
}
|
||||
} else {
|
||||
await AppInstaller.installApk(_outFile!.path);
|
||||
return await DeviceApps.isAppInstalled(patchedApp.packageName);
|
||||
return await DeviceApps.isAppInstalled(
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void exportPatchedFile(String appName, String version) {
|
||||
try {
|
||||
if (_outFile != null) {
|
||||
String newName = _getFileName(appName, version);
|
||||
|
||||
// This is temporary workaround to populate initial file name
|
||||
// ref: https://github.com/Cleveroad/cr_file_saver/issues/7
|
||||
int lastSeparator = _outFile!.path.lastIndexOf('/');
|
||||
String newSourcePath = _outFile!.path.substring(0, lastSeparator + 1) + newName;
|
||||
_outFile!.copySync(newSourcePath);
|
||||
|
||||
CRFileSaver.saveFileWithDialog(SaveFileDialogParams(
|
||||
sourceFilePath: newSourcePath,
|
||||
destinationFileName: newName
|
||||
));
|
||||
final String newName = _getFileName(appName, version);
|
||||
CRFileSaver.saveFileWithDialog(
|
||||
SaveFileDialogParams(
|
||||
sourceFilePath: _outFile!.path,
|
||||
destinationFileName: newName,
|
||||
),
|
||||
);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
Sentry.captureException(e, stackTrace: s);
|
||||
}
|
||||
}
|
||||
|
||||
void sharePatchedFile(String appName, String version) {
|
||||
try {
|
||||
if (_outFile != null) {
|
||||
String newName = _getFileName(appName, version);
|
||||
int lastSeparator = _outFile!.path.lastIndexOf('/');
|
||||
String newPath =
|
||||
final String newName = _getFileName(appName, version);
|
||||
final int lastSeparator = _outFile!.path.lastIndexOf('/');
|
||||
final String newPath =
|
||||
_outFile!.path.substring(0, lastSeparator + 1) + newName;
|
||||
File shareFile = _outFile!.copySync(newPath);
|
||||
final File shareFile = _outFile!.copySync(newPath);
|
||||
ShareExtend.share(shareFile.path, 'file');
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _getFileName(String appName, String version) {
|
||||
String prefix = appName.toLowerCase().replaceAll(' ', '-');
|
||||
String newName = '$prefix-revanced_v$version.apk';
|
||||
return newName;
|
||||
|
||||
final String prefix = appName.toLowerCase().replaceAll(' ', '-');
|
||||
final String newName = '$prefix-revanced_v$version.apk';
|
||||
return newName;
|
||||
}
|
||||
|
||||
Future<void> sharePatcherLog(String logs) async {
|
||||
Directory appCache = await getTemporaryDirectory();
|
||||
Directory logDir = Directory('${appCache.path}/logs');
|
||||
final Directory appCache = await getTemporaryDirectory();
|
||||
final Directory logDir = Directory('${appCache.path}/logs');
|
||||
logDir.createSync();
|
||||
String dateTime = DateTime.now()
|
||||
final String dateTime = DateTime.now()
|
||||
.toIso8601String()
|
||||
.replaceAll('-', '')
|
||||
.replaceAll(':', '')
|
||||
.replaceAll('T', '')
|
||||
.replaceAll('.', '');
|
||||
File log = File('${logDir.path}/revanced-manager_patcher_$dateTime.log');
|
||||
final File log =
|
||||
File('${logDir.path}/revanced-manager_patcher_$dateTime.log');
|
||||
log.writeAsStringSync(logs);
|
||||
ShareExtend.share(log.path, 'file');
|
||||
}
|
||||
|
||||
String getRecommendedVersion(String packageName) {
|
||||
Map<String, int> versions = {};
|
||||
for (Patch patch in _patches) {
|
||||
Package? package = patch.compatiblePackages.firstWhereOrNull(
|
||||
String getSuggestedVersion(String packageName) {
|
||||
final Map<String, int> versions = {};
|
||||
for (final Patch patch in _patches) {
|
||||
final Package? package = patch.compatiblePackages.firstWhereOrNull(
|
||||
(pack) => pack.name == packageName,
|
||||
);
|
||||
if (package != null) {
|
||||
for (String version in package.versions) {
|
||||
for (final String version in package.versions) {
|
||||
versions.update(
|
||||
version,
|
||||
(value) => versions[version]! + 1,
|
||||
@ -305,7 +327,7 @@ class PatcherAPI {
|
||||
}
|
||||
}
|
||||
if (versions.isNotEmpty) {
|
||||
var entries = versions.entries.toList()
|
||||
final entries = versions.entries.toList()
|
||||
..sort((a, b) => a.value.compareTo(b.value));
|
||||
versions
|
||||
..clear()
|
||||
|
@ -1,69 +1,64 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:native_dio_client/native_dio_client.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
|
||||
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/utils/check_for_gms.dart';
|
||||
import 'package:timeago/timeago.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:sentry_dio/sentry_dio.dart';
|
||||
|
||||
@lazySingleton
|
||||
class RevancedAPI {
|
||||
late Dio _dio = Dio();
|
||||
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
|
||||
final Options _cacheOptions = buildCacheOptions(
|
||||
const Duration(hours: 6),
|
||||
|
||||
final _cacheOptions = CacheOptions(
|
||||
store: MemCacheStore(),
|
||||
maxStale: const Duration(days: 1),
|
||||
priority: CachePriority.high,
|
||||
);
|
||||
|
||||
Future<void> initialize(String apiUrl) async {
|
||||
try {
|
||||
bool isGMSInstalled = await checkForGMS();
|
||||
|
||||
if (!isGMSInstalled) {
|
||||
_dio = Dio(BaseOptions(
|
||||
_dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: apiUrl,
|
||||
));
|
||||
print('ReVanced API: Using default engine + $isGMSInstalled');
|
||||
} else {
|
||||
_dio = Dio(BaseOptions(
|
||||
baseUrl: apiUrl,
|
||||
))
|
||||
..httpClientAdapter = NativeAdapter();
|
||||
print('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);
|
||||
|
||||
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> clearAllCache() async {
|
||||
try {
|
||||
await _dioCacheManager.clearAll();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
await _cacheOptions.store!.clean();
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, List<dynamic>>> getContributors() async {
|
||||
Map<String, List<dynamic>> contributors = {};
|
||||
final Map<String, List<dynamic>> contributors = {};
|
||||
try {
|
||||
var response = await _dio.get('/contributors', options: _cacheOptions);
|
||||
List<dynamic> repositories = response.data['repositories'];
|
||||
for (Map<String, dynamic> repo in repositories) {
|
||||
String name = repo['name'];
|
||||
final response = await _dio.get('/contributors');
|
||||
final List<dynamic> repositories = response.data['repositories'];
|
||||
for (final Map<String, dynamic> repo in repositories) {
|
||||
final String name = repo['name'];
|
||||
contributors[name] = repo['contributors'];
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return contributors;
|
||||
@ -71,11 +66,13 @@ class RevancedAPI {
|
||||
|
||||
Future<List<Patch>> getPatches() async {
|
||||
try {
|
||||
var response = await _dio.get('/patches', options: _cacheOptions);
|
||||
List<dynamic> patches = response.data;
|
||||
final response = await _dio.get('/patches');
|
||||
final List<dynamic> patches = response.data;
|
||||
return patches.map((patch) => Patch.fromJson(patch)).toList();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return List.empty();
|
||||
}
|
||||
}
|
||||
@ -85,15 +82,17 @@ class RevancedAPI {
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
var response = await _dio.get('/tools', options: _cacheOptions);
|
||||
List<dynamic> tools = response.data['tools'];
|
||||
final response = await _dio.get('/tools');
|
||||
final List<dynamic> tools = response.data['tools'];
|
||||
return tools.firstWhereOrNull(
|
||||
(t) =>
|
||||
t['repository'] == repoName &&
|
||||
(t['name'] as String).endsWith(extension),
|
||||
);
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -103,52 +102,99 @@ class RevancedAPI {
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
Map<String, dynamic>? release = await _getLatestRelease(
|
||||
final Map<String, dynamic>? release = await _getLatestRelease(
|
||||
extension,
|
||||
repoName,
|
||||
);
|
||||
if (release != null) {
|
||||
return release['version'];
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<File?> getLatestReleaseFile(String extension, String repoName) async {
|
||||
Future<File?> getLatestReleaseFile(
|
||||
String extension,
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
Map<String, dynamic>? release = await _getLatestRelease(
|
||||
final Map<String, dynamic>? release = await _getLatestRelease(
|
||||
extension,
|
||||
repoName,
|
||||
);
|
||||
if (release != null) {
|
||||
String url = release['browser_download_url'];
|
||||
final String url = release['browser_download_url'];
|
||||
return await DefaultCacheManager().getSingleFile(url);
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
StreamController<double> managerUpdateProgress = StreamController<double>();
|
||||
|
||||
void updateManagerDownloadProgress(int progress) {
|
||||
managerUpdateProgress.add(progress.toDouble());
|
||||
}
|
||||
|
||||
Stream<double> getManagerUpdateProgress() {
|
||||
return managerUpdateProgress.stream;
|
||||
}
|
||||
|
||||
void disposeManagerUpdateProgress() {
|
||||
managerUpdateProgress.close();
|
||||
}
|
||||
|
||||
Future<File?> downloadManager() async {
|
||||
final Map<String, dynamic>? release = await _getLatestRelease(
|
||||
'.apk',
|
||||
'revanced/revanced-manager',
|
||||
);
|
||||
File? outputFile;
|
||||
await for (final result in DefaultCacheManager().getFileStream(
|
||||
release!['browser_download_url'] as String,
|
||||
withProgress: true,
|
||||
)) {
|
||||
if (result is DownloadProgress) {
|
||||
final totalSize = result.totalSize ?? 10000000;
|
||||
final progress = (result.downloaded / totalSize * 100).round();
|
||||
|
||||
updateManagerDownloadProgress(progress);
|
||||
} else if (result is FileInfo) {
|
||||
// The download is complete; convert the FileInfo object to a File object
|
||||
outputFile = File(result.file.path);
|
||||
}
|
||||
}
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
Future<String?> getLatestReleaseTime(
|
||||
String extension,
|
||||
String repoName,
|
||||
) async {
|
||||
try {
|
||||
Map<String, dynamic>? release = await _getLatestRelease(
|
||||
final Map<String, dynamic>? release = await _getLatestRelease(
|
||||
extension,
|
||||
repoName,
|
||||
);
|
||||
if (release != null) {
|
||||
DateTime timestamp = DateTime.parse(release['timestamp'] as String);
|
||||
final DateTime timestamp =
|
||||
DateTime.parse(release['timestamp'] as String);
|
||||
return format(timestamp, locale: 'en_short');
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:root/root.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
class RootAPI {
|
||||
final String _managerDirPath = '/data/local/tmp/revanced-manager';
|
||||
@ -8,10 +8,12 @@ class RootAPI {
|
||||
|
||||
Future<bool> isRooted() async {
|
||||
try {
|
||||
bool? isRooted = await Root.isRootAvailable();
|
||||
final bool? isRooted = await Root.isRootAvailable();
|
||||
return isRooted != null && isRooted;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -24,8 +26,10 @@ class RootAPI {
|
||||
return isRooted != null && isRooted;
|
||||
}
|
||||
return false;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -70,16 +74,18 @@ class RootAPI {
|
||||
|
||||
Future<List<String>> getInstalledApps() async {
|
||||
try {
|
||||
String? res = await Root.exec(
|
||||
final String? res = await Root.exec(
|
||||
cmd: 'ls "$_managerDirPath"',
|
||||
);
|
||||
if (res != null) {
|
||||
List<String> apps = res.split('\n');
|
||||
final List<String> apps = res.split('\n');
|
||||
apps.removeWhere((pack) => pack.isEmpty);
|
||||
return apps.map((pack) => pack.trim()).toList();
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return List.empty();
|
||||
}
|
||||
return List.empty();
|
||||
@ -125,19 +131,21 @@ class RootAPI {
|
||||
await installApk(packageName, patchedFilePath);
|
||||
await mountApk(packageName, originalFilePath);
|
||||
return true;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> installServiceDScript(String packageName) async {
|
||||
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'
|
||||
'base_path=$_managerDirPath/$packageName/base.apk\n'
|
||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path';
|
||||
String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
||||
r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path';
|
||||
final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
||||
await Root.exec(
|
||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||
);
|
||||
@ -145,10 +153,10 @@ class RootAPI {
|
||||
}
|
||||
|
||||
Future<void> installPostFsDataScript(String packageName) async {
|
||||
String content = '#!/system/bin/sh\n'
|
||||
final String content = '#!/system/bin/sh\n'
|
||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||
'[ ! -z \$stock_path ] && umount -l \$stock_path';
|
||||
String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
|
||||
r'[ ! -z $stock_path ] && umount -l $stock_path';
|
||||
final String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
|
||||
await Root.exec(
|
||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||
);
|
||||
@ -156,7 +164,7 @@ class RootAPI {
|
||||
}
|
||||
|
||||
Future<void> installApk(String packageName, String patchedFilePath) async {
|
||||
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
await Root.exec(
|
||||
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
||||
);
|
||||
@ -169,7 +177,7 @@ class RootAPI {
|
||||
}
|
||||
|
||||
Future<void> mountApk(String packageName, String originalFilePath) async {
|
||||
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
await Root.exec(
|
||||
cmd: 'am force-stop "$packageName"',
|
||||
);
|
||||
@ -182,7 +190,7 @@ class RootAPI {
|
||||
}
|
||||
|
||||
Future<bool> isMounted(String packageName) async {
|
||||
String? res = await Root.exec(
|
||||
final String? res = await Root.exec(
|
||||
cmd: 'cat /proc/mounts | grep $packageName',
|
||||
);
|
||||
return res != null && res.isNotEmpty;
|
||||
@ -192,7 +200,7 @@ class RootAPI {
|
||||
String packageName,
|
||||
String originalFilePath,
|
||||
) async {
|
||||
bool isInstalled = await isAppInstalled(packageName);
|
||||
final bool isInstalled = await isAppInstalled(packageName);
|
||||
if (isInstalled && await isMounted(packageName)) {
|
||||
originalFilePath = '$_managerDirPath/$packageName/original.apk';
|
||||
await setPermissions(
|
||||
@ -209,7 +217,8 @@ class RootAPI {
|
||||
String packageName,
|
||||
String originalFilePath,
|
||||
) async {
|
||||
String originalRootPath = '$_managerDirPath/$packageName/original.apk';
|
||||
final String originalRootPath =
|
||||
'$_managerDirPath/$packageName/original.apk';
|
||||
await Root.exec(
|
||||
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||
);
|
||||
|
@ -3,7 +3,6 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
var lightCustomColorScheme = ColorScheme.fromSeed(
|
||||
seedColor: Colors.blue,
|
||||
brightness: Brightness.light,
|
||||
primary: const Color(0xff1B73E8),
|
||||
);
|
||||
|
||||
@ -13,7 +12,7 @@ var lightCustomTheme = ThemeData(
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
labelTextStyle: MaterialStateProperty.all(
|
||||
TextStyle(
|
||||
color: lightCustomColorScheme.secondary,
|
||||
color: lightCustomColorScheme.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
@ -34,13 +33,12 @@ var darkCustomTheme = ThemeData(
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
labelTextStyle: MaterialStateProperty.all(
|
||||
TextStyle(
|
||||
color: darkCustomColorScheme.secondary,
|
||||
color: darkCustomColorScheme.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
canvasColor: const Color(0xff1B1A1D),
|
||||
scaffoldBackgroundColor: const Color(0xff1B1A1D),
|
||||
toggleableActiveColor: const Color(0xffA5CAFF),
|
||||
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
|
||||
);
|
||||
|
@ -7,65 +7,44 @@ import 'package:revanced_manager/theme.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
class DynamicThemeBuilder extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget home;
|
||||
final Iterable<LocalizationsDelegate> localizationsDelegates;
|
||||
|
||||
const DynamicThemeBuilder({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.home,
|
||||
required this.localizationsDelegates,
|
||||
}) : super(key: key);
|
||||
final String title;
|
||||
final Widget home;
|
||||
final Iterable<LocalizationsDelegate> localizationsDelegates;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DynamicColorBuilder(
|
||||
builder: (lightColorScheme, darkColorScheme) {
|
||||
ThemeData lightDynamicTheme = ThemeData(
|
||||
final ThemeData lightDynamicTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
canvasColor: lightColorScheme?.background,
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: lightColorScheme?.background,
|
||||
indicatorColor: lightColorScheme?.primary.withAlpha(150),
|
||||
labelTextStyle: MaterialStateProperty.all(
|
||||
GoogleFonts.roboto(
|
||||
color: lightColorScheme?.secondary,
|
||||
color: lightColorScheme?.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
iconTheme: MaterialStateProperty.all(
|
||||
IconThemeData(
|
||||
color: lightColorScheme?.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
scaffoldBackgroundColor: lightColorScheme?.background,
|
||||
colorScheme: lightColorScheme?.harmonized(),
|
||||
toggleableActiveColor: lightColorScheme?.primary,
|
||||
textTheme: GoogleFonts.robotoTextTheme(ThemeData.light().textTheme),
|
||||
);
|
||||
ThemeData darkDynamicTheme = ThemeData(
|
||||
final ThemeData darkDynamicTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
canvasColor: darkColorScheme?.background,
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: darkColorScheme?.background,
|
||||
indicatorColor: darkColorScheme?.primary.withOpacity(0.4),
|
||||
labelTextStyle: MaterialStateProperty.all(
|
||||
GoogleFonts.roboto(
|
||||
color: darkColorScheme?.secondary,
|
||||
color: darkColorScheme?.onSurface,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
iconTheme: MaterialStateProperty.all(
|
||||
IconThemeData(
|
||||
color: darkColorScheme?.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
scaffoldBackgroundColor: darkColorScheme?.background,
|
||||
colorScheme: darkColorScheme?.harmonized(),
|
||||
toggleableActiveColor: darkColorScheme?.primary,
|
||||
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
|
||||
);
|
||||
return DynamicTheme(
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide SearchBar;
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
|
||||
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
|
||||
import 'package:stacked/stacked.dart' hide SkeletonLoader;
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
|
||||
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
|
||||
import 'package:revanced_manager/ui/widgets/appSelectorView/not_installed_app_item.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
|
||||
import 'package:stacked/stacked.dart' hide SkeletonLoader;
|
||||
|
||||
class AppSelectorView extends StatefulWidget {
|
||||
const AppSelectorView({Key? key}) : super(key: key);
|
||||
@ -19,7 +20,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<AppSelectorViewModel>.reactive(
|
||||
onModelReady: (model) => model.initialize(),
|
||||
onViewModelReady: (model) => model.initialize(),
|
||||
viewModelBuilder: () => AppSelectorViewModel(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
@ -36,20 +37,19 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
SliverAppBar(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
snap: false,
|
||||
title: I18nText(
|
||||
'appSelectorView.viewTitle',
|
||||
child: Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
@ -61,7 +61,6 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
horizontal: 12.0,
|
||||
),
|
||||
child: SearchBar(
|
||||
showSelectIcon: false,
|
||||
hintText: FlutterI18n.translate(
|
||||
context,
|
||||
'appSelectorView.searchBarHint',
|
||||
@ -78,26 +77,61 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
SliverToBoxAdapter(
|
||||
child: model.noApps
|
||||
? Center(
|
||||
child: I18nText('appSelectorCard.noAppsLabel'),
|
||||
child: I18nText(
|
||||
'appSelectorCard.noAppsLabel',
|
||||
child: Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: model.apps.isEmpty
|
||||
? const AppSkeletonLoader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
||||
.copyWith(bottom: 80),
|
||||
.copyWith(
|
||||
bottom:
|
||||
MediaQuery.of(context).viewPadding.bottom + 8.0,
|
||||
),
|
||||
child: Column(
|
||||
children: model
|
||||
.getFilteredApps(_query)
|
||||
.map((app) => InstalledAppItem(
|
||||
children: [
|
||||
...model
|
||||
.getFilteredApps(_query)
|
||||
.map(
|
||||
(app) => InstalledAppItem(
|
||||
name: app.appName,
|
||||
pkgName: app.packageName,
|
||||
icon: app.icon,
|
||||
patchesCount:
|
||||
model.patchesCount(app.packageName),
|
||||
suggestedVersion:
|
||||
model.getSuggestedVersion(
|
||||
app.packageName,
|
||||
),
|
||||
installedVersion: app.versionName!,
|
||||
onTap: () => model.canSelectInstalled(
|
||||
context, app.packageName),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
...model
|
||||
.getFilteredAppsNames(_query)
|
||||
.map(
|
||||
(app) => NotInstalledAppItem(
|
||||
name: app,
|
||||
patchesCount: model.patchesCount(app),
|
||||
suggestedVersion:
|
||||
model.getSuggestedVersion(app),
|
||||
onTap: () {
|
||||
model.selectApp(app);
|
||||
Navigator.of(context).pop();
|
||||
model.showDownloadToast();
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,30 +1,79 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/services/revanced_api.dart';
|
||||
import 'package:revanced_manager/services/toast.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class AppSelectorViewModel extends BaseViewModel {
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
|
||||
final Toast _toast = locator<Toast>();
|
||||
final List<ApplicationWithIcon> apps = [];
|
||||
List<String> allApps = [];
|
||||
bool noApps = false;
|
||||
bool isRooted = false;
|
||||
int patchesCount(String packageName) {
|
||||
return _patcherAPI.getFilteredPatches(packageName).length;
|
||||
}
|
||||
|
||||
List<Patch> patches = [];
|
||||
|
||||
Future<void> initialize() async {
|
||||
apps.addAll(await _patcherAPI.getFilteredInstalledApps());
|
||||
apps.sort((a, b) => a.appName.compareTo(b.appName));
|
||||
patches = await _revancedAPI.getPatches();
|
||||
isRooted = _managerAPI.isRooted;
|
||||
|
||||
apps.addAll(
|
||||
await _patcherAPI
|
||||
.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
|
||||
);
|
||||
apps.sort(
|
||||
(a, b) => _patcherAPI
|
||||
.getFilteredPatches(b.packageName)
|
||||
.length
|
||||
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
|
||||
);
|
||||
noApps = apps.isEmpty;
|
||||
getAllApps();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void selectApp(ApplicationWithIcon application) async {
|
||||
List<String> getAllApps() {
|
||||
allApps = patches
|
||||
.expand((e) => e.compatiblePackages.map((p) => p.name))
|
||||
.toSet()
|
||||
.where((name) => !apps.any((app) => app.packageName == name))
|
||||
.toList();
|
||||
|
||||
return allApps;
|
||||
}
|
||||
|
||||
String getSuggestedVersion(String packageName) {
|
||||
return _patcherAPI.getSuggestedVersion(packageName);
|
||||
}
|
||||
|
||||
Future<bool> checkSplitApk(String packageName) async {
|
||||
final app = await DeviceApps.getApp(packageName);
|
||||
if (app != null) {
|
||||
return app.isSplit;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> selectApp(ApplicationWithIcon application) async {
|
||||
locator<PatcherViewModel>().selectedApp = PatchedApplication(
|
||||
name: application.appName,
|
||||
packageName: application.packageName,
|
||||
@ -35,27 +84,117 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||
patchDate: DateTime.now(),
|
||||
);
|
||||
locator<PatcherViewModel>().loadLastSelectedPatches();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> canSelectInstalled(
|
||||
BuildContext context,
|
||||
String packageName,
|
||||
) async {
|
||||
final app =
|
||||
await DeviceApps.getApp(packageName, true) as ApplicationWithIcon?;
|
||||
if (app != null) {
|
||||
if (await checkSplitApk(packageName) && !isRooted) {
|
||||
return showSelectFromStorageDialog(context);
|
||||
} else if (!await checkSplitApk(packageName) || isRooted) {
|
||||
selectApp(app);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future showSelectFromStorageDialog(BuildContext context) async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => SimpleDialog(
|
||||
alignment: Alignment.center,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Icon(
|
||||
Icons.block,
|
||||
size: 28,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
I18nText(
|
||||
'appSelectorView.featureNotAvailable',
|
||||
child: const Text(
|
||||
'',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
wordSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
I18nText(
|
||||
'appSelectorView.featureNotAvailableText',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
CustomMaterialButton(
|
||||
onPressed: () => selectAppFromStorage(context).then(
|
||||
(_) {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
label: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.sd_card),
|
||||
const SizedBox(width: 10),
|
||||
I18nText('appSelectorView.selectFromStorageButton'),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomMaterialButton(
|
||||
isFilled: false,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
label: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
I18nText('cancelButton'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> selectAppFromStorage(BuildContext context) async {
|
||||
try {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
final FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['apk'],
|
||||
);
|
||||
if (result != null && result.files.single.path != null) {
|
||||
File apkFile = File(result.files.single.path!);
|
||||
List<String> pathSplit = result.files.single.path!.split("/");
|
||||
final File apkFile = File(result.files.single.path!);
|
||||
final List<String> pathSplit = result.files.single.path!.split('/');
|
||||
pathSplit.removeLast();
|
||||
Directory filePickerCacheDir = Directory(pathSplit.join("/"));
|
||||
Iterable<File> deletableFiles =
|
||||
final Directory filePickerCacheDir = Directory(pathSplit.join('/'));
|
||||
final Iterable<File> deletableFiles =
|
||||
(await filePickerCacheDir.list().toList()).whereType<File>();
|
||||
for (var file in deletableFiles) {
|
||||
if (file.path != apkFile.path && file.path.endsWith(".apk"))
|
||||
for (final file in deletableFiles) {
|
||||
if (file.path != apkFile.path && file.path.endsWith('.apk')) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
ApplicationWithIcon? application = await DeviceApps.getAppFromStorage(
|
||||
final ApplicationWithIcon? application =
|
||||
await DeviceApps.getAppFromStorage(
|
||||
apkFile.path,
|
||||
true,
|
||||
) as ApplicationWithIcon?;
|
||||
@ -71,21 +210,38 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||
isFromStorage: true,
|
||||
);
|
||||
locator<PatcherViewModel>().loadLastSelectedPatches();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
_toast.showBottom('appSelectorView.errorMessage');
|
||||
}
|
||||
}
|
||||
|
||||
List<ApplicationWithIcon> getFilteredApps(String query) {
|
||||
return apps
|
||||
.where((app) =>
|
||||
query.isEmpty ||
|
||||
query.length < 2 ||
|
||||
app.appName.toLowerCase().contains(query.toLowerCase()))
|
||||
.where(
|
||||
(app) =>
|
||||
query.isEmpty ||
|
||||
query.length < 2 ||
|
||||
app.appName.toLowerCase().contains(query.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<String> getFilteredAppsNames(String query) {
|
||||
return allApps
|
||||
.where(
|
||||
(app) =>
|
||||
query.isEmpty ||
|
||||
query.length < 2 ||
|
||||
app.toLowerCase().contains(query.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
void showDownloadToast() =>
|
||||
_toast.showBottom('appSelectorView.downloadToast');
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class ContributorsView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<ContributorsViewModel>.reactive(
|
||||
viewModelBuilder: () => ContributorsViewModel(),
|
||||
onModelReady: (model) => model.getContributors(),
|
||||
onViewModelReady: (model) => model.getContributors(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
@ -23,7 +23,7 @@ class ContributorsView extends StatelessWidget {
|
||||
child: Text(
|
||||
'',
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -57,6 +57,7 @@ class ContributorsView extends StatelessWidget {
|
||||
title: 'contributorsView.managerContributors',
|
||||
contributors: model.managerContributors,
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).viewPadding.bottom)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -11,7 +11,7 @@ class ContributorsViewModel extends BaseViewModel {
|
||||
List<dynamic> managerContributors = [];
|
||||
|
||||
Future<void> getContributors() async {
|
||||
Map<String, List<dynamic>> contributors =
|
||||
final Map<String, List<dynamic>> contributors =
|
||||
await _managerAPI.getContributors();
|
||||
patcherContributors = contributors[_managerAPI.defaultPatcherRepo] ?? [];
|
||||
patchesContributors = contributors[_managerAPI.getPatchesRepo()] ?? [];
|
||||
|
@ -1,13 +1,12 @@
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/homeView/available_updates_card.dart';
|
||||
import 'package:revanced_manager/ui/widgets/homeView/installed_apps_card.dart';
|
||||
import 'package:revanced_manager/ui/widgets/homeView/latest_commit_card.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_chip.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
@ -18,12 +17,11 @@ class HomeView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<HomeViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
onModelReady: (model) => model.initialize(context),
|
||||
fireOnViewModelReadyOnce: true,
|
||||
onViewModelReady: (model) => model.initialize(context),
|
||||
viewModelBuilder: () => locator<HomeViewModel>(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
body: RefreshIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
onRefresh: () => model.forceRefresh(context),
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
@ -34,7 +32,7 @@ class HomeView extends StatelessWidget {
|
||||
child: Text(
|
||||
'',
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -48,40 +46,67 @@ class HomeView extends StatelessWidget {
|
||||
'homeView.updatesSubtitle',
|
||||
child: Text(
|
||||
'',
|
||||
style: Theme.of(context).textTheme.headline6!,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
LatestCommitCard(
|
||||
onPressed: () =>
|
||||
onPressedManager: () =>
|
||||
model.showUpdateConfirmationDialog(context),
|
||||
onPressedPatches: () => model.forceRefresh(context),
|
||||
),
|
||||
const SizedBox(height: 23),
|
||||
I18nText(
|
||||
'homeView.patchedSubtitle',
|
||||
child: Text(
|
||||
'',
|
||||
style: Theme.of(context).textTheme.headline6!,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
CustomChip(
|
||||
ActionChip(
|
||||
avatar: const Icon(Icons.grid_view),
|
||||
label: I18nText('homeView.installed'),
|
||||
isSelected: !model.showUpdatableApps,
|
||||
onSelected: (value) {
|
||||
side: BorderSide(
|
||||
color: model.showUpdatableApps
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
width: model.showUpdatableApps ? 1 : 1,
|
||||
),
|
||||
backgroundColor: model.showUpdatableApps
|
||||
? Theme.of(context).colorScheme.background
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
onPressed: () {
|
||||
model.toggleUpdatableApps(false);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
CustomChip(
|
||||
ActionChip(
|
||||
avatar: const Icon(Icons.update),
|
||||
label: I18nText('homeView.updatesAvailable'),
|
||||
isSelected: model.showUpdatableApps,
|
||||
onSelected: (value) {
|
||||
side: BorderSide(
|
||||
color: !model.showUpdatableApps
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
width: !model.showUpdatableApps ? 1 : 1,
|
||||
),
|
||||
backgroundColor: !model.showUpdatableApps
|
||||
? Theme.of(context).colorScheme.background
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
onPressed: () {
|
||||
model.toggleUpdatableApps(true);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
|
@ -1,26 +1,29 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_installer/app_installer.dart';
|
||||
import 'package:cross_connectivity/cross_connectivity.dart';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
import 'package:revanced_manager/services/revanced_api.dart';
|
||||
import 'package:revanced_manager/services/toast.dart';
|
||||
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/homeView/update_confirmation_dialog.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
@lazySingleton
|
||||
class HomeViewModel extends BaseViewModel {
|
||||
@ -28,29 +31,52 @@ class HomeViewModel extends BaseViewModel {
|
||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final GithubAPI _githubAPI = locator<GithubAPI>();
|
||||
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
|
||||
final Toast _toast = locator<Toast>();
|
||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
DateTime? _lastUpdate;
|
||||
bool showUpdatableApps = false;
|
||||
List<PatchedApplication> patchedInstalledApps = [];
|
||||
List<PatchedApplication> patchedUpdatableApps = [];
|
||||
String? _latestManagerVersion = '';
|
||||
|
||||
Future<void> initialize(BuildContext context) async {
|
||||
_latestManagerVersion = await _managerAPI.getLatestManagerVersion();
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
const InitializationSettings(
|
||||
android: AndroidInitializationSettings('ic_notification'),
|
||||
),
|
||||
onSelectNotification: (p) =>
|
||||
DeviceApps.openApp('app.revanced.manager.flutter'),
|
||||
onDidReceiveNotificationResponse: (response) async {
|
||||
if (response.id == 0) {
|
||||
_toast.showBottom('homeView.installingMessage');
|
||||
final File? managerApk = await _managerAPI.downloadManager();
|
||||
if (managerApk != null) {
|
||||
await AppInstaller.installApk(managerApk.path);
|
||||
} else {
|
||||
_toast.showBottom('homeView.errorDownloadMessage');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermission();
|
||||
bool isConnected = await Connectivity().checkConnection();
|
||||
final bool isConnected = await Connectivity().checkConnection();
|
||||
if (!isConnected) {
|
||||
_toast.showBottom('homeView.noConnection');
|
||||
}
|
||||
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
|
||||
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
|
||||
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
|
||||
_toast.showBottom('homeView.installingMessage');
|
||||
final File? managerApk = await _managerAPI.downloadManager();
|
||||
if (managerApk != null) {
|
||||
await AppInstaller.installApk(managerApk.path);
|
||||
} else {
|
||||
_toast.showBottom('homeView.errorDownloadMessage');
|
||||
}
|
||||
}
|
||||
_getPatchedApps();
|
||||
_managerAPI.reAssessSavedApps().then((_) => _getPatchedApps());
|
||||
}
|
||||
@ -67,7 +93,7 @@ class HomeViewModel extends BaseViewModel {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void navigateToPatcher(PatchedApplication app) async {
|
||||
Future<void> navigateToPatcher(PatchedApplication app) async {
|
||||
locator<PatcherViewModel>().selectedApp = app;
|
||||
locator<PatcherViewModel>().selectedPatches =
|
||||
await _patcherAPI.getAppliedPatches(app.appliedPatches);
|
||||
@ -76,10 +102,7 @@ class HomeViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
void _getPatchedApps() {
|
||||
patchedInstalledApps = _managerAPI
|
||||
.getPatchedApps()
|
||||
.where((app) => app.hasUpdates == false)
|
||||
.toList();
|
||||
patchedInstalledApps = _managerAPI.getPatchedApps().toList();
|
||||
patchedUpdatableApps = _managerAPI
|
||||
.getPatchedApps()
|
||||
.where((app) => app.hasUpdates == true)
|
||||
@ -88,59 +111,158 @@ class HomeViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
Future<bool> hasManagerUpdates() async {
|
||||
String? latestVersion = await _managerAPI.getLatestManagerVersion();
|
||||
String currentVersion = await _managerAPI.getCurrentManagerVersion();
|
||||
_latestManagerVersion = await _managerAPI.getLatestManagerVersion();
|
||||
|
||||
// add v to current version
|
||||
if (!currentVersion.startsWith('v')) {
|
||||
currentVersion = 'v$currentVersion';
|
||||
}
|
||||
|
||||
if (_latestManagerVersion != currentVersion) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> hasPatchesUpdates() async {
|
||||
final String? latestVersion = await _managerAPI.getLatestPatchesVersion();
|
||||
final String? currentVersion = await _managerAPI.getCurrentPatchesVersion();
|
||||
if (latestVersion != null) {
|
||||
try {
|
||||
int latestVersionInt =
|
||||
final int latestVersionInt =
|
||||
int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), ''));
|
||||
int currentVersionInt =
|
||||
int.parse(currentVersion.replaceAll(RegExp('[^0-9]'), ''));
|
||||
final int currentVersionInt =
|
||||
int.parse(currentVersion!.replaceAll(RegExp('[^0-9]'), ''));
|
||||
return latestVersionInt > currentVersionInt;
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<File?> downloadManager() async {
|
||||
try {
|
||||
final response = await _revancedAPI.downloadManager();
|
||||
final bytes = await response!.readAsBytes();
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final tempFile = File('${tempDir.path}/revanced-manager.apk');
|
||||
await tempFile.writeAsBytes(bytes);
|
||||
return tempFile;
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateManager(BuildContext context) async {
|
||||
try {
|
||||
_toast.showBottom('homeView.downloadingMessage');
|
||||
File? managerApk = await _managerAPI.downloadManager();
|
||||
if (managerApk != null) {
|
||||
await flutterLocalNotificationsPlugin.zonedSchedule(
|
||||
0,
|
||||
FlutterI18n.translate(
|
||||
context,
|
||||
'homeView.notificationTitle',
|
||||
),
|
||||
FlutterI18n.translate(
|
||||
context,
|
||||
'homeView.notificationText',
|
||||
),
|
||||
tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
|
||||
const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'revanced_manager_channel',
|
||||
'ReVanced Manager Channel',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => SimpleDialog(
|
||||
contentPadding: const EdgeInsets.all(16.0),
|
||||
title: I18nText(
|
||||
'homeView.downloadingMessage',
|
||||
child: Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
androidAllowWhileIdle: true,
|
||||
uiLocalNotificationDateInterpretation:
|
||||
UILocalNotificationDateInterpretation.absoluteTime,
|
||||
);
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.new_releases_outlined,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
'$_latestManagerVersion',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
StreamBuilder<double>(
|
||||
initialData: 0.0,
|
||||
stream: _revancedAPI.managerUpdateProgress.stream,
|
||||
builder: (context, snapshot) {
|
||||
return LinearProgressIndicator(
|
||||
value: snapshot.data! * 0.01,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: CustomMaterialButton(
|
||||
label: I18nText('cancelButton'),
|
||||
onPressed: () {
|
||||
_revancedAPI.disposeManagerUpdateProgress();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
final File? managerApk = await downloadManager();
|
||||
if (managerApk != null) {
|
||||
// await flutterLocalNotificationsPlugin.zonedSchedule(
|
||||
// 0,
|
||||
// FlutterI18n.translate(
|
||||
// context,
|
||||
// 'homeView.notificationTitle',
|
||||
// ),
|
||||
// FlutterI18n.translate(
|
||||
// context,
|
||||
// 'homeView.notificationText',
|
||||
// ),
|
||||
// tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
|
||||
// const NotificationDetails(
|
||||
// android: AndroidNotificationDetails(
|
||||
// 'revanced_manager_channel',
|
||||
// 'ReVanced Manager Channel',
|
||||
// importance: Importance.max,
|
||||
// priority: Priority.high,
|
||||
// ticker: 'ticker',
|
||||
// ),
|
||||
// ),
|
||||
// androidAllowWhileIdle: true,
|
||||
// uiLocalNotificationDateInterpretation:
|
||||
// UILocalNotificationDateInterpretation.absoluteTime,
|
||||
// );
|
||||
_toast.showBottom('homeView.installingMessage');
|
||||
await AppInstaller.installApk(managerApk.path);
|
||||
} else {
|
||||
_toast.showBottom('homeView.errorDownloadMessage');
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
_toast.showBottom('homeView.errorInstallMessage');
|
||||
}
|
||||
}
|
||||
@ -149,7 +271,9 @@ class HomeViewModel extends BaseViewModel {
|
||||
_toast.showBottom('homeView.updatesDisabled');
|
||||
}
|
||||
|
||||
Future<void> showUpdateConfirmationDialog(BuildContext parentContext) {
|
||||
Future<void> showUpdateConfirmationDialog(
|
||||
BuildContext parentContext,
|
||||
) {
|
||||
return showModalBottomSheet(
|
||||
context: parentContext,
|
||||
isScrollControlled: true,
|
||||
@ -178,6 +302,7 @@ class HomeViewModel extends BaseViewModel {
|
||||
_lastUpdate!.difference(DateTime.now()).inSeconds > 2) {
|
||||
_managerAPI.clearAllData();
|
||||
}
|
||||
_toast.showBottom('homeView.refreshSuccess');
|
||||
initialize(context);
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
@ -15,11 +15,12 @@ class InstallerView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<InstallerViewModel>.reactive(
|
||||
onModelReady: (model) => model.initialize(context),
|
||||
onViewModelReady: (model) => model.initialize(context),
|
||||
viewModelBuilder: () => InstallerViewModel(),
|
||||
builder: (context, model, child) => WillPopScope(
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
controller: model.scrollController,
|
||||
@ -28,7 +29,7 @@ class InstallerView extends StatelessWidget {
|
||||
title: Text(
|
||||
model.headerLogs,
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
onBackButtonPressed: () => model.onWillPop(context),
|
||||
@ -48,15 +49,15 @@ class InstallerView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
1: I18nText(
|
||||
'installerView.exportApkMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
1: I18nText(
|
||||
'installerView.exportApkMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
2: I18nText(
|
||||
'installerView.shareLogMenuOption',
|
||||
child: const Text(
|
||||
@ -71,9 +72,9 @@ class InstallerView extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size(double.infinity, 1.0),
|
||||
child:
|
||||
GradientProgressIndicator(progress: model.progress!)),
|
||||
preferredSize: const Size(double.infinity, 1.0),
|
||||
child: GradientProgressIndicator(progress: model.progress),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
@ -153,6 +154,12 @@ class InstallerView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,5 +1,6 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_background/flutter_background.dart';
|
||||
@ -14,7 +15,6 @@ import 'package:revanced_manager/services/root_api.dart';
|
||||
import 'package:revanced_manager/services/toast.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
@ -51,16 +51,15 @@ class InstallerViewModel extends BaseViewModel {
|
||||
context,
|
||||
'installerView.notificationText',
|
||||
),
|
||||
notificationImportance: AndroidNotificationImportance.Default,
|
||||
notificationIcon: const AndroidResource(
|
||||
name: 'ic_notification',
|
||||
defType: 'drawable',
|
||||
),
|
||||
),
|
||||
).then((value) => FlutterBackground.enableBackgroundExecution());
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
// ignore
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
} // ignore
|
||||
}
|
||||
}
|
||||
await Wakelock.enable();
|
||||
@ -73,10 +72,10 @@ class InstallerViewModel extends BaseViewModel {
|
||||
switch (call.method) {
|
||||
case 'update':
|
||||
if (call.arguments != null) {
|
||||
Map<dynamic, dynamic> arguments = call.arguments;
|
||||
double progress = arguments['progress'];
|
||||
String header = arguments['header'];
|
||||
String log = arguments['log'];
|
||||
final Map<dynamic, dynamic> arguments = call.arguments;
|
||||
final double progress = arguments['progress'];
|
||||
final String header = arguments['header'];
|
||||
final String log = arguments['log'];
|
||||
update(progress, header, log);
|
||||
}
|
||||
break;
|
||||
@ -133,39 +132,44 @@ class InstallerViewModel extends BaseViewModel {
|
||||
_app.apkFilePath,
|
||||
_patches,
|
||||
);
|
||||
} on Exception catch (e, s) {
|
||||
} on Exception catch (e) {
|
||||
update(
|
||||
-100.0,
|
||||
'Aborting...',
|
||||
'An error occurred! Aborting\nError:\n$e',
|
||||
'Aborted...',
|
||||
'An error occurred! Aborted\nError:\n$e',
|
||||
);
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
throw await Sentry.captureException(e, stackTrace: s);
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
update(-100.0, 'Aborting...', 'No app or patches selected! Aborting');
|
||||
update(-100.0, 'Aborted...', 'No app or patches selected! Aborted');
|
||||
}
|
||||
if (FlutterBackground.isBackgroundExecutionEnabled) {
|
||||
try {
|
||||
FlutterBackground.disableBackgroundExecution();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
// ignore
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
} // ignore
|
||||
}
|
||||
}
|
||||
await Wakelock.disable();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void installResult(BuildContext context, bool installAsRoot) async {
|
||||
Future<void> installResult(BuildContext context, bool installAsRoot) async {
|
||||
try {
|
||||
_app.isRooted = installAsRoot;
|
||||
bool hasMicroG = _patches.any((p) => p.name.endsWith('microg-support'));
|
||||
bool rootMicroG = installAsRoot && hasMicroG;
|
||||
bool rootFromStorage = installAsRoot && _app.isFromStorage;
|
||||
bool ytWithoutRootMicroG =
|
||||
final bool hasMicroG =
|
||||
_patches.any((p) => p.name.endsWith('microg-support'));
|
||||
final bool rootMicroG = installAsRoot && hasMicroG;
|
||||
final bool rootFromStorage = installAsRoot && _app.isFromStorage;
|
||||
final bool ytWithoutRootMicroG =
|
||||
!installAsRoot && !hasMicroG && _app.packageName.contains('youtube');
|
||||
if (rootMicroG || rootFromStorage || ytWithoutRootMicroG) {
|
||||
return showDialog(
|
||||
@ -212,24 +216,30 @@ class InstallerViewModel extends BaseViewModel {
|
||||
await _managerAPI.savePatchedApp(_app);
|
||||
}
|
||||
}
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exportResult() {
|
||||
try {
|
||||
_patcherAPI.exportPatchedFile(_app.name, _app.version);
|
||||
} on Exception catch (e, s) {
|
||||
Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shareResult() {
|
||||
try {
|
||||
_patcherAPI.sharePatchedFile(_app.name, _app.version);
|
||||
} on Exception catch (e, s) {
|
||||
Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,8 +253,10 @@ class InstallerViewModel extends BaseViewModel {
|
||||
locator<PatcherViewModel>().selectedApp = null;
|
||||
locator<PatcherViewModel>().selectedPatches.clear();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
} on Exception catch (e, s) {
|
||||
await Sentry.captureException(e, stackTrace: s);
|
||||
} on Exception catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ class NavigationView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<NavigationViewModel>.reactive(
|
||||
onModelReady: (model) => model.initialize(context),
|
||||
onViewModelReady: (model) => model.initialize(context),
|
||||
viewModelBuilder: () => locator<NavigationViewModel>(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
body: PageTransitionSwitcher(
|
||||
@ -42,7 +42,6 @@ class NavigationView extends StatelessWidget {
|
||||
context,
|
||||
'navigationView.dashboardTab',
|
||||
),
|
||||
tooltip: '',
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: model.isIndexSelected(1)
|
||||
@ -52,7 +51,6 @@ class NavigationView extends StatelessWidget {
|
||||
context,
|
||||
'navigationView.patcherTab',
|
||||
),
|
||||
tooltip: '',
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: model.isIndexSelected(2)
|
||||
@ -62,7 +60,6 @@ class NavigationView extends StatelessWidget {
|
||||
context,
|
||||
'navigationView.settingsTab',
|
||||
),
|
||||
tooltip: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -15,10 +15,12 @@ import 'package:stacked/stacked.dart';
|
||||
|
||||
@lazySingleton
|
||||
class NavigationViewModel extends IndexTrackingViewModel {
|
||||
void initialize(BuildContext context) async {
|
||||
Future<void> initialize(BuildContext context) async {
|
||||
locator<Toast>().initialize(context);
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
if (prefs.getBool('permissionsRequested') == null) {
|
||||
await Permission.storage.request();
|
||||
await Permission.manageExternalStorage.request();
|
||||
await prefs.setBool('permissionsRequested', true);
|
||||
RootAPI().hasRootPermissions().then(
|
||||
(value) => Permission.requestInstallPackages.request().then(
|
||||
@ -27,7 +29,7 @@ class NavigationViewModel extends IndexTrackingViewModel {
|
||||
);
|
||||
}
|
||||
if (prefs.getBool('useDarkTheme') == null) {
|
||||
bool isDark =
|
||||
final bool isDark =
|
||||
MediaQuery.of(context).platformBrightness != Brightness.light;
|
||||
await prefs.setBool('useDarkTheme', isDark);
|
||||
await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0);
|
||||
|
@ -34,7 +34,7 @@ class PatcherView extends StatelessWidget {
|
||||
child: Text(
|
||||
'',
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
color: Theme.of(context).textTheme.titleLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
|