Compare commits
1 Commits
v1.26.0-de
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3da2b49dad |
35
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a bug or an issue.
|
||||
title: 'bug: '
|
||||
labels: ['Bug report']
|
||||
title: "bug: "
|
||||
labels: ["Bug report"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -80,21 +80,38 @@ body:
|
||||
- Describe your bug in detail
|
||||
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
||||
- Add images and videos if possible
|
||||
- List used patches, downloader and settings if applicable
|
||||
- List used patches if applicable
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Version of ReVanced Manager and version & name of app you are patching
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Installation method
|
||||
options:
|
||||
- Regular
|
||||
- Mount
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: ReVanced Manager logs
|
||||
description: Export logs from the ReVanced Manager settings.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Patch logs
|
||||
description: Patch logs can be exported by clicking on the "Logs" button in the "Patcher" screen, when patching finishes.
|
||||
description: Export logs from the "Patcher" screen.
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Debug logs
|
||||
description: Debug logs can be exported by clicking on "Export debug logs" in "Settings" > "Advanced".
|
||||
validations:
|
||||
required: true
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your bug report will be closed if you don't follow the checklist below.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -2,4 +2,4 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🗨 Discussions
|
||||
url: https://github.com/revanced/revanced-suggestions/discussions
|
||||
about: Have something unspecific to ReVanced Manager in mind? Search for or start a new discussion!
|
||||
about: Have something unspecific to ReVanced Manager in mind? Search for or start a new discussion!
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed request for a new feature.
|
||||
title: 'feat: '
|
||||
labels: ['Feature request']
|
||||
title: "feat: "
|
||||
labels: ["Feature request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -84,7 +84,7 @@ body:
|
||||
label: Motivation
|
||||
description: |
|
||||
A strong motivation is necessary for a feature request to be considered.
|
||||
|
||||
|
||||
- Why should this feature be implemented?
|
||||
- What is the explicit use case?
|
||||
- What are the benefits?
|
||||
@@ -97,11 +97,9 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your feature request will be closed if you don't follow the checklist below.
|
||||
options:
|
||||
- label: I have checked all open and closed feature requests and this is not a duplicate
|
||||
- label: I have checked all open and closed feature requests and this is not a duplicate.
|
||||
required: true
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
- label: All requested information has been provided properly.
|
||||
required: true
|
||||
- label: The feature request is only related to ReVanced Manager.
|
||||
required: true
|
||||
|
||||
78
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
labels: []
|
||||
directory: /
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
gh-actions:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
- package-ecosystem: npm
|
||||
labels: []
|
||||
directory: /
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
npm:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
# ReVanced Manager Flutter
|
||||
- package-ecosystem: pub
|
||||
labels: []
|
||||
directory: /
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
pubspec:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
- package-ecosystem: gradle
|
||||
labels: []
|
||||
directory: /android
|
||||
target-branch: dev
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
gradle:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
# ReVanced Manager Compose
|
||||
- package-ecosystem: gradle
|
||||
labels: [ "ReVanced Manager Compose" ]
|
||||
directory: /
|
||||
target-branch: compose-dev
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
gradle-compose:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
59
.github/workflows/build_pull_request.yml
vendored
@@ -2,30 +2,69 @@ name: Build pull request
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
inputs:
|
||||
pr-number:
|
||||
description: PR number
|
||||
required: true
|
||||
app-flavor:
|
||||
description: App flavor
|
||||
default: release
|
||||
type: choice
|
||||
options:
|
||||
- release
|
||||
- debug
|
||||
- profile
|
||||
|
||||
jobs:
|
||||
release:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/pull/${{ inputs.pr-number }}/merge
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: stable
|
||||
flutter-version: 3.29.x
|
||||
cache: true
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
uses: burrunan/gradle-cache-action@v3
|
||||
with:
|
||||
build-root-directory: ${{ github.workspace }}/android
|
||||
|
||||
- name: Get dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Generate translations
|
||||
run: dart run slang
|
||||
|
||||
- name: Generate code files
|
||||
run: dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Build
|
||||
id: flutter-build
|
||||
run: flutter build apk --${{ inputs.app-flavor }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew assembleRelease --no-daemon
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.flutter-build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: revanced-manager
|
||||
name: revanced-manager-(${{ env.COMMIT_HASH }}
|
||||
path: |
|
||||
app/build/outputs/apk/release/revanced-manager*.apk
|
||||
app/build/outputs/apk/release/revanced-manager*.apk.asc
|
||||
build/app/outputs/flutter-apk/app-*.apk
|
||||
|
||||
5
.github/workflows/open_pull_request.yml
vendored
@@ -12,6 +12,8 @@ env:
|
||||
jobs:
|
||||
pull-request:
|
||||
name: Open pull request
|
||||
permissions:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -22,5 +24,6 @@ jobs:
|
||||
with:
|
||||
destination_branch: 'main'
|
||||
pr_title: 'chore: ${{ env.MESSAGE }}'
|
||||
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
|
||||
pr_body: |
|
||||
This pull request will ${{ env.MESSAGE }}.
|
||||
pr_draft: true
|
||||
|
||||
43
.github/workflows/pull_strings.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Pull strings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pull:
|
||||
name: Pull strings
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: dev
|
||||
clean: true
|
||||
|
||||
- name: Pull strings
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
skip_ref_checkout: true
|
||||
localization_branch_name: feat/translations
|
||||
create_pull_request: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
- name: Open pull request
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
source_branch: feat/translations
|
||||
destination_branch: dev
|
||||
pr_title: "chore: Sync translations"
|
||||
pr_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||
26
.github/workflows/push_strings.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Push strings
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- app/src/main/res/values/strings.xml
|
||||
|
||||
jobs:
|
||||
push:
|
||||
name: Push strings
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Push strings
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: true
|
||||
env:
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
63
.github/workflows/release.yml
vendored
@@ -12,7 +12,6 @@ jobs:
|
||||
name: Release
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,55 +25,51 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v3
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
fingerprint: ${{ vars.GPG_FINGERPRINT }}
|
||||
channel: stable
|
||||
flutter-version: 3.29.x
|
||||
cache: true
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v3
|
||||
with:
|
||||
build-root-directory: ${{ github.workspace }}/android
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Get dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Generate translations
|
||||
run: dart run slang
|
||||
|
||||
- name: Generate code files
|
||||
run: dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Setup keystore
|
||||
run: |
|
||||
echo "${{ secrets.KEYSTORE }}" | base64 --decode > "app/keystore.jks"
|
||||
echo "${{ secrets.KEYSTORE }}" | base64 --decode > "android/app/keystore.jks"
|
||||
|
||||
- name: Release API
|
||||
run: npx multi-semantic-release --tag-format 'api@${version}' --ignore-packages app
|
||||
- name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v4
|
||||
id: semantic
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Release
|
||||
id: release
|
||||
run: |
|
||||
echo "NEW_TAG=$(npx multi-semantic-release --tag-format 'v${version}' --ignore-packages api | tee | grep 'Created tag ' | sed -E 's/.*Created tag ([^ ]+).*/\1/')" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
KEYSTORE_ENTRY_ALIAS: ${{ secrets.KEYSTORE_ENTRY_ALIAS }}
|
||||
KEYSTORE_ENTRY_PASSWORD: ${{ secrets.KEYSTORE_ENTRY_PASSWORD }}
|
||||
|
||||
|
||||
- name: Attest
|
||||
if: steps.release.outputs.NEW_TAG != ''
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: 'ReVanced Manager ${{ steps.release.outputs.NEW_TAG }}'
|
||||
subject-path: app/build/outputs/apk/release/revanced-manager*.apk
|
||||
subject-path: build/app/outputs/apk/release/revanced-manager-*.apk
|
||||
|
||||
81
.github/workflows/sync_crowdin.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Sync Crowdin
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 00 12 * * 1
|
||||
push:
|
||||
branches: dev
|
||||
paths:
|
||||
- assets/i18n/*.json
|
||||
- assets/i18n/*.dart
|
||||
- .github/workflows/sync_crowdin.yml
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev
|
||||
clean: true
|
||||
|
||||
- name: Setup Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: stable
|
||||
flutter-version: 3.29.x
|
||||
cache: true
|
||||
|
||||
- name: Sync translations from Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
localization_branch_name: feat/translations
|
||||
skip_ref_checkout: true
|
||||
create_pull_request: true
|
||||
pull_request_title: "chore: Sync translations"
|
||||
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||
pull_request_base_branch_name: "dev"
|
||||
commit_message: "chore: Sync translations"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
- name: Validation of synced translations
|
||||
run: |
|
||||
dart pub get
|
||||
dart run slang validate
|
||||
|
||||
- name: Normalization of Translation Strings
|
||||
run: |
|
||||
sudo chmod 766 assets/i18n/*.i18n.json
|
||||
|
||||
dart run slang analyze
|
||||
dart run slang clean
|
||||
dart run slang normalize
|
||||
|
||||
dart run slang
|
||||
|
||||
cd assets/i18n
|
||||
dart nuke.dart >> $GITHUB_STEP_SUMMARY
|
||||
cd ../..
|
||||
|
||||
flutter analyze lib/gen/strings.g.dart --no-fatal-infos --no-fatal-warnings
|
||||
|
||||
- name: Commit translations
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
sudo chown -R $USER:$USER .git
|
||||
git commit -m "chore: Remove empty values from JSON" assets/i18n/*.i18n.json
|
||||
git push origin HEAD:feat/translations
|
||||
2
.github/workflows/update_documentation.yml
vendored
@@ -16,4 +16,4 @@ jobs:
|
||||
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
|
||||
repository: revanced/revanced-documentation
|
||||
event-type: update-documentation
|
||||
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'
|
||||
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'
|
||||
|
||||
174
.gitignore
vendored
@@ -1,132 +1,64 @@
|
||||
### Java template
|
||||
# Compiled class file
|
||||
# Miscellaneous
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.lock
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.buildlog/
|
||||
.history
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
# packages file containing multi-root paths
|
||||
.packages.generated
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Gradle template
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
# Potentially copyrighted test APK
|
||||
*.apk
|
||||
|
||||
# Ignore vscode config
|
||||
.vscode/
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# Ignore IDEA files
|
||||
.idea/
|
||||
|
||||
.kotlin/
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
**/generated_plugin_registrant.dart
|
||||
.packages
|
||||
.pub-preload-cache/
|
||||
.pub/
|
||||
build/
|
||||
flutter_*.png
|
||||
linked_*.ds
|
||||
unlinked.ds
|
||||
unlinked_spec.ds
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
.gradle/
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
**/android/key.properties
|
||||
*.jks
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# Symbols
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Generated files
|
||||
android/app/.cxx
|
||||
**/*.g.dart
|
||||
**/*.locator.dart
|
||||
**/*.router.dart
|
||||
|
||||
# Project specific
|
||||
node_modules/
|
||||
.vscode/
|
||||
|
||||
local.properties
|
||||
|
||||
.cxx
|
||||
30
.metadata
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "2663184aa79047d0a33a14a3b607954f8fdd8730"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
|
||||
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
|
||||
- platform: android
|
||||
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
|
||||
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -8,24 +8,33 @@
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"@semantic-release/commit-analyzer", {
|
||||
"releaseRules": [
|
||||
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"gradle-semantic-release-plugin",
|
||||
"@semantic-release/release-notes-generator",
|
||||
[
|
||||
"semantic-release-pub",
|
||||
{
|
||||
"publishPub": false,
|
||||
"updateBuildNumber": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
"prepareCmd": "flutter build apk"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"CHANGELOG.md",
|
||||
"gradle.properties"
|
||||
],
|
||||
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
"pubspec.yaml"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
@@ -33,8 +42,11 @@
|
||||
{
|
||||
"assets": [
|
||||
{
|
||||
"path": "build/outputs/apk/release/revanced-manager*.apk?(.asc)"
|
||||
}
|
||||
"path": "build/app/outputs/apk/release/revanced-manager*.apk"
|
||||
},
|
||||
],
|
||||
"commits": [
|
||||
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
],
|
||||
"successComment": false
|
||||
}
|
||||
@@ -96,7 +96,7 @@ If you encounter a bug while using ReVanced Manager, open an issue using the
|
||||
|
||||
## 🤚 I want to contribute but don't know how to code
|
||||
|
||||
Even if you don't know how to code, you can still contribute by
|
||||
Even if you don't know how to code, you can still contribute by
|
||||
translating ReVanced Manager on [Crowdin](https://translate.revanced.app/).
|
||||
|
||||
❤️ Thank you for considering contributing to ReVanced Manager,
|
||||
|
||||
@@ -73,9 +73,10 @@ ReVanced Manager is an application that uses [ReVanced Patcher](https://github.c
|
||||
|
||||
Some of the features ReVanced Manager provides are:
|
||||
|
||||
- ⬇️ **Download**: Automatically download apps using the ReVanced Manager downloader system
|
||||
- 💉 **Patch**: Select and apply patches to any Android app
|
||||
- 🛠️ **Customize**: Manage patches, apps, signing, themes, updates, and many more settings
|
||||
- 💉 **Patch apps**: Apply any patch of your choice to Android apps
|
||||
- 📱 **Portable**: ReVanced Patcher that fits in your pocket
|
||||
- 🤗 **Simple UI**: Quickly understand the ins and outs of ReVanced Manager
|
||||
- 🛠️ **Customization**: Configurable API, custom sources, language, signing keystore, theme and more
|
||||
|
||||
## 🔽 Download
|
||||
|
||||
|
||||
77
SECURITY.md
Normal file
@@ -0,0 +1,77 @@
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source
|
||||
width="256px"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
>
|
||||
<img
|
||||
width="256px"
|
||||
src="assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
>
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://revanced.app/">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://github.com/ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="http://revanced.app/discord">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/revancedapp">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://t.me/app_revanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://x.com/revancedapp">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/@ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
Continuing the legacy of Vanced
|
||||
</p>
|
||||
|
||||
# 🔒 Security Policy
|
||||
|
||||
This document describes how to report security vulnerabilities for ReVanced Manager.
|
||||
|
||||
## 🚨 Reporting a Vulnerability
|
||||
|
||||
Please open an issue in our [advisory tracker](https://github.com/ReVanced/revanced-manager/security/advisories/new) or reach out privately to us on [Discord](https://discord.gg/revanced).
|
||||
|
||||
If a vulnerability is confirmed and accepted, you can join our [Discord](https://discord.gg/revanced) server to receive a special contributor role.
|
||||
|
||||
### ⏳ Supported Versions
|
||||
|
||||
| Version | Branch | Supported |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------ |
|
||||
|  | main | :white_check_mark: |
|
||||
|  | dev | :white_check_mark: |
|
||||
|  | compose-dev | :white_check_mark: |
|
||||
156
analysis_options.yaml
Normal file
@@ -0,0 +1,156 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/app/app.locator.dart
|
||||
- lib/app/app.router.dart
|
||||
- lib/models/patch.g.dart
|
||||
- lib/models/patched_application.g.dart
|
||||
- lib/gen/
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- always_declare_return_types
|
||||
- require_trailing_commas
|
||||
- always_put_control_body_on_new_line
|
||||
- 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_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
|
||||
- collection_methods_unrelated_type
|
||||
- leading_newlines_in_multiline_strings
|
||||
- library_prefixes
|
||||
- library_private_types_in_public_api
|
||||
- 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_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_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
|
||||
- provide_deprecation_message
|
||||
- recursive_getters
|
||||
- sized_box_for_whitespace
|
||||
- slash_for_doc_comments
|
||||
- sort_child_properties_last
|
||||
- sort_constructors_first
|
||||
- sort_pub_dependencies
|
||||
- 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
|
||||
- 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
|
||||
13
android/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/to/reference-keystore
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
104
android/app/build.gradle.kts
Normal file
@@ -0,0 +1,104 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.manager.flutter"
|
||||
compileSdk = 35
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "app.revanced.manager.flutter"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
|
||||
resValue("string", "app_name", "ReVanced Manager")
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
outputs.all {
|
||||
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
||||
|
||||
outputFileName = "revanced-manager-$versionName.apk"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
configureEach {
|
||||
isShrinkResources = false
|
||||
isMinifyEnabled = false
|
||||
|
||||
signingConfig = signingConfigs["debug"]
|
||||
|
||||
ndk.abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64")
|
||||
}
|
||||
|
||||
release {
|
||||
isShrinkResources = true
|
||||
isMinifyEnabled = true
|
||||
|
||||
val keystoreFile = file("keystore.jks")
|
||||
if (keystoreFile.exists()) {
|
||||
signingConfig = signingConfigs.create("release") {
|
||||
storeFile = keystoreFile
|
||||
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||||
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
||||
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
||||
}
|
||||
|
||||
resValue("string", "app_name", "ReVanced Manager")
|
||||
} else {
|
||||
applicationIdSuffix = ".development"
|
||||
resValue("string", "app_name", "ReVanced Manager (Development)")
|
||||
signingConfig = signingConfigs["debug"]
|
||||
}
|
||||
}
|
||||
|
||||
debug {
|
||||
applicationIdSuffix = ".debug"
|
||||
resValue("string", "app_name", "ReVanced Manager (Debug)")
|
||||
}
|
||||
|
||||
named("profile") {
|
||||
initWith(getByName("debug"))
|
||||
applicationIdSuffix = ".profile"
|
||||
resValue("string", "app_name", "ReVanced Manager (Profile)")
|
||||
}
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
excludes.add("/prebuilt/**")
|
||||
}
|
||||
|
||||
resources {
|
||||
excludes.add("/prebuilt/**")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.desugar.jdk.libs) // https://pub.dev/packages/flutter_local_notifications#gradle-setup
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.revanced.library)
|
||||
}
|
||||
17
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
-dontobfuscate
|
||||
|
||||
-keep class app.revanced.** { *; }
|
||||
-keep class com.android.tools.smali.** { *; }
|
||||
-keep class kotlin.** { *; }
|
||||
-keep class com.google.auto.value.** { *; }
|
||||
-keep class com.android.apksig.internal.** { *; }
|
||||
-keepnames class com.google.common.collect.**
|
||||
-keepnames class org.xmlpull.** { *; }
|
||||
|
||||
-dontwarn com.google.auto.value.**
|
||||
-dontwarn com.google.j2objc.annotations.*
|
||||
-dontwarn java.awt.**
|
||||
-dontwarn javax.**
|
||||
|
||||
# Required for Share Plus, ref: ReVanced/revanced-manager#2474
|
||||
-keep interface android.content.res.XmlResourceParser { *; }
|
||||
7
android/app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
88
android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<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"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:largeHeap="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name="de.julianassmann.flutter_background.IsolateHolderService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="shortService" />
|
||||
<activity
|
||||
android:name=".ExportSettingsActivity"
|
||||
android:exported="true">
|
||||
</activity>
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".utils.packageInstaller.InstallerReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="APP_INSTALL_ACTION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".utils.packageInstaller.UninstallerReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="APP_UNINSTALL_ACTION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,81 @@
|
||||
package app.revanced.manager.flutter
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.util.Base64
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import java.security.MessageDigest
|
||||
|
||||
class ExportSettingsActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (getFingerprint(callingPackage!!) == getFingerprint(packageName)) {
|
||||
// Create JSON Object
|
||||
val json = JSONObject()
|
||||
|
||||
// Default Data
|
||||
json.put("keystorePassword", "s3cur3p@ssw0rd")
|
||||
|
||||
// Load Shared Preferences
|
||||
val sharedPreferences = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
||||
val allEntries: Map<String, *> = sharedPreferences.getAll()
|
||||
for ((key, value) in allEntries.entries) {
|
||||
json.put(key.replace("flutter.", ""), value)
|
||||
}
|
||||
|
||||
// Load keystore
|
||||
val keystoreFile = File(getExternalFilesDir(null), "/revanced-manager.keystore")
|
||||
if (keystoreFile.exists()) {
|
||||
val keystoreBytes = keystoreFile.readBytes()
|
||||
val keystoreBase64 = Base64.encodeToString(keystoreBytes, Base64.DEFAULT)
|
||||
json.put("keystore", keystoreBase64)
|
||||
}
|
||||
|
||||
// Load saved patches
|
||||
val storedPatchesFile = File(filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json")
|
||||
if (storedPatchesFile.exists()) {
|
||||
val patchesBytes = storedPatchesFile.readBytes()
|
||||
val patches = String(patchesBytes, Charsets.UTF_8)
|
||||
json.put("patches", JSONObject(patches))
|
||||
}
|
||||
|
||||
// Send data back
|
||||
val resultIntent = Intent()
|
||||
resultIntent.putExtra("data", json.toString())
|
||||
setResult(Activity.RESULT_OK, resultIntent)
|
||||
finish()
|
||||
} else {
|
||||
val resultIntent = Intent()
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun getFingerprint(packageName: String): String {
|
||||
// Get the signature of the app that matches the package name
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
|
||||
val signature = packageInfo.signatures!![0]
|
||||
|
||||
// Get the raw certificate data
|
||||
val rawCert = signature.toByteArray()
|
||||
|
||||
// Generate an X509Certificate from the data
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val x509Cert = certFactory.generateCertificate(ByteArrayInputStream(rawCert)) as X509Certificate
|
||||
|
||||
// Get the SHA256 fingerprint
|
||||
val fingerprint = MessageDigest.getInstance("SHA256").digest(x509Cert.encoded).joinToString("") {
|
||||
"%02x".format(it)
|
||||
}
|
||||
|
||||
return fingerprint
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
package app.revanced.manager.flutter
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import app.revanced.library.ApkUtils
|
||||
import app.revanced.library.ApkUtils.applyTo
|
||||
import app.revanced.manager.flutter.utils.Aapt
|
||||
import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver
|
||||
import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver
|
||||
import app.revanced.patcher.Patcher
|
||||
import app.revanced.patcher.PatcherConfig
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.loadPatchesFromDex
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.util.logging.LogRecord
|
||||
import java.util.logging.Logger
|
||||
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private lateinit var installerChannel: MethodChannel
|
||||
private var cancel: Boolean = false
|
||||
private var stopResult: MethodChannel.Result? = null
|
||||
|
||||
private lateinit var patches: Set<Patch<*>>
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
val patcherChannel = "app.revanced.manager.flutter/patcher"
|
||||
val installerChannel = "app.revanced.manager.flutter/installer"
|
||||
val openBrowserChannel = "app.revanced.manager.flutter/browser"
|
||||
|
||||
MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
openBrowserChannel
|
||||
).setMethodCallHandler { call, result ->
|
||||
if (call.method == "openBrowser") {
|
||||
val searchQuery = call.argument<String>("query")
|
||||
openBrowser(searchQuery)
|
||||
result.success(null)
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
val mainChannel =
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, patcherChannel)
|
||||
|
||||
this.installerChannel =
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, installerChannel)
|
||||
|
||||
mainChannel.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"runPatcher" -> {
|
||||
val inFilePath = call.argument<String>("inFilePath")
|
||||
val outFilePath = call.argument<String>("outFilePath")
|
||||
val selectedPatches = call.argument<List<String>>("selectedPatches")
|
||||
val options = call.argument<Map<String, Map<String, Any>>>("options")
|
||||
val tmpDirPath = call.argument<String>("tmpDirPath")
|
||||
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
||||
val keystorePassword = call.argument<String>("keystorePassword")
|
||||
|
||||
if (
|
||||
inFilePath != null &&
|
||||
outFilePath != null &&
|
||||
selectedPatches != null &&
|
||||
options != null &&
|
||||
tmpDirPath != null &&
|
||||
keyStoreFilePath != null &&
|
||||
keystorePassword != null
|
||||
) {
|
||||
cancel = false
|
||||
runPatcher(
|
||||
result,
|
||||
inFilePath,
|
||||
outFilePath,
|
||||
selectedPatches,
|
||||
options,
|
||||
tmpDirPath,
|
||||
keyStoreFilePath,
|
||||
keystorePassword
|
||||
)
|
||||
} else result.error(
|
||||
"INVALID_ARGUMENTS",
|
||||
"Invalid arguments",
|
||||
"One or more arguments are missing"
|
||||
)
|
||||
}
|
||||
|
||||
"stopPatcher" -> {
|
||||
cancel = true
|
||||
stopResult = result
|
||||
}
|
||||
|
||||
"getPatches" -> {
|
||||
val patchBundleFilePath = call.argument<String>("patchBundleFilePath")!!
|
||||
|
||||
try {
|
||||
val patchBundleFile = File(patchBundleFilePath)
|
||||
patchBundleFile.setWritable(false)
|
||||
patches = loadPatchesFromDex(
|
||||
setOf(patchBundleFile),
|
||||
optimizedDexDirectory = codeCacheDir
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
return@setMethodCallHandler result.error(
|
||||
"PATCH_BUNDLE_ERROR",
|
||||
"Failed to load patch bundle",
|
||||
t.stackTraceToString()
|
||||
)
|
||||
}
|
||||
|
||||
JSONArray().apply {
|
||||
patches.forEach {
|
||||
JSONObject().apply {
|
||||
put("name", it.name)
|
||||
put("description", it.description)
|
||||
put("excluded", !it.use)
|
||||
put("compatiblePackages", JSONArray().apply {
|
||||
it.compatiblePackages?.forEach { (name, versions) ->
|
||||
val compatiblePackageJson = JSONObject().apply {
|
||||
put("name", name)
|
||||
put(
|
||||
"versions",
|
||||
JSONArray().apply {
|
||||
versions?.forEach { version ->
|
||||
put(version)
|
||||
}
|
||||
})
|
||||
}
|
||||
put(compatiblePackageJson)
|
||||
}
|
||||
})
|
||||
put("options", JSONArray().apply {
|
||||
it.options.values.forEach { option ->
|
||||
JSONObject().apply {
|
||||
put("key", option.key)
|
||||
put("title", option.title)
|
||||
put("description", option.description)
|
||||
put("required", option.required)
|
||||
|
||||
fun JSONObject.putValue(
|
||||
value: Any?,
|
||||
key: String = "value"
|
||||
) = if (value is Array<*>) put(
|
||||
key,
|
||||
JSONArray().apply {
|
||||
value.forEach { put(it) }
|
||||
})
|
||||
else put(key, value)
|
||||
|
||||
putValue(option.default)
|
||||
|
||||
option.values?.let { values ->
|
||||
put(
|
||||
"values",
|
||||
JSONObject().apply {
|
||||
values.forEach { (key, value) ->
|
||||
putValue(value, key)
|
||||
}
|
||||
})
|
||||
} ?: put("values", null)
|
||||
put("type", option.type)
|
||||
}.let(::put)
|
||||
}
|
||||
})
|
||||
}.let(::put)
|
||||
}
|
||||
}.toString().let(result::success)
|
||||
}
|
||||
|
||||
"installApk" -> {
|
||||
val apkPath = call.argument<String>("apkPath")!!
|
||||
PackageInstallerManager.result = result
|
||||
installApk(apkPath)
|
||||
}
|
||||
|
||||
"uninstallApp" -> {
|
||||
val packageName = call.argument<String>("packageName")!!
|
||||
uninstallApp(packageName)
|
||||
PackageInstallerManager.result = result
|
||||
}
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openBrowser(query: String?) {
|
||||
val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
|
||||
putExtra(SearchManager.QUERY, query)
|
||||
}
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun runPatcher(
|
||||
result: MethodChannel.Result,
|
||||
inFilePath: String,
|
||||
outFilePath: String,
|
||||
selectedPatches: List<String>,
|
||||
options: Map<String, Map<String, Any>>,
|
||||
tmpDirPath: String,
|
||||
keyStoreFilePath: String,
|
||||
keystorePassword: String
|
||||
) {
|
||||
val inFile = File(inFilePath)
|
||||
// Necessary because the file is copied from a nonwriteable location.
|
||||
inFile.setWritable(true)
|
||||
inFile.setReadable(true)
|
||||
val outFile = File(outFilePath)
|
||||
val keyStoreFile = File(keyStoreFilePath)
|
||||
val tmpDir = File(tmpDirPath)
|
||||
|
||||
Thread {
|
||||
fun updateProgress(progress: Double, header: String, log: String) {
|
||||
handler.post {
|
||||
installerChannel.invokeMethod(
|
||||
"update",
|
||||
mapOf(
|
||||
"progress" to progress,
|
||||
"header" to header,
|
||||
"log" to log
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun postStop() = handler.post { stopResult!!.success(null) }
|
||||
|
||||
fun cancel(block: () -> Unit = {}): Boolean {
|
||||
if (cancel) {
|
||||
block()
|
||||
postStop()
|
||||
}
|
||||
|
||||
return cancel
|
||||
}
|
||||
|
||||
|
||||
// Setup logger
|
||||
Logger.getLogger("").apply {
|
||||
handlers.forEach { handler ->
|
||||
handler.close()
|
||||
removeHandler(handler)
|
||||
}
|
||||
|
||||
object : java.util.logging.Handler() {
|
||||
override fun publish(record: LogRecord) {
|
||||
if (cancel) return
|
||||
if (
|
||||
record.loggerName?.startsWith("app.revanced") == true ||
|
||||
// Logger in class brut.util.OS.
|
||||
record.loggerName == ""
|
||||
) updateProgress(-1.0, "", record.message)
|
||||
}
|
||||
|
||||
override fun flush() = Unit
|
||||
override fun close() = flush()
|
||||
}.let(::addHandler)
|
||||
}
|
||||
|
||||
try {
|
||||
updateProgress(0.0, "Reading APK...", "Reading APK")
|
||||
|
||||
val patcher = Patcher(
|
||||
PatcherConfig(
|
||||
inFile,
|
||||
tmpDir,
|
||||
Aapt.binary(applicationContext).absolutePath,
|
||||
tmpDir.path,
|
||||
)
|
||||
)
|
||||
|
||||
if (cancel(patcher::close)) return@Thread
|
||||
updateProgress(0.02, "Loading patches...", "Loading patches")
|
||||
|
||||
val patches = patches.filter { patch ->
|
||||
val isCompatible = patch.compatiblePackages?.any { (name, _) ->
|
||||
name == patcher.context.packageMetadata.packageName
|
||||
} ?: false
|
||||
|
||||
val compatibleOrUniversal =
|
||||
isCompatible || patch.compatiblePackages.isNullOrEmpty()
|
||||
|
||||
compatibleOrUniversal && selectedPatches.any { it == patch.name }
|
||||
}.onEach { patch ->
|
||||
options[patch.name]?.forEach { (key, value) ->
|
||||
patch.options[key] = value
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
if (cancel(patcher::close)) return@Thread
|
||||
updateProgress(0.05, "Executing...", "")
|
||||
|
||||
val patcherResult = patcher.use {
|
||||
it += patches
|
||||
|
||||
runBlocking {
|
||||
// Update the progress bar every time a patch is executed from 0.15 to 0.7
|
||||
val totalPatchesCount = patches.size
|
||||
val progressStep = 0.55 / totalPatchesCount
|
||||
var progress = 0.05
|
||||
|
||||
patcher().collect(FlowCollector { patchResult: PatchResult ->
|
||||
if (cancel(patcher::close)) return@FlowCollector
|
||||
|
||||
val msg = patchResult.exception?.let {
|
||||
val writer = StringWriter()
|
||||
it.printStackTrace(PrintWriter(writer))
|
||||
"${patchResult.patch.name} failed: $writer"
|
||||
} ?: run {
|
||||
"${patchResult.patch.name} succeeded"
|
||||
}
|
||||
|
||||
updateProgress(progress, "", msg)
|
||||
progress += progressStep
|
||||
})
|
||||
}
|
||||
|
||||
if (cancel(patcher::close)) return@Thread
|
||||
updateProgress(0.75, "Building...", "")
|
||||
|
||||
patcher.get()
|
||||
}
|
||||
|
||||
if (cancel(patcher::close)) return@Thread
|
||||
|
||||
patcherResult.applyTo(inFile)
|
||||
|
||||
if (cancel(patcher::close)) return@Thread
|
||||
|
||||
ApkUtils.signApk(
|
||||
inFile,
|
||||
outFile,
|
||||
"ReVanced",
|
||||
ApkUtils.KeyStoreDetails(
|
||||
keyStoreFile,
|
||||
keystorePassword,
|
||||
"alias",
|
||||
keystorePassword
|
||||
)
|
||||
)
|
||||
|
||||
updateProgress(.85, "Patched", "Patched APK")
|
||||
} catch (ex: Throwable) {
|
||||
if (!cancel) {
|
||||
val stack = ex.stackTraceToString()
|
||||
updateProgress(
|
||||
-100.0,
|
||||
"Failed",
|
||||
"An error occurred:\n$stack"
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
inFile.delete()
|
||||
tmpDir.deleteRecursively()
|
||||
}
|
||||
|
||||
handler.post { result.success(null) }
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun installApk(apkPath: String) {
|
||||
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
|
||||
val sessionParams =
|
||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val sessionId: Int = packageInstaller.createSession(sessionParams)
|
||||
val session: PackageInstaller.Session = packageInstaller.openSession(sessionId)
|
||||
session.use { activeSession ->
|
||||
val sessionOutputStream = activeSession.openWrite(applicationContext.packageName, 0, -1)
|
||||
sessionOutputStream.use { outputStream ->
|
||||
val apkFile = File(apkPath)
|
||||
apkFile.inputStream().use { inputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
val receiverIntent = Intent(applicationContext, InstallerReceiver::class.java).apply {
|
||||
action = "APP_INSTALL_ACTION"
|
||||
}
|
||||
val receiverPendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
sessionId,
|
||||
receiverIntent,
|
||||
PackageInstallerManager.flags
|
||||
)
|
||||
session.commit(receiverPendingIntent.intentSender)
|
||||
session.close()
|
||||
}
|
||||
|
||||
private fun uninstallApp(packageName: String) {
|
||||
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
|
||||
val receiverIntent = Intent(applicationContext, UninstallerReceiver::class.java).apply {
|
||||
action = "APP_UNINSTALL_ACTION"
|
||||
}
|
||||
val receiverPendingIntent =
|
||||
PendingIntent.getBroadcast(context, 0, receiverIntent, PackageInstallerManager.flags)
|
||||
packageInstaller.uninstall(packageName, receiverPendingIntent.intentSender)
|
||||
}
|
||||
|
||||
object PackageInstallerManager {
|
||||
var result: MethodChannel.Result? = null
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app.revanced.manager.flutter.utils
|
||||
|
||||
import android.content.Context
|
||||
import java.io.File
|
||||
|
||||
object Aapt {
|
||||
fun binary(context: Context): File {
|
||||
return File(context.applicationInfo.nativeLibraryDir).resolveAapt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.resolveAapt() = resolve(list { _, f -> !File(f).isDirectory && f.contains("aapt") }!!.first())
|
||||
@@ -0,0 +1,32 @@
|
||||
package app.revanced.manager.flutter.utils.packageInstaller
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import app.revanced.manager.flutter.MainActivity
|
||||
|
||||
class InstallerReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
if (confirmationIntent != null) {
|
||||
context.startActivity(confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
|
||||
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
||||
val otherPackageName = intent.getStringExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME)
|
||||
MainActivity.PackageInstallerManager.result!!.success(mapOf(
|
||||
"status" to status,
|
||||
"packageName" to packageName,
|
||||
"message" to message,
|
||||
"otherPackageName" to otherPackageName
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.revanced.manager.flutter.utils.packageInstaller
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import app.revanced.manager.flutter.MainActivity
|
||||
|
||||
class UninstallerReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
if (confirmationIntent != null) {
|
||||
context.startActivity(confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
MainActivity.PackageInstallerManager.result!!.success(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
12
android/app/src/main/res/drawable/launch_background.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
3
android/app/src/main/res/raw/revanced_manager_keep.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:keep="@drawable/ic_notification" />
|
||||
19
android/app/src/main/res/values-night-v31/styles.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
18
android/app/src/main/res/values-night/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
20
android/app/src/main/res/values-v31/styles.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1B1B1B</color>
|
||||
</resources>
|
||||
18
android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
4
android/app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="cache" path="." />
|
||||
</paths>
|
||||
7
android/app/src/profile/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
40
android/build.gradle.kts
Normal file
@@ -0,0 +1,40 @@
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||
credentials {
|
||||
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
|
||||
password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout.buildDirectory = File("../build")
|
||||
|
||||
project(":screenshot_callback") {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
afterEvaluate {
|
||||
extensions.findByName("android")?.let {
|
||||
it as CommonExtension<*, *, *, *, *, *>
|
||||
if (it.compileSdk != null && it.compileSdk!! < 31)
|
||||
it.compileSdk = 34
|
||||
}
|
||||
}
|
||||
|
||||
layout.buildDirectory = rootProject.layout.buildDirectory.file(name).get().asFile
|
||||
evaluationDependsOn(":app")
|
||||
}
|
||||
7
android/gradle.properties
Normal file
@@ -0,0 +1,7 @@
|
||||
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
org.gradle.caching=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
9
android/gradle/libs.versions.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[versions]
|
||||
revanced-patcher = "21.0.0"
|
||||
revanced-library = "3.1.0"
|
||||
desugar_jdk_libs = "2.1.4"
|
||||
|
||||
[libraries]
|
||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||
revanced-library = { module = "app.revanced:revanced-library", version.ref = "revanced-library" }
|
||||
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
distributionSha256Sum=8d97a97984f6cbd2b85fe4c60a743440a347544bf18818048e611f5288d46c94
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
24
android/settings.gradle.kts
Normal file
@@ -0,0 +1,24 @@
|
||||
pluginManagement {
|
||||
val properties = java.util.Properties().apply {
|
||||
load(file("local.properties").inputStream())
|
||||
}
|
||||
|
||||
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.9.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.10" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"branches": [
|
||||
"main",
|
||||
{
|
||||
"name": "dev",
|
||||
"prerelease": true
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"releaseRules": [
|
||||
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"gradle-semantic-release-plugin",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"CHANGELOG.md",
|
||||
"gradle.properties"
|
||||
],
|
||||
"message": "chore: Release API v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@saithodev/semantic-release-backmerge",
|
||||
{
|
||||
"backmergeBranches": [{"from": "main", "to": "dev"}],
|
||||
"clearWorkspace": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
# api [1.0.0-dev.2](https://github.com/ReVanced/revanced-manager/compare/api@1.0.0-dev.1...api@1.0.0-dev.2) (2026-02-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Update to Patcher v22 ([#2939](https://github.com/ReVanced/revanced-manager/issues/2939)) ([8667051](https://github.com/ReVanced/revanced-manager/commit/8667051283f934a32ac7b7cc76178397dc45a0e6))
|
||||
|
||||
# api 1.0.0-dev.1 (2026-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Allow multiple downloaders per APK ([#2916](https://github.com/ReVanced/revanced-manager/issues/2916)) ([0f56377](https://github.com/ReVanced/revanced-manager/commit/0f56377214fae05693a21177daf7534c103df74e))
|
||||
158
api/api/api.api
@@ -1,158 +0,0 @@
|
||||
public abstract interface class app/revanced/manager/downloader/BaseDownloadScope : app/revanced/manager/downloader/Scope {
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/ConstantsKt {
|
||||
public static final field DOWNLOADER_HOST_PERMISSION Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/DownloadUrl : android/os/Parcelable {
|
||||
public static final field $stable I
|
||||
public static final field CREATOR Landroid/os/Parcelable$Creator;
|
||||
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun component1 ()Ljava/lang/String;
|
||||
public final fun component2 ()Ljava/util/Map;
|
||||
public final fun copy (Ljava/lang/String;Ljava/util/Map;)Lapp/revanced/manager/downloader/DownloadUrl;
|
||||
public static synthetic fun copy$default (Lapp/revanced/manager/downloader/DownloadUrl;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lapp/revanced/manager/downloader/DownloadUrl;
|
||||
public final fun describeContents ()I
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getHeaders ()Ljava/util/Map;
|
||||
public final fun getUrl ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public final fun toDownloadResult ()Lkotlin/Pair;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
public final fun writeToParcel (Landroid/os/Parcel;I)V
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/Downloader {
|
||||
public static final field $stable I
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/DownloaderBuilder {
|
||||
public static final field $stable I
|
||||
}
|
||||
|
||||
public abstract interface annotation class app/revanced/manager/downloader/DownloaderHostApi : java/lang/annotation/Annotation {
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/DownloaderKt {
|
||||
public static final fun Downloader (ILkotlin/jvm/functions/Function1;)Lapp/revanced/manager/downloader/DownloaderBuilder;
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/DownloaderScope : app/revanced/manager/downloader/Scope {
|
||||
public static final field $stable I
|
||||
public final fun download (Lkotlin/jvm/functions/Function3;)V
|
||||
public final fun get (Lkotlin/jvm/functions/Function4;)V
|
||||
public fun getDownloaderPackageName ()Ljava/lang/String;
|
||||
public fun getHostPackageName ()Ljava/lang/String;
|
||||
public final fun useService (Landroid/content/Intent;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/ExtensionsKt {
|
||||
public static final fun download (Lapp/revanced/manager/downloader/DownloaderScope;Lkotlin/jvm/functions/Function4;)V
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/manager/downloader/GetScope : app/revanced/manager/downloader/Scope {
|
||||
public abstract fun requestStartActivity (Landroid/content/Intent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/manager/downloader/InputDownloadScope : app/revanced/manager/downloader/BaseDownloadScope {
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/manager/downloader/OutputDownloadScope : app/revanced/manager/downloader/BaseDownloadScope {
|
||||
public abstract fun reportSize (JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/Package : android/os/Parcelable {
|
||||
public static final field $stable I
|
||||
public static final field CREATOR Landroid/os/Parcelable$Creator;
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||
public final fun component1 ()Ljava/lang/String;
|
||||
public final fun component2 ()Ljava/lang/String;
|
||||
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/manager/downloader/Package;
|
||||
public static synthetic fun copy$default (Lapp/revanced/manager/downloader/Package;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lapp/revanced/manager/downloader/Package;
|
||||
public final fun describeContents ()I
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getName ()Ljava/lang/String;
|
||||
public final fun getVersion ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
public final fun writeToParcel (Landroid/os/Parcel;I)V
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/manager/downloader/Scope {
|
||||
public abstract fun getDownloaderPackageName ()Ljava/lang/String;
|
||||
public abstract fun getHostPackageName ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/manager/downloader/UserInteractionException : java/lang/Exception {
|
||||
public static final field $stable I
|
||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/manager/downloader/UserInteractionException$Activity : app/revanced/manager/downloader/UserInteractionException {
|
||||
public static final field $stable I
|
||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/UserInteractionException$Activity$Cancelled : app/revanced/manager/downloader/UserInteractionException$Activity {
|
||||
public static final field $stable I
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/UserInteractionException$Activity$NotCompleted : app/revanced/manager/downloader/UserInteractionException$Activity {
|
||||
public static final field $stable I
|
||||
public final fun getIntent ()Landroid/content/Intent;
|
||||
public final fun getResultCode ()I
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/UserInteractionException$RequestDenied : app/revanced/manager/downloader/UserInteractionException {
|
||||
public static final field $stable I
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/webview/APIKt {
|
||||
public static final fun WebViewDownloader (ILkotlin/jvm/functions/Function4;)Lapp/revanced/manager/downloader/DownloaderBuilder;
|
||||
public static final fun runWebView (Lapp/revanced/manager/downloader/GetScope;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public class app/revanced/manager/downloader/webview/IWebView$Default : app/revanced/manager/downloader/webview/IWebView {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public fun finish ()V
|
||||
public fun load (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/manager/downloader/webview/IWebView$Stub : android/os/Binder, app/revanced/manager/downloader/webview/IWebView {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public static fun asInterface (Landroid/os/IBinder;)Lapp/revanced/manager/downloader/webview/IWebView;
|
||||
public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
|
||||
}
|
||||
|
||||
public class app/revanced/manager/downloader/webview/IWebViewEvents$Default : app/revanced/manager/downloader/webview/IWebViewEvents {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public fun download (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
public fun pageLoad (Ljava/lang/String;)V
|
||||
public fun ready (Lapp/revanced/manager/downloader/webview/IWebView;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/manager/downloader/webview/IWebViewEvents$Stub : android/os/Binder, app/revanced/manager/downloader/webview/IWebViewEvents {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public static fun asInterface (Landroid/os/IBinder;)Lapp/revanced/manager/downloader/webview/IWebViewEvents;
|
||||
public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/manager/downloader/webview/WebViewCallbackScope : app/revanced/manager/downloader/Scope {
|
||||
public abstract fun finish (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun load (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class app/revanced/manager/downloader/webview/WebViewScope : app/revanced/manager/downloader/Scope {
|
||||
public static final field $stable I
|
||||
public final fun download (Lkotlin/jvm/functions/Function5;)V
|
||||
public fun getDownloaderPackageName ()Ljava/lang/String;
|
||||
public fun getHostPackageName ()Ljava/lang/String;
|
||||
public final fun pageLoad (Lkotlin/jvm/functions/Function3;)V
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
`maven-publish`
|
||||
signing
|
||||
}
|
||||
|
||||
group = "app.revanced"
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.ktx)
|
||||
implementation(libs.runtime.ktx)
|
||||
implementation(libs.activity.compose)
|
||||
implementation(libs.appcompat)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
freeCompilerArgs.addAll(
|
||||
"-Xexplicit-backing-fields",
|
||||
"-Xcontext-parameters",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.manager.downloader"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
nonPublicMarkers += "app.revanced.manager.downloader.DownloaderHostApi"
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-manager")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR") ?: extra["gpr.user"] as String?
|
||||
password = System.getenv("GITHUB_TOKEN") ?: extra["gpr.key"] as String?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
create<MavenPublication>("Api") {
|
||||
afterEvaluate {
|
||||
from(components["release"])
|
||||
}
|
||||
|
||||
groupId = "app.revanced"
|
||||
artifactId = "revanced-manager-api"
|
||||
version = project.version.toString()
|
||||
|
||||
pom {
|
||||
name = "ReVanced Manager API"
|
||||
description = "API for ReVanced Manager."
|
||||
url = "https://revanced.app"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = "GNU General Public License v3.0"
|
||||
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = "ReVanced"
|
||||
name = "ReVanced"
|
||||
email = "contact@revanced.app"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = "scm:git:git://github.com/revanced/revanced-manager.git"
|
||||
developerConnection = "scm:git:git@github.com:revanced/revanced-manager.git"
|
||||
url = "https://github.com/revanced/revanced-manager"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications["Api"])
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
version = 1.0.0-dev.2
|
||||
1
api/gradlew
vendored
@@ -1 +0,0 @@
|
||||
../gradlew
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "api",
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@anolilab/multi-semantic-release": "^1.1.10",
|
||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
</manifest>
|
||||
@@ -1,8 +0,0 @@
|
||||
// IWebView.aidl
|
||||
package app.revanced.manager.downloader.webview;
|
||||
|
||||
@JavaPassthrough(annotation="@app.revanced.manager.downloader.DownloaderHostApi")
|
||||
oneway interface IWebView {
|
||||
void load(String url);
|
||||
void finish();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// IWebViewEvents.aidl
|
||||
package app.revanced.manager.downloader.webview;
|
||||
|
||||
import app.revanced.manager.downloader.webview.IWebView;
|
||||
|
||||
@JavaPassthrough(annotation="@app.revanced.manager.downloader.DownloaderHostApi")
|
||||
oneway interface IWebViewEvents {
|
||||
void ready(IWebView iface);
|
||||
void pageLoad(String url);
|
||||
void download(String url, String mimetype, String userAgent);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package app.revanced.manager.downloader
|
||||
|
||||
/**
|
||||
* The permission ID of the special downloader host permission. Only ReVanced Manager will have this permission.
|
||||
* Downloader UI activities and internal services can be protected using this permission.
|
||||
*/
|
||||
const val DOWNLOADER_HOST_PERMISSION = "app.revanced.manager.permission.DOWNLOADER_HOST"
|
||||
@@ -1,175 +0,0 @@
|
||||
package app.revanced.manager.downloader
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.app.Activity
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.StringRes
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@RequiresOptIn(
|
||||
level = RequiresOptIn.Level.ERROR,
|
||||
message = "This API is only intended for downloader hosts, don't use it in a downloader.",
|
||||
)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class DownloaderHostApi
|
||||
|
||||
/**
|
||||
* The base interface for all DSL scopes.
|
||||
*/
|
||||
interface Scope {
|
||||
/**
|
||||
* The package name of ReVanced Manager.
|
||||
*/
|
||||
val hostPackageName: String
|
||||
|
||||
/**
|
||||
* The package name of the downloader.
|
||||
*/
|
||||
val downloaderPackageName: String
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope of [DownloaderScope.get].
|
||||
*/
|
||||
interface GetScope : Scope {
|
||||
/**
|
||||
* Ask the user to perform some required interaction in the activity specified by the provided [Intent].
|
||||
* This function returns normally with the resulting [Intent] when the activity finishes with code [Activity.RESULT_OK].
|
||||
*
|
||||
* @throws UserInteractionException.RequestDenied User decided to skip this downloader.
|
||||
* @throws UserInteractionException.Activity.Cancelled The activity was cancelled.
|
||||
* @throws UserInteractionException.Activity.NotCompleted The activity finished with an unknown result code.
|
||||
*/
|
||||
suspend fun requestStartActivity(intent: Intent): Intent?
|
||||
}
|
||||
|
||||
interface BaseDownloadScope : Scope
|
||||
|
||||
/**
|
||||
* The scope for [DownloaderScope.download].
|
||||
*/
|
||||
interface InputDownloadScope : BaseDownloadScope
|
||||
|
||||
typealias Size = Long
|
||||
typealias DownloadResult = Pair<InputStream, Size?>
|
||||
|
||||
typealias Version = String
|
||||
typealias GetResult<T> = Pair<T, Version?>
|
||||
|
||||
class DownloaderScope<T : Parcelable> internal constructor(
|
||||
private val scopeImpl: Scope,
|
||||
internal val context: Context
|
||||
) : Scope by scopeImpl {
|
||||
// Returning an InputStream is the primary way for a downloader to implement the download function, but we also want to offer an OutputStream API since using InputStream might not be convenient in all cases.
|
||||
// It is much easier to implement the main InputStream API on top of OutputStreams compared to doing it the other way around, which is why we are using OutputStream here.
|
||||
internal var download: (suspend OutputDownloadScope.(T, OutputStream) -> Unit)? = null
|
||||
internal var get: (suspend GetScope.(String, String?) -> GetResult<T>?)? = null
|
||||
private val inputDownloadScopeImpl = object : InputDownloadScope, Scope by scopeImpl {}
|
||||
|
||||
/**
|
||||
* Define the download block of the downloader.
|
||||
*/
|
||||
fun download(block: suspend InputDownloadScope.(data: T) -> DownloadResult) {
|
||||
download = { app, outputStream ->
|
||||
val (inputStream, size) = inputDownloadScopeImpl.block(app)
|
||||
|
||||
inputStream.use {
|
||||
if (size != null) reportSize(size)
|
||||
it.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the get block of the downloader.
|
||||
* The block should return null if the app cannot be found. The version in the result must match the version argument unless it is null.
|
||||
*/
|
||||
fun get(block: suspend GetScope.(packageName: String, version: String?) -> GetResult<T>?) {
|
||||
get = block
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilize the service specified by the provided [Intent]. The service will be unbound when the scope ends.
|
||||
*/
|
||||
suspend fun <R : Any?> useService(intent: Intent, block: suspend (IBinder) -> R): R {
|
||||
var onBind: ((IBinder) -> Unit)? = null
|
||||
val serviceConn = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) =
|
||||
onBind!!(service!!)
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {}
|
||||
}
|
||||
|
||||
return try {
|
||||
val binder = withTimeout(10000L) {
|
||||
suspendCoroutine { continuation ->
|
||||
onBind = continuation::resume
|
||||
context.bindService(intent, serviceConn, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
}
|
||||
block(binder)
|
||||
} finally {
|
||||
onBind = null
|
||||
context.unbindService(serviceConn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DownloaderBuilder<T : Parcelable> internal constructor(
|
||||
@param:StringRes private val name: Int,
|
||||
private val block: DownloaderScope<T>.() -> Unit
|
||||
) {
|
||||
@DownloaderHostApi
|
||||
fun build(scopeImpl: Scope, context: Context) =
|
||||
with(DownloaderScope<T>(scopeImpl, context)) {
|
||||
block()
|
||||
|
||||
Downloader(
|
||||
download = download!!,
|
||||
get = get!!,
|
||||
name = name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Downloader<T : Parcelable> internal constructor(
|
||||
@property:DownloaderHostApi val get: suspend GetScope.(packageName: String, version: String?) -> GetResult<T>?,
|
||||
@property:DownloaderHostApi val download: suspend OutputDownloadScope.(data: T, outputStream: OutputStream) -> Unit,
|
||||
@property:DownloaderHostApi @param:StringRes val name: Int,
|
||||
)
|
||||
|
||||
/**
|
||||
* Define a downloader.
|
||||
*/
|
||||
fun <T : Parcelable> Downloader(@StringRes name: Int, block: DownloaderScope<T>.() -> Unit) =
|
||||
DownloaderBuilder(name, block)
|
||||
|
||||
/**
|
||||
* @see GetScope.requestStartActivity
|
||||
*/
|
||||
sealed class UserInteractionException(message: String) : Exception(message) {
|
||||
class RequestDenied @DownloaderHostApi constructor() :
|
||||
UserInteractionException("Request denied by user")
|
||||
|
||||
sealed class Activity(message: String) : UserInteractionException(message) {
|
||||
class Cancelled @DownloaderHostApi constructor() : Activity("Interaction cancelled")
|
||||
|
||||
/**
|
||||
* @param resultCode The result code of the activity.
|
||||
* @param intent The [Intent] of the activity.
|
||||
*/
|
||||
class NotCompleted @DownloaderHostApi constructor(
|
||||
val resultCode: Int,
|
||||
val intent: Intent?
|
||||
) :
|
||||
Activity("Unexpected activity result code: $resultCode")
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package app.revanced.manager.downloader
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import java.io.OutputStream
|
||||
|
||||
/**
|
||||
* The scope of the [OutputStream] version of [DownloaderScope.download].
|
||||
*/
|
||||
interface OutputDownloadScope : BaseDownloadScope {
|
||||
suspend fun reportSize(size: Long)
|
||||
}
|
||||
|
||||
/**
|
||||
* A replacement for [DownloaderScope.download] that uses [OutputStream].
|
||||
* The provided [OutputStream] does not need to be closed manually.
|
||||
*/
|
||||
fun <T : Parcelable> DownloaderScope<T>.download(block: suspend OutputDownloadScope.(T, OutputStream) -> Unit) {
|
||||
download = block
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs [GetScope.requestStartActivity] with an [Intent] created using the type information of [ACTIVITY].
|
||||
* @see [GetScope.requestStartActivity]
|
||||
*/
|
||||
suspend inline fun <reified ACTIVITY : Activity> GetScope.requestStartActivity() =
|
||||
requestStartActivity(
|
||||
Intent().apply { setClassName(downloaderPackageName, ACTIVITY::class.qualifiedName!!) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Performs [DownloaderScope.useService] with an [Intent] created using the type information of [SERVICE].
|
||||
* @see [DownloaderScope.useService]
|
||||
*/
|
||||
suspend inline fun <reified SERVICE : Service, R : Any?> DownloaderScope<*>.useService(
|
||||
noinline block: suspend (IBinder) -> R
|
||||
) = useService(
|
||||
Intent().apply { setClassName(downloaderPackageName, SERVICE::class.qualifiedName!!) }, block
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
package app.revanced.manager.downloader
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URI
|
||||
|
||||
/**
|
||||
* A simple parcelable data class for storing a package name and version.
|
||||
* This can be used as the data type for downloaders that only need a name and version to implement their [DownloaderScope.download] function.
|
||||
*
|
||||
* @param name The package name.
|
||||
* @param version The version.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Package(val name: String, val version: String) : Parcelable
|
||||
|
||||
/**
|
||||
* A data class for storing a download URL.
|
||||
*
|
||||
* @param url The download URL.
|
||||
* @param headers The headers to use for the request.
|
||||
*/
|
||||
@Parcelize
|
||||
data class DownloadUrl(val url: String, val headers: Map<String, String> = emptyMap()) : Parcelable {
|
||||
/**
|
||||
* Converts this into a [DownloadResult].
|
||||
*/
|
||||
fun toDownloadResult(): DownloadResult = with(URI.create(url).toURL().openConnection() as HttpURLConnection) {
|
||||
useCaches = false
|
||||
allowUserInteraction = false
|
||||
headers.forEach(::setRequestProperty)
|
||||
|
||||
connectTimeout = 10_000
|
||||
connect()
|
||||
|
||||
inputStream to getHeaderField("Content-Length").toLong()
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package app.revanced.manager.downloader.webview
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.annotation.StringRes
|
||||
import app.revanced.manager.downloader.DownloadUrl
|
||||
import app.revanced.manager.downloader.DownloaderScope
|
||||
import app.revanced.manager.downloader.GetScope
|
||||
import app.revanced.manager.downloader.Scope
|
||||
import app.revanced.manager.downloader.Downloader
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
typealias InitialUrl = String
|
||||
typealias PageLoadCallback<T> = suspend WebViewCallbackScope<T>.(url: String) -> Unit
|
||||
typealias DownloadCallback<T> = suspend WebViewCallbackScope<T>.(url: String, mimeType: String, userAgent: String) -> Unit
|
||||
|
||||
interface WebViewCallbackScope<T> : Scope {
|
||||
/**
|
||||
* Finishes the activity and returns the [result].
|
||||
*/
|
||||
suspend fun finish(result: T)
|
||||
|
||||
/**
|
||||
* Tells the WebView to load the specified [url].
|
||||
*/
|
||||
suspend fun load(url: String)
|
||||
}
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
class WebViewScope<T> internal constructor(
|
||||
coroutineScope: CoroutineScope,
|
||||
private val scopeImpl: Scope,
|
||||
setResult: (T) -> Unit
|
||||
) : Scope by scopeImpl {
|
||||
private var onPageLoadCallback: PageLoadCallback<T> = {}
|
||||
private var onDownloadCallback: DownloadCallback<T> = { _, _, _ -> }
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val dispatcher = Dispatchers.Default.limitedParallelism(1)
|
||||
private lateinit var webView: IWebView
|
||||
internal lateinit var initialUrl: String
|
||||
|
||||
internal val binder = object : IWebViewEvents.Stub() {
|
||||
override fun ready(iface: IWebView?) {
|
||||
coroutineScope.launch(dispatcher) {
|
||||
webView = iface!!.also {
|
||||
it.load(initialUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageLoad(url: String?) {
|
||||
coroutineScope.launch(dispatcher) { onPageLoadCallback(callbackScope, url!!) }
|
||||
}
|
||||
|
||||
override fun download(url: String?, mimetype: String?, userAgent: String?) {
|
||||
coroutineScope.launch(dispatcher) {
|
||||
onDownloadCallback(
|
||||
callbackScope,
|
||||
url!!,
|
||||
mimetype!!,
|
||||
userAgent!!
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val callbackScope = object : WebViewCallbackScope<T>, Scope by scopeImpl {
|
||||
override suspend fun finish(result: T) {
|
||||
setResult(result)
|
||||
// Tell the WebViewActivity to finish
|
||||
webView.let { withContext(Dispatchers.IO) { it.finish() } }
|
||||
}
|
||||
|
||||
override suspend fun load(url: String) {
|
||||
webView.let { withContext(Dispatchers.IO) { it.load(url) } }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView attempts to download a file to disk.
|
||||
*/
|
||||
fun download(block: DownloadCallback<T>) {
|
||||
onDownloadCallback = block
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView finishes loading a page.
|
||||
*/
|
||||
fun pageLoad(block: PageLoadCallback<T>) {
|
||||
onPageLoadCallback = block
|
||||
}
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
private value class Container<U>(val value: U)
|
||||
|
||||
/**
|
||||
* Run a [android.webkit.WebView] Activity controlled by the provided code block.
|
||||
* The activity will keep running until it is cancelled or an event handler calls [WebViewCallbackScope.finish].
|
||||
* The [block] defines the event handlers and returns the initial URL.
|
||||
*
|
||||
* @param title The string displayed in the action bar.
|
||||
* @param block The control block.
|
||||
*/
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
suspend fun <T> GetScope.runWebView(
|
||||
title: String,
|
||||
block: suspend WebViewScope<T>.() -> InitialUrl
|
||||
) = supervisorScope {
|
||||
var result by Delegates.notNull<Container<T>>()
|
||||
|
||||
val scope = WebViewScope<T>(this@supervisorScope, this@runWebView) { result = Container(it) }
|
||||
scope.initialUrl = scope.block()
|
||||
|
||||
// Start the webview activity and wait until it finishes.
|
||||
requestStartActivity(Intent().apply {
|
||||
putExtra(
|
||||
WebViewActivity.KEY,
|
||||
WebViewActivity.Parameters(title, scope.binder)
|
||||
)
|
||||
setClassName(
|
||||
hostPackageName,
|
||||
WebViewActivity::class.qualifiedName!!
|
||||
)
|
||||
})
|
||||
|
||||
// Return the result and cancel any leftover coroutines.
|
||||
coroutineContext.cancelChildren()
|
||||
result.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement a downloader using [runWebView] and [DownloadUrl]. This function will automatically define a handler for download events unlike [runWebView].
|
||||
* Returning null inside the [block] is equivalent to returning null inside [DownloaderScope.get].
|
||||
*
|
||||
* @see runWebView
|
||||
*/
|
||||
fun WebViewDownloader(@StringRes name: Int, block: suspend WebViewScope<DownloadUrl>.(packageName: String, version: String?) -> InitialUrl?) =
|
||||
Downloader(name) {
|
||||
val label = context.getString(name)
|
||||
|
||||
get { packageName, version ->
|
||||
class ReturnNull : Exception()
|
||||
|
||||
try {
|
||||
runWebView(label) {
|
||||
download { url, _, userAgent ->
|
||||
finish(
|
||||
DownloadUrl(
|
||||
url,
|
||||
mapOf("User-Agent" to userAgent)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
block(this@runWebView, packageName, version) ?: throw ReturnNull()
|
||||
} to version
|
||||
} catch (_: ReturnNull) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
download {
|
||||
it.toDownloadResult()
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package app.revanced.manager.downloader.webview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.view.MenuItem
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.downloader.DownloaderHostApi
|
||||
import app.revanced.manager.downloader.R
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
@DownloaderHostApi
|
||||
class WebViewActivity : ComponentActivity() {
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val vm by viewModels<WebViewModel>()
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_webview)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
val webView = findViewById<WebView>(R.id.webview)
|
||||
onBackPressedDispatcher.addCallback {
|
||||
if (webView.canGoBack()) webView.goBack()
|
||||
else cancelActivity()
|
||||
}
|
||||
|
||||
val params = intent.getParcelableExtra<Parameters>(KEY)!!
|
||||
actionBar?.apply {
|
||||
title = params.title
|
||||
setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel)
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
val events = IWebViewEvents.Stub.asInterface(params.events)!!
|
||||
vm.setup(events)
|
||||
|
||||
webView.apply {
|
||||
settings.apply {
|
||||
cacheMode = WebSettings.LOAD_NO_CACHE
|
||||
allowContentAccess = false
|
||||
domStorageEnabled = true
|
||||
javaScriptEnabled = true
|
||||
}
|
||||
|
||||
webViewClient = vm.webViewClient
|
||||
setDownloadListener { url, userAgent, _, mimetype, _ ->
|
||||
vm.onDownload(url, mimetype, userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
vm.commands.collect {
|
||||
when (it) {
|
||||
is WebViewModel.Command.Finish -> {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
is WebViewModel.Command.Load -> webView.loadUrl(it.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelActivity() {
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) {
|
||||
cancelActivity()
|
||||
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
|
||||
@Parcelize
|
||||
internal class Parameters(
|
||||
val title: String, val events: IBinder
|
||||
) : Parcelable
|
||||
|
||||
internal companion object {
|
||||
const val KEY = "params"
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DownloaderHostApi::class)
|
||||
internal class WebViewModel : ViewModel() {
|
||||
init {
|
||||
CookieManager.getInstance().apply {
|
||||
removeAllCookies(null)
|
||||
setAcceptCookie(true)
|
||||
}
|
||||
}
|
||||
|
||||
private val commandChannel = Channel<Command>()
|
||||
val commands = commandChannel.receiveAsFlow()
|
||||
|
||||
private var eventBinder: IWebViewEvents? = null
|
||||
private val ctrlBinder = object : IWebView.Stub() {
|
||||
override fun load(url: String?) {
|
||||
viewModelScope.launch {
|
||||
commandChannel.send(Command.Load(url!!))
|
||||
}
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
viewModelScope.launch {
|
||||
commandChannel.send(Command.Finish)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
eventBinder!!.pageLoad(url)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDownload(url: String, mimeType: String, userAgent: String) {
|
||||
eventBinder!!.download(url, mimeType, userAgent)
|
||||
}
|
||||
|
||||
fun setup(binder: IWebViewEvents) {
|
||||
if (eventBinder != null) return
|
||||
eventBinder = binder
|
||||
binder.ready(ctrlBinder)
|
||||
}
|
||||
|
||||
sealed interface Command {
|
||||
data class Load(val url: String) : Command
|
||||
data object Finish : Command
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/main">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
@@ -1 +0,0 @@
|
||||
<resources></resources>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.WebViewActivity" parent="Theme.AppCompat.DayNight">
|
||||
<item name="android:windowActionBar">true</item>
|
||||
<item name="android:windowNoTitle">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
1
app/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
465
app/CHANGELOG.md
@@ -1,465 +0,0 @@
|
||||
# app [1.26.0-dev.26](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.25...v1.26.0-dev.26) (2026-02-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve InstalledAppsScreen caching/perf ([#2963](https://github.com/ReVanced/revanced-manager/issues/2963)) ([323d217](https://github.com/ReVanced/revanced-manager/commit/323d2170efa0e3f2c8d2245ff08a2e92c52cb9d7))
|
||||
|
||||
# app [1.26.0-dev.25](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.24...v1.26.0-dev.25) (2026-02-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve AppSelectorScreen caching/perf ([#2961](https://github.com/ReVanced/revanced-manager/issues/2961)) ([81e5a93](https://github.com/ReVanced/revanced-manager/commit/81e5a9315db4c858fa29163d15457af97163967a))
|
||||
|
||||
# app [1.26.0-dev.24](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.23...v1.26.0-dev.24) (2026-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Incorrect patcher string value ([f19080a](https://github.com/ReVanced/revanced-manager/commit/f19080a9862be4600860b67cd981b9a3a56b21e7))
|
||||
* Never show again toggle doing nothing for Update dialog ([6f4219c](https://github.com/ReVanced/revanced-manager/commit/6f4219c01ba43b70e130f210c8d48978a6056f68))
|
||||
* Release builds not working ([00b3c59](https://github.com/ReVanced/revanced-manager/commit/00b3c59572760993f69f2663456237faafff0283))
|
||||
* Remove quotes from strings ([fc30ba5](https://github.com/ReVanced/revanced-manager/commit/fc30ba5e83f27329b05dc81f484bb8f7471b6a99))
|
||||
* we actually don't want to exclude reflection symbols ([3222840](https://github.com/ReVanced/revanced-manager/commit/32228404c4ef38bb8931d5cfdf6a7aefde23a89c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add announcements ([#2948](https://github.com/ReVanced/revanced-manager/issues/2948)) ([813df46](https://github.com/ReVanced/revanced-manager/commit/813df46847620bd854dccc52ff244388b3385e17))
|
||||
* Update to Patcher v22 ([#2939](https://github.com/ReVanced/revanced-manager/issues/2939)) ([8667051](https://github.com/ReVanced/revanced-manager/commit/8667051283f934a32ac7b7cc76178397dc45a0e6))
|
||||
|
||||
# app [1.26.0-dev.23](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.22...v1.26.0-dev.23) (2026-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Flutter migration ([#2946](https://github.com/ReVanced/revanced-manager/issues/2946)) ([31a0f75](https://github.com/ReVanced/revanced-manager/commit/31a0f751aaff3233f50821963f28454d239318b2))
|
||||
|
||||
# app [1.26.0-dev.22](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.21...v1.26.0-dev.22) (2026-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Create modules path in root installer ([4fc2ba3](https://github.com/ReVanced/revanced-manager/commit/4fc2ba339b6d275fb9da8b65ff86b74a6594436d))
|
||||
|
||||
# app [1.26.0-dev.21](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.20...v1.26.0-dev.21) (2026-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Allow multiple downloaders per APK ([#2916](https://github.com/ReVanced/revanced-manager/issues/2916)) ([0f56377](https://github.com/ReVanced/revanced-manager/commit/0f56377214fae05693a21177daf7534c103df74e))
|
||||
|
||||
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Save FAB freaking out in select patches screen ([4c0b6b0](https://github.com/ReVanced/revanced-manager/commit/4c0b6b02e95a8d6f655bcf5c25493b1f9a4a4dcd))
|
||||
|
||||
# app [1.26.0-dev.19](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.18...v1.26.0-dev.19) (2026-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **locales:** use buildconfig instead of generating kt file ([72b1db9](https://github.com/ReVanced/revanced-manager/commit/72b1db9a2f33ab5d5fffd8ba83c05901eff19bea))
|
||||
|
||||
# app [1.26.0-dev.18](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.17...v1.26.0-dev.18) (2026-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Prevent trailing comma when no locales are generated ([b16931c](https://github.com/ReVanced/revanced-manager/commit/b16931ca79d5ce4d17c75f6dd3bf6f976b8ff7be))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add language settings ([#2913](https://github.com/ReVanced/revanced-manager/issues/2913)) ([df31b39](https://github.com/ReVanced/revanced-manager/commit/df31b39cc8c1fbf00bc3301468e8e7e4b283caf2))
|
||||
|
||||
# app [1.26.0-dev.17](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.16...v1.26.0-dev.17) (2026-01-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow updating patches on metered networks ([9d9a0e8](https://github.com/ReVanced/revanced-manager/commit/9d9a0e81dbc9e73e6e3181f6bea9cabb69e49ea8))
|
||||
|
||||
# app [1.26.0-dev.16](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.15...v1.26.0-dev.16) (2025-12-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Show patches as individual steps in patcher screen ([#2889](https://github.com/ReVanced/revanced-manager/issues/2889)) ([11dd6e4](https://github.com/ReVanced/revanced-manager/commit/11dd6e4064099427a8c9bc6f225a19412e5c70e2))
|
||||
|
||||
# app [1.26.0-dev.15](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.14...v1.26.0-dev.15) (2025-12-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* install dialog getting stuck ([#2900](https://github.com/ReVanced/revanced-manager/issues/2900)) ([18a4df9](https://github.com/ReVanced/revanced-manager/commit/18a4df9af9cac120fdb8e4ff7aadd2e2a8d5c1a6))
|
||||
|
||||
# app [1.26.0-dev.14](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.13...v1.26.0-dev.14) (2025-12-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Update selected patch count when SelectionState changes ([#2896](https://github.com/ReVanced/revanced-manager/issues/2896)) ([0d26df0](https://github.com/ReVanced/revanced-manager/commit/0d26df03f463195dae550240c7f652680763079c))
|
||||
|
||||
# app [1.26.0-dev.13](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.12...v1.26.0-dev.13) (2025-12-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Make patcher screen design more consistent with inspiration ([#2805](https://github.com/ReVanced/revanced-manager/issues/2805)) ([dbb6c01](https://github.com/ReVanced/revanced-manager/commit/dbb6c01e89a5e710185ff4304de0ac9e19bed053))
|
||||
|
||||
# app [1.26.0-dev.12](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.11...v1.26.0-dev.12) (2025-12-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Improve trust plugin dialog design ([#2420](https://github.com/ReVanced/revanced-manager/issues/2420)) ([0300da9](https://github.com/ReVanced/revanced-manager/commit/0300da9eac6c0fc29dbbb66622c0d52f4cf68934))
|
||||
|
||||
# app [1.26.0-dev.11](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.10...v1.26.0-dev.11) (2025-10-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add pure black theme ([#2824](https://github.com/ReVanced/revanced-manager/issues/2824)) ([3d75ffe](https://github.com/ReVanced/revanced-manager/commit/3d75ffe6a7a39efdebe13dbd07c937c1de409ead))
|
||||
|
||||
# app [1.26.0-dev.10](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.9...v1.26.0-dev.10) (2025-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prevent back presses during installation ([2ff7072](https://github.com/ReVanced/revanced-manager/commit/2ff70728b490b92f212a82dcf599bc0c23f589e7))
|
||||
|
||||
# app [1.26.0-dev.9](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.8...v1.26.0-dev.9) (2025-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Instantly re-fetch patch bundle on pre-release preference update ([d5671db](https://github.com/ReVanced/revanced-manager/commit/d5671db3a77541c07bbbb4c3baca02f3ba0703f2)), closes [#2784](https://github.com/ReVanced/revanced-manager/issues/2784)
|
||||
|
||||
# app [1.26.0-dev.8](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.7...v1.26.0-dev.8) (2025-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Offcenter loading indicator in AppSelector ([12d92ba](https://github.com/ReVanced/revanced-manager/commit/12d92ba8110f5d1ac78aeecfa575444b5c53f561))
|
||||
|
||||
# app [1.26.0-dev.7](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.6...v1.26.0-dev.7) (2025-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Improve consistency between pre-release toggles ([e1b768c](https://github.com/ReVanced/revanced-manager/commit/e1b768c4679ecae8bff8007bdab56ff6544b12b6))
|
||||
|
||||
# app [1.26.0-dev.6](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.5...v1.26.0-dev.6) (2025-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Broken version comparison ([c327857](https://github.com/ReVanced/revanced-manager/commit/c3278578237dcddd9e7ab79ee80a02fdeef9604d))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Open contributor's GitHub profile when clicked ([#2775](https://github.com/ReVanced/revanced-manager/issues/2775)) ([2571cb8](https://github.com/ReVanced/revanced-manager/commit/2571cb8c1108e9c1ed84950f17692c09d66e0556))
|
||||
|
||||
# app [1.26.0-dev.5](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.4...v1.26.0-dev.5) (2025-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Toggle to use pre-release versions of ReVanced Patches ([08cec67](https://github.com/ReVanced/revanced-manager/commit/08cec674bbbe5297090ac5ee6039569975fbe9e7))
|
||||
|
||||
# app [1.26.0-dev.4](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.3...v1.26.0-dev.4) (2025-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add newlines to debug logs ([4753873](https://github.com/ReVanced/revanced-manager/commit/4753873866b575e2dcb160020df63f63862c8f33))
|
||||
|
||||
# app [1.26.0-dev.3](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.2...v1.26.0-dev.3) (2025-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Toggle to use pre-release versions of ReVanced Manager ([#2773](https://github.com/ReVanced/revanced-manager/issues/2773)) ([d758964](https://github.com/ReVanced/revanced-manager/commit/d7589647426b3d3438161a2f0b59bf4f154ac34b))
|
||||
|
||||
# app [1.26.0-dev.2](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.1...v1.26.0-dev.2) (2025-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Migration of keystore, by fixing mislabeling of alias as cn ([#2769](https://github.com/ReVanced/revanced-manager/issues/2769)) ([aeab639](https://github.com/ReVanced/revanced-manager/commit/aeab639b2b09e8bbd2478cfbf5a518586405c0f7))
|
||||
|
||||
# app [1.26.0-dev.1](https://github.com/ReVanced/revanced-manager/compare/v1.25.1...v1.26.0-dev.1) (2025-10-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `ExtendedFloatingActionButton` not accessible by screen readers ([#2080](https://github.com/ReVanced/revanced-manager/issues/2080)) ([e4f19b0](https://github.com/ReVanced/revanced-manager/commit/e4f19b0c251e818cce59e11362a29dc8f657e065))
|
||||
* add bounds checks in patch selector ([483be5d](https://github.com/ReVanced/revanced-manager/commit/483be5d722db2be2595f6f6dd0c537a6c8487daf))
|
||||
* Add missing header for "Updates" settings ([#2642](https://github.com/ReVanced/revanced-manager/issues/2642)) ([d4d2056](https://github.com/ReVanced/revanced-manager/commit/d4d2056585ccd4a0456318448dc822c0f40c9c50))
|
||||
* Allow different app version when downloading via plugin if setting is off ([#2579](https://github.com/ReVanced/revanced-manager/issues/2579)) ([59d233e](https://github.com/ReVanced/revanced-manager/commit/59d233e15c885104900c7d4129fb4839c4da81e0))
|
||||
* always use default patch selection if customization is disabled ([cc77181](https://github.com/ReVanced/revanced-manager/commit/cc771817cba3dfd8f704cb7ecc9089ad7911c6ce))
|
||||
* android icon not loading in app selector ([deea682](https://github.com/ReVanced/revanced-manager/commit/deea68265157da65ef98986d751e2551797522e0))
|
||||
* automatically focus search views ([d23d673](https://github.com/ReVanced/revanced-manager/commit/d23d673c4703cdfa3be3a292873bbb37bea30ac7))
|
||||
* available updates dialog list item color ([1a54313](https://github.com/ReVanced/revanced-manager/commit/1a54313c1dc4efbb8b274201a79e28661a7ecf64))
|
||||
* Broken header padding in `AlertDialogExtended` when using an Icon ([8d939a6](https://github.com/ReVanced/revanced-manager/commit/8d939a6669909a44382fc7404276f2eeefcf728d))
|
||||
* broken logo in about page on release builds ([ad775f3](https://github.com/ReVanced/revanced-manager/commit/ad775f3059345dd93ff2baf6d018c2beecc413df))
|
||||
* buildfile syntax ([#66](https://github.com/ReVanced/revanced-manager/issues/66)) ([5c17a78](https://github.com/ReVanced/revanced-manager/commit/5c17a78e46db586642d53362267472fbbd47ae8c))
|
||||
* bundles not loading on Android 14 ([56896d6](https://github.com/ReVanced/revanced-manager/commit/56896d6197baa836bcd4a499ea2cee487e3d07c8))
|
||||
* Change the title in the Update screen from "Updates" to "Update" ([5f23769](https://github.com/ReVanced/revanced-manager/commit/5f2376919bd036987eba8188e3a1a2ff53ef6793)), closes [#1960](https://github.com/ReVanced/revanced-manager/issues/1960)
|
||||
* cleanup advanced settings screen ([02ea5c6](https://github.com/ReVanced/revanced-manager/commit/02ea5c6d4a2e6baa7c034b614deb6e4232cf6d0b))
|
||||
* **Compose:** Adjusted universal patches safeguard and warnings ([#2550](https://github.com/ReVanced/revanced-manager/issues/2550)) ([663cf2d](https://github.com/ReVanced/revanced-manager/commit/663cf2d6b86c276c6bb236af8e05a4f69df9eba0))
|
||||
* contributors screen fix ([#1256](https://github.com/ReVanced/revanced-manager/issues/1256)) ([dc73462](https://github.com/ReVanced/revanced-manager/commit/dc73462ac41bd5f1813358eb5e2265a3e2e7c0f9))
|
||||
* contributors screen repository name ([426b289](https://github.com/ReVanced/revanced-manager/commit/426b28932fe37a6d7412685819ffc8e26b69d31c))
|
||||
* Correct preference description ([#2619](https://github.com/ReVanced/revanced-manager/issues/2619)) ([0096169](https://github.com/ReVanced/revanced-manager/commit/0096169af8f9e2db6c22b8e88f0dfe1cab1260be))
|
||||
* Correctly display universal patches warning ([#2570](https://github.com/ReVanced/revanced-manager/issues/2570)) ([24c4cd3](https://github.com/ReVanced/revanced-manager/commit/24c4cd3f991953dd00b5bf5e7c3ec965315a9528))
|
||||
* correctly patch apk files ([c5cb18a](https://github.com/ReVanced/revanced-manager/commit/c5cb18a7eab838ea096577780335a29b9771b43d))
|
||||
* crash caused by compose inlining bug ([05fe058](https://github.com/ReVanced/revanced-manager/commit/05fe0581516a373cc26dd559d3fc7f21fcf16f3f))
|
||||
* crash when removing used bundles ([189c993](https://github.com/ReVanced/revanced-manager/commit/189c993ada6406db6f8c48c4051c5bd9fac98e2b))
|
||||
* delete temporary files ([#1341](https://github.com/ReVanced/revanced-manager/issues/1341)) ([b03f7b1](https://github.com/ReVanced/revanced-manager/commit/b03f7b18a029465142d08fe1ed68e92c81586a5f))
|
||||
* disable `WebView` history ([#1278](https://github.com/ReVanced/revanced-manager/issues/1278)) ([a811df9](https://github.com/ReVanced/revanced-manager/commit/a811df9547da33fc61397cb33ba5fd35ee470ff9))
|
||||
* display version from manifest ([#2634](https://github.com/ReVanced/revanced-manager/issues/2634)) ([1fb94b7](https://github.com/ReVanced/revanced-manager/commit/1fb94b711fdbbbca7d9baaa90c53faf208fc4d0d))
|
||||
* Do not poll battery optimization status ([#2491](https://github.com/ReVanced/revanced-manager/issues/2491)) ([26778f5](https://github.com/ReVanced/revanced-manager/commit/26778f57e6dd185d9aed1086aa03659a2e91d1a9))
|
||||
* don't store app list in parcel ([e7802ed](https://github.com/ReVanced/revanced-manager/commit/e7802ed3d714cbe6e29409d27989c65d4d7ce6a5))
|
||||
* dont crash when the bundle cannot be downloaded ([4d201f1](https://github.com/ReVanced/revanced-manager/commit/4d201f17f2ce01aad6adb456a49c3f03526c5ad3))
|
||||
* **downloader:** versions not loading correctly ([16c4290](https://github.com/ReVanced/revanced-manager/commit/16c4290f05d94cbe53e68cb98307d7be1bfce7af))
|
||||
* handle edge-to-edge properly in fullscreen dialogs ([eba92e2](https://github.com/ReVanced/revanced-manager/commit/eba92e2644663b10e7e17f2cf955afefe260d769))
|
||||
* handle exceptions when checking for bundle updates ([1dd6738](https://github.com/ReVanced/revanced-manager/commit/1dd673896454710094e83789abb585c106ee6bcb))
|
||||
* Handle open source licenses page crash ([#2569](https://github.com/ReVanced/revanced-manager/issues/2569)) ([f2ea007](https://github.com/ReVanced/revanced-manager/commit/f2ea00757a76ed8758bc0d4df54843c89483c986))
|
||||
* hide patch button ([#1284](https://github.com/ReVanced/revanced-manager/issues/1284)) ([dadc546](https://github.com/ReVanced/revanced-manager/commit/dadc5462e352e91cf971395def91d693677701bc))
|
||||
* Ignore long click when already in delete mode ([6f6296b](https://github.com/ReVanced/revanced-manager/commit/6f6296b8cde56d5fc73e00ef671ca7ab431455f4)), closes [#2503](https://github.com/ReVanced/revanced-manager/issues/2503)
|
||||
* import bundles on another thread ([0383bd7](https://github.com/ReVanced/revanced-manager/commit/0383bd74f73a3523d539c44cdf38b0e857c16bdc))
|
||||
* import export screen UX ([69c119d](https://github.com/ReVanced/revanced-manager/commit/69c119d545ac811c605124173e5cbc97a9064c79))
|
||||
* Improve background running notification ([#2614](https://github.com/ReVanced/revanced-manager/issues/2614)) ([05444d8](https://github.com/ReVanced/revanced-manager/commit/05444d8824a429c7e554d0597f8997e670936a63))
|
||||
* improve bundle page strings ([2a63a61](https://github.com/ReVanced/revanced-manager/commit/2a63a6163a8d2e6ee649cb22099b426ed605de8f))
|
||||
* improve keystore import error handling and show toast ([cd142a7](https://github.com/ReVanced/revanced-manager/commit/cd142a70d3f210161d3c1f20d2cb82a70432469f))
|
||||
* Inconsistent padding for battery optimisation warning ([6c3a99a](https://github.com/ReVanced/revanced-manager/commit/6c3a99a4921ab4438a038ad4c4bccd0326fdd565))
|
||||
* **installer:** make the correct column scrollable ([64496bf](https://github.com/ReVanced/revanced-manager/commit/64496bfbe77a9a44f5535fd5f12eee803ac7c26a))
|
||||
* **installer:** progress tracking ([f547bb7](https://github.com/ReVanced/revanced-manager/commit/f547bb7ab1b7149d7290729527714168a2561b23))
|
||||
* **installer:** properly track worker state ([#32](https://github.com/ReVanced/revanced-manager/issues/32)) ([de1ef23](https://github.com/ReVanced/revanced-manager/commit/de1ef23824227796c8583242e624f83d9dae5af3))
|
||||
* **installer:** save step incorrectly being marked as completed ([0264308](https://github.com/ReVanced/revanced-manager/commit/0264308b6dad051db80da6f130e8d28d86b38f04))
|
||||
* **installer:** sign and install on threads ([3d59ee5](https://github.com/ReVanced/revanced-manager/commit/3d59ee51acc5a6ebb17f68c0462d17d7ecb0f07c))
|
||||
* jvm signature clash error ([ee0f342](https://github.com/ReVanced/revanced-manager/commit/ee0f34245636027d55bd5bdfce4d6a5e6c3b3dcd))
|
||||
* library info not being embedded ([8c9fe69](https://github.com/ReVanced/revanced-manager/commit/8c9fe6989fc6d05afd53baa877f1e6dffc067b50))
|
||||
* load patch bundles earlier ([a2f9e2f](https://github.com/ReVanced/revanced-manager/commit/a2f9e2f1da961a13b2b20e2812593031c9339b88))
|
||||
* Match "Installation incompatible" dialog message with Flutter Manager ([#2231](https://github.com/ReVanced/revanced-manager/issues/2231)) ([fedaedf](https://github.com/ReVanced/revanced-manager/commit/fedaedfda112260144b0b9b0776509ddb3438046))
|
||||
* minify crash on building release ([#1245](https://github.com/ReVanced/revanced-manager/issues/1245)) ([6561e4c](https://github.com/ReVanced/revanced-manager/commit/6561e4c97c19134b22b72e19fad3884f99327b9a))
|
||||
* more android 34 fixes ([7fb1e27](https://github.com/ReVanced/revanced-manager/commit/7fb1e27617b69803b3d4463993b2290877502545))
|
||||
* move battery warning to dashboard ([3a05150](https://github.com/ReVanced/revanced-manager/commit/3a05150fa33f119ecdf436f8508862ef81c327a0))
|
||||
* Move temporary files outside of the cache directory ([#2122](https://github.com/ReVanced/revanced-manager/issues/2122)) ([b93ecc0](https://github.com/ReVanced/revanced-manager/commit/b93ecc0db20339393e1296c44ce4b1dbd837b577))
|
||||
* Offset badge ([c73fdfd](https://github.com/ReVanced/revanced-manager/commit/c73fdfdd2d3a1b8552d9c26df575b3019346596d))
|
||||
* only perform haptics on events ([e55566d](https://github.com/ReVanced/revanced-manager/commit/e55566d3df25480260922f0418b4bbee5d7b7a07))
|
||||
* option state crash ([#1456](https://github.com/ReVanced/revanced-manager/issues/1456)) ([f183b6d](https://github.com/ReVanced/revanced-manager/commit/f183b6d8a6b139fe3e84d5ea3a9658ef900453bc))
|
||||
* parcel error for nullable types ([336eed3](https://github.com/ReVanced/revanced-manager/commit/336eed3a95111ebbe456321f5986e6875ded354e))
|
||||
* pass worker inputs without serialization ([#44](https://github.com/ReVanced/revanced-manager/issues/44)) ([059a72b](https://github.com/ReVanced/revanced-manager/commit/059a72b9dd9103d2b3704daa7dbb13ad83971460))
|
||||
* patch count remaining at zero when using process runtime ([#2542](https://github.com/ReVanced/revanced-manager/issues/2542)) ([f5e1e0b](https://github.com/ReVanced/revanced-manager/commit/f5e1e0b0659e5775dd460b8dfc15427eb0175139))
|
||||
* patch options reset button being broken ([e1647fd](https://github.com/ReVanced/revanced-manager/commit/e1647fdef0c9f68e171a2d15e2b6e744da6bbaf5))
|
||||
* Patch process cancelation dialog conditions ([#2554](https://github.com/ReVanced/revanced-manager/issues/2554)) ([e97b19d](https://github.com/ReVanced/revanced-manager/commit/e97b19d2b65dbfc49ed062b123c363e412b9bf8e))
|
||||
* Patch selection screen padding ([#2533](https://github.com/ReVanced/revanced-manager/issues/2533)) ([cd2dbcc](https://github.com/ReVanced/revanced-manager/commit/cd2dbcc841e56dac99230ea6501af87c43e9c572))
|
||||
* **patcher:** add notification and wakelock to worker; chore: add app icon ([8b6d32d](https://github.com/ReVanced/revanced-manager/commit/8b6d32dd7b3ca4c694414a55a1b6202b62636530))
|
||||
* patches not being reloaded ([dccf861](https://github.com/ReVanced/revanced-manager/commit/dccf86163af34341e3e451df9f24356c7294ae1e))
|
||||
* **patches selector:** copy the selected patches list ([70e49aa](https://github.com/ReVanced/revanced-manager/commit/70e49aaaa3a42510cb9ced2209c90cd1da98391d))
|
||||
* perform selected app operations in the correct order ([34cf848](https://github.com/ReVanced/revanced-manager/commit/34cf848baaaa2504d162c515a95240d45bd7092a))
|
||||
* permission error when using installed app ([8767f0e](https://github.com/ReVanced/revanced-manager/commit/8767f0e99c6de5bbb0a690ced40f6e9a486f0828))
|
||||
* Playback Switch's Haptic Feedback ([#2639](https://github.com/ReVanced/revanced-manager/issues/2639)) ([9fdca5a](https://github.com/ReVanced/revanced-manager/commit/9fdca5a0afd6be8a24e2ec09eec0000b0b9cd179))
|
||||
* process death resilience and account for android 11 bug ([#2355](https://github.com/ReVanced/revanced-manager/issues/2355)) ([83eeeae](https://github.com/ReVanced/revanced-manager/commit/83eeeae801827800a0787e9e753c72d2a24d7970))
|
||||
* progress bar not updating ([dcaa38c](https://github.com/ReVanced/revanced-manager/commit/dcaa38c8824f54da7a833c354b247f309d1c9871))
|
||||
* release builds not working properly ([6f6476e](https://github.com/ReVanced/revanced-manager/commit/6f6476e85158cad4e2497e9f72b73c4dc948f0bc))
|
||||
* remove battery optimization notification if user grants the permission ([9863c51](https://github.com/ReVanced/revanced-manager/commit/9863c5161a1bc16941a323e654f80f8cb0122f9f))
|
||||
* remove the unique constraint for patch bundle names ([ea29d0f](https://github.com/ReVanced/revanced-manager/commit/ea29d0f00c3b3b2c137c4849e6c445a6bf9a180f))
|
||||
* Remove unnecessary screen padding ([8419f75](https://github.com/ReVanced/revanced-manager/commit/8419f75d597dd198aa1029fae2109646c5874078)), closes [#2062](https://github.com/ReVanced/revanced-manager/issues/2062)
|
||||
* remove unused function preventing compilation ([2297e94](https://github.com/ReVanced/revanced-manager/commit/2297e94cb81a9a22ea032d8e247769774ca85087))
|
||||
* Reset cached theme on theme change to avoid broken colors ([#2527](https://github.com/ReVanced/revanced-manager/issues/2527)) ([9a82b78](https://github.com/ReVanced/revanced-manager/commit/9a82b785280954973cafc5e6dccb3c90fdb5ef49))
|
||||
* run blocking IO operations in the correct context ([969ddb7](https://github.com/ReVanced/revanced-manager/commit/969ddb7bef321d7aa2a682b8128b1f755f35c28b))
|
||||
* run props flow on correct dispatcher ([#2035](https://github.com/ReVanced/revanced-manager/issues/2035)) ([d3d4c27](https://github.com/ReVanced/revanced-manager/commit/d3d4c27f6d7affceef233a0138ee6c985c7f56bc))
|
||||
* Screen turns off while patching due to wrong WakeLock ([#2147](https://github.com/ReVanced/revanced-manager/issues/2147)) ([4de5340](https://github.com/ReVanced/revanced-manager/commit/4de534094adc0665021d3ba129a648d896718568))
|
||||
* scrolling in patch selector ([154f036](https://github.com/ReVanced/revanced-manager/commit/154f036fe956096bca983fe9d6654ccca38fd8ac))
|
||||
* Selected patch count ([#2559](https://github.com/ReVanced/revanced-manager/issues/2559)) ([a91ff60](https://github.com/ReVanced/revanced-manager/commit/a91ff60533b44629ea60e8cd6acceeb80b0253b7))
|
||||
* serialization not working ([4d04ae0](https://github.com/ReVanced/revanced-manager/commit/4d04ae088c406d84936120cb753cd1f11fb8a8c2))
|
||||
* show available and selected patches in patch selector screen ([61f1ee0](https://github.com/ReVanced/revanced-manager/commit/61f1ee0627d6cbb6b9a4d226eb6c2f9e0b8c6453))
|
||||
* show install button when installation has been cancelled ([93f4a5b](https://github.com/ReVanced/revanced-manager/commit/93f4a5bb7c912ca77bb04e414432922c89d3e2c0))
|
||||
* Show selection warning also on patch option ([#2643](https://github.com/ReVanced/revanced-manager/issues/2643)) ([3b82767](https://github.com/ReVanced/revanced-manager/commit/3b82767a897eeca1dda1d8343f1db4207050e960))
|
||||
* sources screen being misaligned during transitions ([2ac3d5c](https://github.com/ReVanced/revanced-manager/commit/2ac3d5c483d5cc4776681ed3f900550a4e45f616))
|
||||
* specify `multithreadingDexFileWriter` in `PatcherOptions` ([#1402](https://github.com/ReVanced/revanced-manager/issues/1402)) ([3f362b6](https://github.com/ReVanced/revanced-manager/commit/3f362b605fbce3ea72e7c95b7e0bc614443c7d44))
|
||||
* Support patching on ARMv7 by updating AAPT2 ([#2084](https://github.com/ReVanced/revanced-manager/issues/2084)) ([15b47f9](https://github.com/ReVanced/revanced-manager/commit/15b47f9bb6cd6bb0360fda6ac641cd4c75542287))
|
||||
* Transparent status on fullscreen dialog ([#2654](https://github.com/ReVanced/revanced-manager/issues/2654)) ([a8820a4](https://github.com/ReVanced/revanced-manager/commit/a8820a4daf71704f6945b8f794495fe8a8d7589e))
|
||||
* Turn off filters by default ([#2079](https://github.com/ReVanced/revanced-manager/issues/2079)) ([44f8b1f](https://github.com/ReVanced/revanced-manager/commit/44f8b1fb6bffed5866ada356910119465320a9a8))
|
||||
* typo in string name `import_keystore_description` ([#1273](https://github.com/ReVanced/revanced-manager/issues/1273)) ([933e69e](https://github.com/ReVanced/revanced-manager/commit/933e69e21e97fede2183a26dd1645a6eb96c4509))
|
||||
* **ui:** make entire patches view button selectable ([#1271](https://github.com/ReVanced/revanced-manager/issues/1271)) ([83cdaae](https://github.com/ReVanced/revanced-manager/commit/83cdaaee183ff1b6d905977df38fe4e47f7d5973))
|
||||
* Updates popup shows incorrect names ([#1283](https://github.com/ReVanced/revanced-manager/issues/1283)) ([c879faf](https://github.com/ReVanced/revanced-manager/commit/c879faf2eb338476c6abd9f104922b0d49f95cd6))
|
||||
* Use `compatible` rather than `support` when referring to patch compatibility ([#2422](https://github.com/ReVanced/revanced-manager/issues/2422)) ([8b3c4eb](https://github.com/ReVanced/revanced-manager/commit/8b3c4eb91c491a0971e2ccf7d46012437eca5c25))
|
||||
* use correct `getViewModel` ([5b6ae80](https://github.com/ReVanced/revanced-manager/commit/5b6ae800fdfc93ef5058b21b3e48daac2a4e1358))
|
||||
* use correct classes to determine option type ([e833bf4](https://github.com/ReVanced/revanced-manager/commit/e833bf4ad14811bb6880ae2d97055e4ce0de222f))
|
||||
* use correct directory ([9e1ebb3](https://github.com/ReVanced/revanced-manager/commit/9e1ebb390244dcb9af03a9164a32386481ec5691))
|
||||
* Use FAB instead of ListItem to patch in App Overview ([6ace71b](https://github.com/ReVanced/revanced-manager/commit/6ace71b739302466274ce9b46f5f7dd6ab9da05d)), closes [#1995](https://github.com/ReVanced/revanced-manager/issues/1995)
|
||||
* use proper update icon ([b59a161](https://github.com/ReVanced/revanced-manager/commit/b59a16191a61c64275137c4a6145fd30d68aa480))
|
||||
* use ReVanced ring logo in about section ([#1302](https://github.com/ReVanced/revanced-manager/issues/1302)) ([933a4a3](https://github.com/ReVanced/revanced-manager/commit/933a4a32203425e745e05615217a8d0975c2e959))
|
||||
* Use the correct icon in API URL dialog ([c22e5b4](https://github.com/ReVanced/revanced-manager/commit/c22e5b4051515e0f02828a2b30f6af19b48ba55f)), closes [#1972](https://github.com/ReVanced/revanced-manager/issues/1972)
|
||||
* use upsert when modifying installed apps ([90edf1d](https://github.com/ReVanced/revanced-manager/commit/90edf1ddd0de29b299855810402a31828d989d04))
|
||||
* **VersionSelector:** use correct LazyColumn item key ([413fe98](https://github.com/ReVanced/revanced-manager/commit/413fe980a8c0b45e3924c98b2fbd1a3e9b579528))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **about screen:** complete about screen ([1d6b34a](https://github.com/ReVanced/revanced-manager/commit/1d6b34a39f76e8e733649f7fcfeb20eb1009a39a))
|
||||
* Add `isScrollingUp` support for ScrollState ([bf049c3](https://github.com/ReVanced/revanced-manager/commit/bf049c3c1ac12a60c5c6226b5c3fec7f72caa7db))
|
||||
* add ability to share debug logs ([feb0ca4](https://github.com/ReVanced/revanced-manager/commit/feb0ca4cf315e5d332f36039fbb989b3cfb9cf58))
|
||||
* add checkboxes to the downloaded apps page ([ca93524](https://github.com/ReVanced/revanced-manager/commit/ca93524be0b37f38b860d8512c81d2898b2860af))
|
||||
* Add confirm dialogs when toggling dangerous settings ([#2072](https://github.com/ReVanced/revanced-manager/issues/2072)) ([6643276](https://github.com/ReVanced/revanced-manager/commit/66432764cfe8192f4cf8e599a592f27c675f25ec))
|
||||
* Add confirmation dialog to "Reset" options ([#2576](https://github.com/ReVanced/revanced-manager/issues/2576)) ([f32ffbb](https://github.com/ReVanced/revanced-manager/commit/f32ffbb6f2224f886af14205721fb2372f396de2))
|
||||
* Add downloader plugin system ([#2041](https://github.com/ReVanced/revanced-manager/issues/2041)) ([ca38737](https://github.com/ReVanced/revanced-manager/commit/ca3873778307612b93af3273ffe4821c6a5e398d))
|
||||
* add external process runtime ([#1799](https://github.com/ReVanced/revanced-manager/issues/1799)) ([0d73e0c](https://github.com/ReVanced/revanced-manager/commit/0d73e0cd32b6af3526c226ce4695c7e905f65b15))
|
||||
* Add haptic feedback ([#1457](https://github.com/ReVanced/revanced-manager/issues/1457)) ([76e0c95](https://github.com/ReVanced/revanced-manager/commit/76e0c9518746620cd2723a99c310f92f5b3fd996))
|
||||
* Add installer status dialog ([#1473](https://github.com/ReVanced/revanced-manager/issues/1473)) ([43b3743](https://github.com/ReVanced/revanced-manager/commit/43b37432138d7cd8a507efad80827d6f3bdcdf08))
|
||||
* add network checks for features that require it ([f3f8bc4](https://github.com/ReVanced/revanced-manager/commit/f3f8bc4ec2f593ade91324d78f9ce83f60ef65cc))
|
||||
* add patch bundle info screen ([#55](https://github.com/ReVanced/revanced-manager/issues/55)) ([8ae4e85](https://github.com/ReVanced/revanced-manager/commit/8ae4e850dae9cf4df14afe90048ca0b0a48389ac))
|
||||
* add patches selector bottom sheet ([#1360](https://github.com/ReVanced/revanced-manager/issues/1360)) ([f6fb534](https://github.com/ReVanced/revanced-manager/commit/f6fb534e04777b4f0ec2ff2b13768c724c68c028))
|
||||
* add required options screen ([#2378](https://github.com/ReVanced/revanced-manager/issues/2378)) ([3a63e42](https://github.com/ReVanced/revanced-manager/commit/3a63e42df9ce50069a573d98cf44a8abec03b639))
|
||||
* Add reset button to custom API ([#2076](https://github.com/ReVanced/revanced-manager/issues/2076)) ([df52a7b](https://github.com/ReVanced/revanced-manager/commit/df52a7bdef05e1c9f034ae067c3dd183fb8fdffd)), closes [#2051](https://github.com/ReVanced/revanced-manager/issues/2051)
|
||||
* Add sensitivity to `isScrollingUp` ([f6ca4e9](https://github.com/ReVanced/revanced-manager/commit/f6ca4e95551193c8d21afd09872d9bbe6c80c0e8))
|
||||
* add social links ([#1294](https://github.com/ReVanced/revanced-manager/issues/1294)) ([7df3350](https://github.com/ReVanced/revanced-manager/commit/7df3350acb4aae957e2a7c0d2f30faf6cae6ab85))
|
||||
* add toast feedback to the bundle update button ([ea50e65](https://github.com/ReVanced/revanced-manager/commit/ea50e65ab1d626152bdd40c1893cd408b7271472))
|
||||
* add user agent ([#1382](https://github.com/ReVanced/revanced-manager/issues/1382)) ([3aea6cb](https://github.com/ReVanced/revanced-manager/commit/3aea6cbaecc9db103e9a3925b3c4a531de6c5f0e))
|
||||
* advanced settings page with device info ([#51](https://github.com/ReVanced/revanced-manager/issues/51)) ([86e4244](https://github.com/ReVanced/revanced-manager/commit/86e42449eb553417726b95f79f6edd7f526f6d44))
|
||||
* allow bundles to use classes from other bundles ([#1951](https://github.com/ReVanced/revanced-manager/issues/1951)) ([af8e2b4](https://github.com/ReVanced/revanced-manager/commit/af8e2b44c027d978046a0e7926f1425f0348b098))
|
||||
* allow user to save logs ([a008cf5](https://github.com/ReVanced/revanced-manager/commit/a008cf5dd143fafb1f642cd037db29393716f7d5))
|
||||
* animate the arrow button ([db070b1](https://github.com/ReVanced/revanced-manager/commit/db070b125bf08ff251450259045755e6469c2d5e))
|
||||
* app downloader ([#43](https://github.com/ReVanced/revanced-manager/issues/43)) ([1f1a480](https://github.com/ReVanced/revanced-manager/commit/1f1a480d51edb310934523024c52e0c19b066662))
|
||||
* app selector screen ([373cc4b](https://github.com/ReVanced/revanced-manager/commit/373cc4bbb1a8194bf9475d0a13e1c154cd87480b))
|
||||
* **app-selector:** show patchable installed apps first ([#1496](https://github.com/ReVanced/revanced-manager/issues/1496)) ([afb0f80](https://github.com/ReVanced/revanced-manager/commit/afb0f80de5a73c213f77bfde761ea1ea0886abef))
|
||||
* armv7 warning ([2ffcaec](https://github.com/ReVanced/revanced-manager/commit/2ffcaec724d5a13b816e04813d45cde75681eb69))
|
||||
* Automatic language detection ([#2032](https://github.com/ReVanced/revanced-manager/issues/2032)) ([36a1c3f](https://github.com/ReVanced/revanced-manager/commit/36a1c3f36807500fbe820bf4142fef159b138c7d))
|
||||
* backend ([45a54d1](https://github.com/ReVanced/revanced-manager/commit/45a54d1608a77547e06748867d63a452224727b6))
|
||||
* better installer ui ([#29](https://github.com/ReVanced/revanced-manager/issues/29)) ([14888f9](https://github.com/ReVanced/revanced-manager/commit/14888f9da71ecf1c50d770123d1e8dd09aa6c8b1))
|
||||
* **bundles tab:** add BackHandler ([a9171e1](https://github.com/ReVanced/revanced-manager/commit/a9171e17bd628601f1e074a7fcdf74c15cb73709))
|
||||
* Change "Update" to "Show" in Update Available notification ([5c43413](https://github.com/ReVanced/revanced-manager/commit/5c434137d332aabaaca236b6f9616d7727d0b3d2)), closes [#1959](https://github.com/ReVanced/revanced-manager/issues/1959)
|
||||
* change appID and name of debug builds ([5b3e9e5](https://github.com/ReVanced/revanced-manager/commit/5b3e9e595cded277c051cc669d9f29bcb6ce5d18))
|
||||
* **Changelogs:** overall improvement ([#1429](https://github.com/ReVanced/revanced-manager/issues/1429)) ([2a3590d](https://github.com/ReVanced/revanced-manager/commit/2a3590ddd2cc74b746a3f632a93970bfa23cf384))
|
||||
* check for updates on startup ([#1462](https://github.com/ReVanced/revanced-manager/issues/1462)) ([bb2164e](https://github.com/ReVanced/revanced-manager/commit/bb2164e1a95a698b1b0f69e725af5e0e1e45b868))
|
||||
* check if the version being used is the recommended version ([#1675](https://github.com/ReVanced/revanced-manager/issues/1675)) ([9d961f6](https://github.com/ReVanced/revanced-manager/commit/9d961f6a52d15ed6116afc78c7008460347da69a))
|
||||
* Collapse ExtendedFAB on scroll ([#1630](https://github.com/ReVanced/revanced-manager/issues/1630)) ([b5c1f6d](https://github.com/ReVanced/revanced-manager/commit/b5c1f6d732b65c1c9becb7962c51a70a840dea73))
|
||||
* **Compose:** Add confirmation dialog on multiple operations ([#2529](https://github.com/ReVanced/revanced-manager/issues/2529)) ([2671e68](https://github.com/ReVanced/revanced-manager/commit/2671e68004269deebdedaee38a6692b2302ca732))
|
||||
* **Compose:** hide developer settings ([#2551](https://github.com/ReVanced/revanced-manager/issues/2551)) ([0030c7a](https://github.com/ReVanced/revanced-manager/commit/0030c7a7885feee0578ee1423ee2aefc6a0e2c2c))
|
||||
* **Compose:** Improve patches selector tab by adding the bundle version ([#2545](https://github.com/ReVanced/revanced-manager/issues/2545)) ([3710675](https://github.com/ReVanced/revanced-manager/commit/3710675ac0ca77cecfb172b4cf148f41a762bf06))
|
||||
* **Compose:** Move developer options to top level ([#2528](https://github.com/ReVanced/revanced-manager/issues/2528)) ([cedc6ad](https://github.com/ReVanced/revanced-manager/commit/cedc6ad49f23d778a52a8846f9e384fd2106e074))
|
||||
* contributors screen ([#42](https://github.com/ReVanced/revanced-manager/issues/42)) ([3f54381](https://github.com/ReVanced/revanced-manager/commit/3f54381d307fd71296be18e97a1ab870f1cdc297))
|
||||
* **Contributors Screen:** implement design from Figma ([#1465](https://github.com/ReVanced/revanced-manager/issues/1465)) ([d5bdc29](https://github.com/ReVanced/revanced-manager/commit/d5bdc293f308e2a283d744afdc1aed6a165f7166))
|
||||
* Dashboard Screen ([#18](https://github.com/ReVanced/revanced-manager/issues/18)) ([a127b95](https://github.com/ReVanced/revanced-manager/commit/a127b959ead5a9c83a0c4f7e7840aeeb68362c0d))
|
||||
* disable filter chips when there are no patches ([fd520bb](https://github.com/ReVanced/revanced-manager/commit/fd520bba700bae9d8eae745ce23a95b07b7f7d34))
|
||||
* dont ask for root on launch ([9562d80](https://github.com/ReVanced/revanced-manager/commit/9562d80bfdc785fe5ed512a15cfd7c0e09091acc))
|
||||
* download apps in patcher screen ([#73](https://github.com/ReVanced/revanced-manager/issues/73)) ([a854221](https://github.com/ReVanced/revanced-manager/commit/a854221969c363712a0b3de84607092709db291f))
|
||||
* experimental patches setting ([b07fd23](https://github.com/ReVanced/revanced-manager/commit/b07fd2321dd0aecce556f341e2b18f930baa58fd))
|
||||
* filter options for patches ([62bccd1](https://github.com/ReVanced/revanced-manager/commit/62bccd150441747e5cd6de71de304e416922bdda))
|
||||
* finish implementing the sources system ([#70](https://github.com/ReVanced/revanced-manager/issues/70)) ([858b0ec](https://github.com/ReVanced/revanced-manager/commit/858b0ec5b456043fa61b681bbbd195fd9c30a6f0))
|
||||
* get bundle information from jar manifest ([#2027](https://github.com/ReVanced/revanced-manager/issues/2027)) ([60fdec9](https://github.com/ReVanced/revanced-manager/commit/60fdec9804c763ef9308a7a56d245401dbd35d7c))
|
||||
* hide tabs when 1 bundle is used ([41268ca](https://github.com/ReVanced/revanced-manager/commit/41268ca80b71f68dbf9523fa7bac34feeec7d011))
|
||||
* hide unfinished pages in release mode ([c199801](https://github.com/ReVanced/revanced-manager/commit/c199801fb7f91306538391177d240cf1121964d2))
|
||||
* Highlight links in Markdown ([7bf8988](https://github.com/ReVanced/revanced-manager/commit/7bf89887e420a402b30da4796ba3648147f00394)), closes [#1962](https://github.com/ReVanced/revanced-manager/issues/1962)
|
||||
* implement DI ([7fa7b9d](https://github.com/ReVanced/revanced-manager/commit/7fa7b9d53a3217c7e1e4c70a524fd68ae170c832))
|
||||
* implement more patch option types ([#2015](https://github.com/ReVanced/revanced-manager/issues/2015)) ([b18c678](https://github.com/ReVanced/revanced-manager/commit/b18c6783547e910fa2dbd3d7edcc5fe329e6d921))
|
||||
* implement navigation ([7fc6ec5](https://github.com/ReVanced/revanced-manager/commit/7fc6ec5c2cf8eb9ebfc3dda01cdfd80962be1f8f))
|
||||
* implement Submit Issue button ([#1276](https://github.com/ReVanced/revanced-manager/issues/1276)) ([a269a39](https://github.com/ReVanced/revanced-manager/commit/a269a39aa4a34b94aef4e1e85126c571e96be575))
|
||||
* improve accessibility ([#64](https://github.com/ReVanced/revanced-manager/issues/64)) ([39b08e5](https://github.com/ReVanced/revanced-manager/commit/39b08e5201d2cec6bdb67f9386120a7a40c9ccc6))
|
||||
* Improve APK file name formatting on save ([#2421](https://github.com/ReVanced/revanced-manager/issues/2421)) ([a53a8ba](https://github.com/ReVanced/revanced-manager/commit/a53a8ba62734daf9bd80ab79265241a4a22f489c))
|
||||
* improve bundle dialog UI ([409c888](https://github.com/ReVanced/revanced-manager/commit/409c888d523f398505daaaff9d2490dc5a863680))
|
||||
* Improve bundle info screen design ([#2548](https://github.com/ReVanced/revanced-manager/issues/2548)) ([55524f7](https://github.com/ReVanced/revanced-manager/commit/55524f7284a44bbf8e8c782eedd7fc06d54944cf))
|
||||
* Improve custom API URL dialog ([#2033](https://github.com/ReVanced/revanced-manager/issues/2033)) ([7dae562](https://github.com/ReVanced/revanced-manager/commit/7dae56281994942577bac7bf50c59e805672d0e1))
|
||||
* Improve device information in debugging section ([d889677](https://github.com/ReVanced/revanced-manager/commit/d889677b29aeb4a49a025da98060265e88876ddf)), closes [#1977](https://github.com/ReVanced/revanced-manager/issues/1977)
|
||||
* Improve initial update popup wording ([5901372](https://github.com/ReVanced/revanced-manager/commit/5901372523643eef5a605256662c8e1f0a9f2263)), closes [#1956](https://github.com/ReVanced/revanced-manager/issues/1956)
|
||||
* improve keystore UI and UX ([#52](https://github.com/ReVanced/revanced-manager/issues/52)) ([49b4bbb](https://github.com/ReVanced/revanced-manager/commit/49b4bbbf0ba84b006a1694ca95662cf224a84b0f))
|
||||
* Improve patch bundle screen ([#2070](https://github.com/ReVanced/revanced-manager/issues/2070)) ([a907528](https://github.com/ReVanced/revanced-manager/commit/a907528a2096d8de9778efa8f85e0cdc1d7c2b80))
|
||||
* improve patcher screen labels ([f4d6c60](https://github.com/ReVanced/revanced-manager/commit/f4d6c60b9ec4c76e8e3fa233f79e062b802860e5))
|
||||
* improve patcher UI ([#1494](https://github.com/ReVanced/revanced-manager/issues/1494)) ([429b428](https://github.com/ReVanced/revanced-manager/commit/429b428f673dd949289baaf27ed2e08970db83ae))
|
||||
* Improve Settings order ([#2060](https://github.com/ReVanced/revanced-manager/issues/2060)) ([fa86c1a](https://github.com/ReVanced/revanced-manager/commit/fa86c1a0bb039a86e0649eae30c7b33620f98dbe))
|
||||
* improve the safeguards ([#2038](https://github.com/ReVanced/revanced-manager/issues/2038)) ([e5b414e](https://github.com/ReVanced/revanced-manager/commit/e5b414e277341967c7b5a5f071ddac1fdfdb8e63))
|
||||
* Improve unsupported patch warnings ([#2066](https://github.com/ReVanced/revanced-manager/issues/2066)) ([3c23d57](https://github.com/ReVanced/revanced-manager/commit/3c23d573bf3998304cad4485016004a871cf1636)), closes [#2052](https://github.com/ReVanced/revanced-manager/issues/2052)
|
||||
* Improve update screen design ([#2487](https://github.com/ReVanced/revanced-manager/issues/2487)) ([7007010](https://github.com/ReVanced/revanced-manager/commit/7007010f14239452e565736fe7cee7666a682ffb))
|
||||
* Improve update setting tile titles ([e2623d6](https://github.com/ReVanced/revanced-manager/commit/e2623d6d79b3b87e9ba29016e42f1d645b2f9e19)), closes [#1968](https://github.com/ReVanced/revanced-manager/issues/1968)
|
||||
* improve UX for failed or missing bundles ([49f8510](https://github.com/ReVanced/revanced-manager/commit/49f851022db72b110c8597aa1c711461c1b01882))
|
||||
* improved compose stability ([8c40119](https://github.com/ReVanced/revanced-manager/commit/8c40119609c650d1f012d810a4117e84fbe2da52))
|
||||
* improved dashboard screen ([5c2f9d9](https://github.com/ReVanced/revanced-manager/commit/5c2f9d91a6e803d9b3705e2b3aa84176353ba963))
|
||||
* in-app updater ([#25](https://github.com/ReVanced/revanced-manager/issues/25)) ([d71a4bf](https://github.com/ReVanced/revanced-manager/commit/d71a4bf3c3457a02578bb8ad3c7615b074f6e3f1))
|
||||
* **installer:** adjust arrow icon size ([e997255](https://github.com/ReVanced/revanced-manager/commit/e997255cf3c3c5ba777da07752217f99e01dd789))
|
||||
* **installer:** adjust step icon size and alignment ([cfcabf6](https://github.com/ReVanced/revanced-manager/commit/cfcabf6ef1c212f2627d5d02f4d59981bdc276ca))
|
||||
* **installer:** apk signing and installation ([da32ff9](https://github.com/ReVanced/revanced-manager/commit/da32ff954a84cf8ff321bbbf71cc5b544d6e6be9))
|
||||
* **installer:** sign apk in patcher worker ([c003c3c](https://github.com/ReVanced/revanced-manager/commit/c003c3c3245f5a663a0371d4e9df71777ba728b9))
|
||||
* **Installer:** use BottomAppBar ([#1428](https://github.com/ReVanced/revanced-manager/issues/1428)) ([ceb7623](https://github.com/ReVanced/revanced-manager/commit/ceb762379461443e7e62c37511df1c84a6068bb4))
|
||||
* integrate revanced patcher ([#22](https://github.com/ReVanced/revanced-manager/issues/22)) ([caeabfc](https://github.com/ReVanced/revanced-manager/commit/caeabfc91b2aa7e3de9e6a31859049d4b2d37388))
|
||||
* keystore import/export ([#30](https://github.com/ReVanced/revanced-manager/issues/30)) ([fd0ec6c](https://github.com/ReVanced/revanced-manager/commit/fd0ec6c6a7fc8488db859056a95ebe0455e2843b))
|
||||
* **koin:** use the android logger ([f30333e](https://github.com/ReVanced/revanced-manager/commit/f30333e75338dd2c1ef891723ecb834fc1eb10f7))
|
||||
* licenses screen ([#47](https://github.com/ReVanced/revanced-manager/issues/47)) ([e3cb056](https://github.com/ReVanced/revanced-manager/commit/e3cb056858ea8917162c1a421a7a8d03ddaa08e2))
|
||||
* make bundles selectable ([#1237](https://github.com/ReVanced/revanced-manager/issues/1237)) ([a246863](https://github.com/ReVanced/revanced-manager/commit/a246863a89fe8781feaf2a45fcb7ea991d26028f))
|
||||
* Make patch bundles list scrollable ([#2322](https://github.com/ReVanced/revanced-manager/issues/2322)) ([a5c8a23](https://github.com/ReVanced/revanced-manager/commit/a5c8a23f9ffb36543d45b46bb5f01c5dea56bf90))
|
||||
* more info for the select from application screen ([#81](https://github.com/ReVanced/revanced-manager/issues/81)) ([3f446f8](https://github.com/ReVanced/revanced-manager/commit/3f446f8236101755a9d51a2aa759f70a0bd429da))
|
||||
* move plugin api to another repository ([55e7ebf](https://github.com/ReVanced/revanced-manager/commit/55e7ebf4fc5adf8800430ad4aa2579cb6210290d))
|
||||
* Move safeguards above patcher preference group ([9f7eaa2](https://github.com/ReVanced/revanced-manager/commit/9f7eaa212339f2093050087dc7ab0b8237356939))
|
||||
* move update to notification card ([#1917](https://github.com/ReVanced/revanced-manager/issues/1917)) ([b80f94b](https://github.com/ReVanced/revanced-manager/commit/b80f94b77bba89e31608cdb302dab0619bf7c5cc))
|
||||
* **NotificationCard:** rewrite & consistent usage ([#1426](https://github.com/ReVanced/revanced-manager/issues/1426)) ([f8aafa0](https://github.com/ReVanced/revanced-manager/commit/f8aafa050328423b3168a7943f566fce58100cb0))
|
||||
* Open the app-specific manage all files permission dialog ([#2148](https://github.com/ReVanced/revanced-manager/issues/2148)) ([a3f31ea](https://github.com/ReVanced/revanced-manager/commit/a3f31ea65788a43ce57d548e8240e5b1fe3005d0))
|
||||
* Order bundles by number of patches ([bb5d414](https://github.com/ReVanced/revanced-manager/commit/bb5d414abb4f294aa88d795486836a99ade2b388))
|
||||
* patch bundle sources system ([#24](https://github.com/ReVanced/revanced-manager/issues/24)) ([9675a27](https://github.com/ReVanced/revanced-manager/commit/9675a2777b364e5ede0d44b92eb7e551d4f7b3d6))
|
||||
* patch options ([#45](https://github.com/ReVanced/revanced-manager/issues/45)) ([8540d30](https://github.com/ReVanced/revanced-manager/commit/8540d301962669e3d79ca345c852f5b01df641a4))
|
||||
* patch options UI ([#80](https://github.com/ReVanced/revanced-manager/issues/80)) ([0a1acd2](https://github.com/ReVanced/revanced-manager/commit/0a1acd24e3f0d06fde412b8eeecd923d92ee64a9))
|
||||
* **patch-selector:** default patches selection ([#1272](https://github.com/ReVanced/revanced-manager/issues/1272)) ([a17c2de](https://github.com/ReVanced/revanced-manager/commit/a17c2de228cccb4a0bb0ca7497720011bec131fc))
|
||||
* **patch-selector:** remove TODO about an unplanned feature ([4924eae](https://github.com/ReVanced/revanced-manager/commit/4924eaef800c429f2a59b8a15fd48fae0292810c))
|
||||
* **patcher:** Improve installation ([#2185](https://github.com/ReVanced/revanced-manager/issues/2185)) ([3bd4f0d](https://github.com/ReVanced/revanced-manager/commit/3bd4f0d8f3f60d079d4647d42592b10a15f0dae8))
|
||||
* patches selector screen ([55e871a](https://github.com/ReVanced/revanced-manager/commit/55e871aa7d27885e44ef33faab1bb4ae33e7a460))
|
||||
* Progressive AlertDialog for adding bundles ([9a01273](https://github.com/ReVanced/revanced-manager/commit/9a01273c43bd6bcdb0cdfd26c5a467cd3193e5d7)), closes [#1992](https://github.com/ReVanced/revanced-manager/issues/1992)
|
||||
* ProGuard ([d84e6a3](https://github.com/ReVanced/revanced-manager/commit/d84e6a3ffc20d018b2edeb505de20a920785ba5c))
|
||||
* Purple default theme ([#1601](https://github.com/ReVanced/revanced-manager/issues/1601)) ([0616666](https://github.com/ReVanced/revanced-manager/commit/0616666d5ef9b53bef5fd630b1b1a47088097d37))
|
||||
* Redesign the patches screen ([#2381](https://github.com/ReVanced/revanced-manager/issues/2381)) ([8dc4e5b](https://github.com/ReVanced/revanced-manager/commit/8dc4e5b89ee4d36263c8b4187650691b68484688))
|
||||
* remember patch options ([#1449](https://github.com/ReVanced/revanced-manager/issues/1449)) ([90db765](https://github.com/ReVanced/revanced-manager/commit/90db765c9aa014495775a34927904dedf5fef1e3))
|
||||
* remove dead help icons ([3bb071d](https://github.com/ReVanced/revanced-manager/commit/3bb071d80d319d4943b0d4c3048f232f3eb9f5cf))
|
||||
* Remove tag from changelog ([d2119d3](https://github.com/ReVanced/revanced-manager/commit/d2119d36430198151140b469192f76f781df6dd3))
|
||||
* Rename "Patch bundle" to "Patches" ([#2541](https://github.com/ReVanced/revanced-manager/issues/2541)) ([2cdd6d1](https://github.com/ReVanced/revanced-manager/commit/2cdd6d1843f1e49c7c720f8859e11d6a30c0eea6))
|
||||
* rename debug build to `ReVanced Manager (dev)` ([d3417ad](https://github.com/ReVanced/revanced-manager/commit/d3417adbeba0a8e06d3494a2fd108f735f73632c))
|
||||
* rename main bundle to `Default` ([e44d3fd](https://github.com/ReVanced/revanced-manager/commit/e44d3fdee444d915e3e8b8143e55f1353980aad2))
|
||||
* rename package to `app.revanced.manager` ([5ec97f4](https://github.com/ReVanced/revanced-manager/commit/5ec97f4a852a07d0e554bbe1eacc379179ac089e))
|
||||
* Rename strings ([e127845](https://github.com/ReVanced/revanced-manager/commit/e1278452b9c73479cdfb0eb0703db1552b158633))
|
||||
* rename ViewModels for consistency ([064a54e](https://github.com/ReVanced/revanced-manager/commit/064a54eaf0675a1cc9d21f3e1071160deb25c201))
|
||||
* Reorder Import & Export settings ([#2403](https://github.com/ReVanced/revanced-manager/issues/2403)) ([2697077](https://github.com/ReVanced/revanced-manager/commit/2697077fc88bb795027303558c9d52448a4daded))
|
||||
* ReVanced theme colors ([59b894d](https://github.com/ReVanced/revanced-manager/commit/59b894dce4b99c51151a4cccd03a998ceec31778))
|
||||
* revert to blue theme colors ([5f4c958](https://github.com/ReVanced/revanced-manager/commit/5f4c9584a94a1edd1eeaa0b9ecfcd9b281b7cccc))
|
||||
* root installation ([#1243](https://github.com/ReVanced/revanced-manager/issues/1243)) ([62e934c](https://github.com/ReVanced/revanced-manager/commit/62e934c4032096bed36201510fc55304ba48de68))
|
||||
* save patch options and selected patches in bundle ([#50](https://github.com/ReVanced/revanced-manager/issues/50)) ([23162f6](https://github.com/ReVanced/revanced-manager/commit/23162f6233fa6a176514b35feff731f8f28b4d4b))
|
||||
* save patch selection using room db ([#38](https://github.com/ReVanced/revanced-manager/issues/38)) ([1efccda](https://github.com/ReVanced/revanced-manager/commit/1efccda3f55d964fae3bee9ee1f0bd260bb1cc74))
|
||||
* Screen slide transition ([#2396](https://github.com/ReVanced/revanced-manager/issues/2396)) ([2de16e1](https://github.com/ReVanced/revanced-manager/commit/2de16e18e8ba5e84149b377f225693ea35fa2385))
|
||||
* Scrollbars ([#1479](https://github.com/ReVanced/revanced-manager/issues/1479)) ([b5558ea](https://github.com/ReVanced/revanced-manager/commit/b5558ea3ffef40f96b271f8dfe3a5cf95328781e))
|
||||
* Select bundle type before adding bundle ([#1490](https://github.com/ReVanced/revanced-manager/issues/1490)) ([88e860c](https://github.com/ReVanced/revanced-manager/commit/88e860cf0132aed23a3cfd3d9d12e472aa895718))
|
||||
* selected app info page ([#1395](https://github.com/ReVanced/revanced-manager/issues/1395)) ([b69a369](https://github.com/ReVanced/revanced-manager/commit/b69a369d4e304c8a4c8a8db052309b485171e353))
|
||||
* Set app ownership when installing apps ([#2558](https://github.com/ReVanced/revanced-manager/issues/2558)) ([7c410fe](https://github.com/ReVanced/revanced-manager/commit/7c410fef4512087657e3978d5be049c422b25456))
|
||||
* settings migration (compose) ([#1309](https://github.com/ReVanced/revanced-manager/issues/1309)) ([bf1d628](https://github.com/ReVanced/revanced-manager/commit/bf1d628944cb5a439d0bda7c49d820a5fa7576b3))
|
||||
* settings screen ([b7d53cf](https://github.com/ReVanced/revanced-manager/commit/b7d53cfca84d7239bed9189e265a03fd44dc2e45))
|
||||
* **settings screen:** add battery optimization notification ([5754864](https://github.com/ReVanced/revanced-manager/commit/57548641e7ecd06decfc926cb860674ce7443d7a))
|
||||
* **settings screen:** match typography from figma ([948a6d1](https://github.com/ReVanced/revanced-manager/commit/948a6d14404e067907c9e84576cfeba76134aaf6))
|
||||
* **settings:** move experimental patches option to advanced ([805d440](https://github.com/ReVanced/revanced-manager/commit/805d440450d821a26d3ef90a4f97cd796635057d))
|
||||
* **Settings:** use SettingsListItem consistently and overall improvements ([#1427](https://github.com/ReVanced/revanced-manager/issues/1427)) ([5e35893](https://github.com/ReVanced/revanced-manager/commit/5e35893883fa109d74b028478e60b51f97a2e12d))
|
||||
* show installed app in version selector ([1ab1e46](https://github.com/ReVanced/revanced-manager/commit/1ab1e4682ffbfe16c02c438ad833adbfdec58b33))
|
||||
* Show manager update dialog ([#2069](https://github.com/ReVanced/revanced-manager/issues/2069)) ([113a74d](https://github.com/ReVanced/revanced-manager/commit/113a74d270c1c222d4d06049b4edda8f27724a20)), closes [#1963](https://github.com/ReVanced/revanced-manager/issues/1963) [#1958](https://github.com/ReVanced/revanced-manager/issues/1958)
|
||||
* show stacktrace in installer ui ([#36](https://github.com/ReVanced/revanced-manager/issues/36)) ([8d53180](https://github.com/ReVanced/revanced-manager/commit/8d53180d86e6e9d9c8a4056a5fde0603f17e3157))
|
||||
* show toast when no patches are selected ([8aa70d3](https://github.com/ReVanced/revanced-manager/commit/8aa70d350e07aae8b4a22b6bc6fb90c0f6227acd))
|
||||
* splash screen ([60a5a11](https://github.com/ReVanced/revanced-manager/commit/60a5a11c71634aeda414c2ed85f7706ba3deefe1))
|
||||
* store patched apps ([#79](https://github.com/ReVanced/revanced-manager/issues/79)) ([b14285b](https://github.com/ReVanced/revanced-manager/commit/b14285b2c83e60376ad42fa6ea508257cd04d47d))
|
||||
* switch to androidx.navigation ([#2362](https://github.com/ReVanced/revanced-manager/issues/2362)) ([7438f45](https://github.com/ReVanced/revanced-manager/commit/7438f45903ec6ed3436a895d4c32d34d41b00010))
|
||||
* switch to Preferences DataStore ([#60](https://github.com/ReVanced/revanced-manager/issues/60)) ([1852799](https://github.com/ReVanced/revanced-manager/commit/18527999b5f8752faf36c145276d51e2e095c8ee))
|
||||
* switch to revanced api v4 ([7e858a2](https://github.com/ReVanced/revanced-manager/commit/7e858a244cc4038bdb029c4418278700f6a6490f))
|
||||
* switch to the new api ([#75](https://github.com/ReVanced/revanced-manager/issues/75)) ([a55160e](https://github.com/ReVanced/revanced-manager/commit/a55160e7c619ec5541de72fa80f079c9bc94d2d5))
|
||||
* TopAppBar scroll behavior ([#2397](https://github.com/ReVanced/revanced-manager/issues/2397)) ([dc51d61](https://github.com/ReVanced/revanced-manager/commit/dc51d6134dae0fdc415f66e2716c6bffa35dfdb5))
|
||||
* **Update Screen:** changelogs & handle states ([#1464](https://github.com/ReVanced/revanced-manager/issues/1464)) ([3af26e7](https://github.com/ReVanced/revanced-manager/commit/3af26e706571339a3c69688098a51616549c58a8))
|
||||
* **update screen:** complete main update screen ([553af83](https://github.com/ReVanced/revanced-manager/commit/553af831393d7276088ceb0b0a854ec654f72def))
|
||||
* updater changelogs ([#48](https://github.com/ReVanced/revanced-manager/issues/48)) ([6dbcd62](https://github.com/ReVanced/revanced-manager/commit/6dbcd6293e94d8d20cccc401b0edeb1d7047553e))
|
||||
* updater UI and code improvements ([#1597](https://github.com/ReVanced/revanced-manager/issues/1597)) ([a12cae7](https://github.com/ReVanced/revanced-manager/commit/a12cae72998d85138dcf29c0e5d430359e338d5e))
|
||||
* Use "Debug" and "Debug signed" for build names respectively ([5133f02](https://github.com/ReVanced/revanced-manager/commit/5133f02ad61b85af28608c7180b7a2accb4811ab))
|
||||
* Use correct casing in module description ([59b4c0b](https://github.com/ReVanced/revanced-manager/commit/59b4c0b2d2e426dfe66b5a01d219b57bb0df5b8b))
|
||||
* use revanced api for changelogs ([686eb40](https://github.com/ReVanced/revanced-manager/commit/686eb40cb0f8b8d785732dd2bc82d17b5a4fd042))
|
||||
* Use simpler strings ([83d33e8](https://github.com/ReVanced/revanced-manager/commit/83d33e87e3f89cb3efce63dcabcde6478f69b8e7))
|
||||
* View bundle patches ([#2065](https://github.com/ReVanced/revanced-manager/issues/2065)) ([089f200](https://github.com/ReVanced/revanced-manager/commit/089f200fe6ff59020a87883a47ef20a0c4c08565))
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* downgrade Kotlin to 1.8.21 ([fc90bbc](https://github.com/ReVanced/revanced-manager/commit/fc90bbc27ce765e0b55bb5ac9132e58f46aee9aa))
|
||||
@@ -1,334 +0,0 @@
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateMode
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateRule
|
||||
import io.github.z4kn4fein.semver.toVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import kotlin.random.Random
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.devtools)
|
||||
alias(libs.plugins.about.libraries)
|
||||
alias(libs.plugins.about.libraries.android)
|
||||
signing
|
||||
}
|
||||
|
||||
val outputApkFileName = "${rootProject.name}-$version.apk"
|
||||
|
||||
dependencies {
|
||||
// AndroidX Core
|
||||
implementation(libs.androidx.ktx)
|
||||
implementation(libs.runtime.ktx)
|
||||
implementation(libs.runtime.compose)
|
||||
implementation(libs.splash.screen)
|
||||
implementation(libs.activity.compose)
|
||||
implementation(libs.work.runtime.ktx)
|
||||
implementation(libs.preferences.datastore)
|
||||
implementation(libs.appcompat)
|
||||
|
||||
// Compose
|
||||
implementation(platform(libs.compose.bom))
|
||||
implementation(libs.compose.ui)
|
||||
implementation(libs.compose.ui.preview)
|
||||
implementation(libs.compose.ui.tooling)
|
||||
implementation(libs.compose.livedata)
|
||||
implementation(libs.compose.material.icons.extended)
|
||||
implementation(libs.compose.material3)
|
||||
implementation(libs.navigation.compose)
|
||||
|
||||
// Accompanist
|
||||
implementation(libs.accompanist.drawablepainter)
|
||||
|
||||
// Placeholder
|
||||
implementation(libs.placeholder.material3)
|
||||
|
||||
// Coil (async image loading, network image)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.appiconloader)
|
||||
|
||||
// KotlinX
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlinx.collection.immutable)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
|
||||
// Room
|
||||
implementation(libs.room.runtime)
|
||||
implementation(libs.room.ktx)
|
||||
annotationProcessor(libs.room.compiler)
|
||||
ksp(libs.room.compiler)
|
||||
|
||||
// ReVanced
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.revanced.library)
|
||||
|
||||
// Downloader plugins
|
||||
implementation(project(":api"))
|
||||
|
||||
// Native processes
|
||||
implementation(libs.kotlin.process)
|
||||
|
||||
// HiddenAPI
|
||||
compileOnly(libs.hidden.api.stub)
|
||||
|
||||
// LibSU
|
||||
implementation(libs.libsu.core)
|
||||
implementation(libs.libsu.service)
|
||||
implementation(libs.libsu.nio)
|
||||
|
||||
// Koin
|
||||
implementation(libs.koin.android)
|
||||
implementation(libs.koin.compose)
|
||||
implementation(libs.koin.compose.navigation)
|
||||
implementation(libs.koin.workmanager)
|
||||
|
||||
// Licenses
|
||||
implementation(libs.about.libraries.core)
|
||||
implementation(libs.about.libraries.m3)
|
||||
|
||||
// Ktor
|
||||
implementation(libs.ktor.core)
|
||||
implementation(libs.ktor.logging)
|
||||
implementation(libs.ktor.okhttp)
|
||||
implementation(libs.ktor.content.negotiation)
|
||||
implementation(libs.ktor.serialization)
|
||||
|
||||
// Markdown
|
||||
implementation(libs.markdown.renderer)
|
||||
|
||||
// Fading Edges
|
||||
implementation(libs.fading.edges)
|
||||
|
||||
// Scrollbars
|
||||
implementation(libs.scrollbars)
|
||||
|
||||
// EnumUtil
|
||||
implementation(libs.enumutil)
|
||||
ksp(libs.enumutil.ksp)
|
||||
|
||||
// Reorderable lists
|
||||
implementation(libs.reorderable)
|
||||
|
||||
// Compose Icons
|
||||
implementation(libs.compose.icons.fontawesome)
|
||||
|
||||
// Ackpine
|
||||
implementation(libs.ackpine.core)
|
||||
implementation(libs.ackpine.ktx)
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
// Semantic versioning string parser
|
||||
classpath(libs.semver.parser)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.manager"
|
||||
compileSdk = 36
|
||||
buildToolsVersion = "35.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "app.revanced.manager.flutter"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
|
||||
val versionStr = if (version == "unspecified") "1.0.0" else version.toString()
|
||||
versionName = versionStr
|
||||
versionCode = with(versionStr.toVersion()) {
|
||||
major * 100_000_000 +
|
||||
minor * 100_000 +
|
||||
patch * 100 +
|
||||
(preRelease?.substringAfterLast('.')?.toInt() ?: 99)
|
||||
}
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
val resDir = file("src/main/res")
|
||||
val locales = resDir.listFiles()
|
||||
.orEmpty()
|
||||
//noinspection WrongGradleMethod
|
||||
.filter { it.isDirectory && it.name.matches(Regex("values-[a-z]{2}(-r[A-Z]{2})?")) }
|
||||
//noinspection WrongGradleMethod
|
||||
.map { it.name.removePrefix("values-").replace("-r", "-") }
|
||||
.sorted()
|
||||
//noinspection WrongGradleMethod
|
||||
.joinToString(prefix = "{", separator = ",", postfix = "}") { "\"$it\"" }
|
||||
|
||||
buildConfigField("String[]", "SUPPORTED_LOCALES", locales)
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix = ".debug"
|
||||
resValue("string", "app_name", "ReVanced Manager (Debug)")
|
||||
|
||||
buildConfigField("long", "BUILD_ID", "${Random.nextLong()}L")
|
||||
}
|
||||
|
||||
release {
|
||||
if (!project.hasProperty("noProguard")) {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
|
||||
val keystoreFile = file("keystore.jks")
|
||||
|
||||
if (project.hasProperty("signAsDebug") || !keystoreFile.exists()) {
|
||||
applicationIdSuffix = ".debug_signed"
|
||||
resValue("string", "app_name", "ReVanced Manager (Debug signed)")
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
|
||||
isPseudoLocalesEnabled = true
|
||||
} else {
|
||||
signingConfig = signingConfigs.create("release") {
|
||||
storeFile = keystoreFile
|
||||
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||||
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
||||
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildConfigField("long", "BUILD_ID", "0L")
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
outputs.all {
|
||||
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
||||
|
||||
outputFileName = outputApkFileName
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
// Useless files
|
||||
excludes += "/XPP3_*_VERSION"
|
||||
excludes += "/font-awesome-license.txt"
|
||||
excludes += "/smali.properties"
|
||||
excludes += "/baksmali.properties"
|
||||
excludes += "/properties/apktool.properties"
|
||||
excludes += "/org/antlr/**"
|
||||
excludes += "/org/mockito/**"
|
||||
excludes += "/org/bouncycastle/pqc/**.properties"
|
||||
excludes += "/org/bouncycastle/x509/**.properties"
|
||||
excludes += "/META-INF/INDEX.LIST"
|
||||
excludes += "/META-INF/**/*.txt"
|
||||
excludes += "/META-INF/**/*.properties"
|
||||
excludes += "/META-INF/DEPENDENCIES"
|
||||
|
||||
// Desktop AAPT binaries
|
||||
excludes += "/prebuilt/**"
|
||||
}
|
||||
jniLibs {
|
||||
// 32-bit x86 is dead
|
||||
excludes += "/lib/x86/*.so"
|
||||
|
||||
// Equivalent of AndroidManifest's extractNativeLibs=true to ensure libs are compressed
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().withBuildType("release")) {
|
||||
it.packaging.resources.excludes.apply {
|
||||
// Debug metadata
|
||||
add("/META-INF/*.version")
|
||||
add("/META-INF/*.kotlin_module")
|
||||
add("/kotlin-tooling-metadata.json")
|
||||
|
||||
// Kotlin debugging (https://github.com/Kotlin/kotlinx.coroutines/issues/2274)
|
||||
add("/DebugProbesKt.bin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
freeCompilerArgs.addAll(
|
||||
"-Xexplicit-backing-fields",
|
||||
"-Xcontext-parameters",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
all {
|
||||
// ReVanced Library has a dependency which conflicts with whatever this is. We don't use protobuf, so it should be fine.
|
||||
exclude(group = "com.google.api.grpc", module = "proto-google-common-protos")
|
||||
}
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
library {
|
||||
// Enable the duplication mode, allows to merge, or link dependencies which relate
|
||||
duplicationMode = DuplicateMode.MERGE
|
||||
// Configure the duplication rule, to match "duplicates" with
|
||||
duplicationRule = DuplicateRule.EXACT
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
// Needed by gradle-semantic-release-plugin.
|
||||
// Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435.
|
||||
val publish by registering {
|
||||
group = "publishing"
|
||||
description = "Build the release APK"
|
||||
|
||||
dependsOn("assembleRelease")
|
||||
|
||||
val apk = project.layout.buildDirectory.file("outputs/apk/release/${outputApkFileName}")
|
||||
val ascFile = apk.map { it.asFile.resolveSibling("${it.asFile.name}.asc") }
|
||||
|
||||
inputs.file(apk).withPropertyName("inputApk")
|
||||
outputs.file(ascFile).withPropertyName("outputAsc")
|
||||
|
||||
doLast {
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(apk.get().asFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
version = 1.26.0-dev.26
|
||||
1
app/gradlew
vendored
@@ -1 +0,0 @@
|
||||
../gradlew
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "app",
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@anolilab/multi-semantic-release": "^1.1.10",
|
||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1"
|
||||
}
|
||||
}
|
||||
17
app/proguard-rules.pro
vendored
@@ -1,17 +0,0 @@
|
||||
-dontobfuscate
|
||||
|
||||
-keep class app.revanced.manager.patcher.runtime.process.* { *; }
|
||||
-keep class app.revanced.manager.downloader.** { *; }
|
||||
-keep class app.revanced.patcher.** { *; }
|
||||
-keep class com.android.tools.smali.** { *; }
|
||||
-keep class kotlin.** { *; }
|
||||
-keepnames class com.android.apksig.internal.** { *; }
|
||||
-keepnames class org.xmlpull.** { *; }
|
||||
|
||||
-dontwarn com.google.j2objc.annotations.*
|
||||
-dontwarn java.awt.**
|
||||
-dontwarn javax.**
|
||||
-dontwarn org.slf4j.**
|
||||
|
||||
-dontwarn org.immutables.value.Generated
|
||||
-dontwarn reactor.blockhound.integration.BlockHoundIntegration
|
||||
@@ -1,417 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "c0060d66d8afa086f2632c4eb145ffc4",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "patch_bundles",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `version` TEXT, `source` TEXT NOT NULL, `auto_update` INTEGER NOT NULL, PRIMARY KEY(`uid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uid",
|
||||
"columnName": "uid",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "versionHash",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "source",
|
||||
"columnName": "source",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "autoUpdate",
|
||||
"columnName": "auto_update",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "patch_selections",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `patch_bundle` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`patch_bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uid",
|
||||
"columnName": "uid",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "patchBundle",
|
||||
"columnName": "patch_bundle",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"uid"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_patch_selections_patch_bundle_package_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"patch_bundle",
|
||||
"package_name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_selections_patch_bundle_package_name` ON `${TABLE_NAME}` (`patch_bundle`, `package_name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "patch_bundles",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"patch_bundle"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "selected_patches",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selection` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`selection`, `patch_name`), FOREIGN KEY(`selection`) REFERENCES `patch_selections`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "selection",
|
||||
"columnName": "selection",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "patchName",
|
||||
"columnName": "patch_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"selection",
|
||||
"patch_name"
|
||||
]
|
||||
},
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "patch_selections",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"selection"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "downloaded_app",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `directory` TEXT NOT NULL, `last_used` INTEGER NOT NULL, PRIMARY KEY(`package_name`, `version`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "version",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "directory",
|
||||
"columnName": "directory",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastUsed",
|
||||
"columnName": "last_used",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"package_name",
|
||||
"version"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "installed_app",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`current_package_name` TEXT NOT NULL, `original_package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `install_type` TEXT NOT NULL, PRIMARY KEY(`current_package_name`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "currentPackageName",
|
||||
"columnName": "current_package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalPackageName",
|
||||
"columnName": "original_package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "version",
|
||||
"columnName": "version",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "installType",
|
||||
"columnName": "install_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"current_package_name"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "applied_patch",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `bundle` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`package_name`, `bundle`, `patch_name`), FOREIGN KEY(`package_name`) REFERENCES `installed_app`(`current_package_name`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bundle",
|
||||
"columnName": "bundle",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "patchName",
|
||||
"columnName": "patch_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"package_name",
|
||||
"bundle",
|
||||
"patch_name"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_applied_patch_bundle",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"bundle"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_applied_patch_bundle` ON `${TABLE_NAME}` (`bundle`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "installed_app",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"package_name"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"current_package_name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "patch_bundles",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"bundle"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "option_groups",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `patch_bundle` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`patch_bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uid",
|
||||
"columnName": "uid",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "patchBundle",
|
||||
"columnName": "patch_bundle",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"uid"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_option_groups_patch_bundle_package_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"patch_bundle",
|
||||
"package_name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_option_groups_patch_bundle_package_name` ON `${TABLE_NAME}` (`patch_bundle`, `package_name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "patch_bundles",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"patch_bundle"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "options",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, `key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`group`, `patch_name`, `key`), FOREIGN KEY(`group`) REFERENCES `option_groups`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "group",
|
||||
"columnName": "group",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "patchName",
|
||||
"columnName": "patch_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "key",
|
||||
"columnName": "key",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"group",
|
||||
"patch_name",
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "option_groups",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"group"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"uid"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "trusted_downloaders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `signature` BLOB NOT NULL, PRIMARY KEY(`package_name`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "packageName",
|
||||
"columnName": "package_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "signature",
|
||||
"columnName": "signature",
|
||||
"affinity": "BLOB",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"package_name"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c0060d66d8afa086f2632c4eb145ffc4')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<permission
|
||||
android:name="app.revanced.manager.permission.DOWNLOADER_HOST"
|
||||
android:protectionLevel="signature"
|
||||
android:label="@string/downloader_host_permission_label"
|
||||
android:description="@string/downloader_host_permission_description"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="app.revanced.manager.permission.DOWNLOADER_HOST" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ENFORCE_UPDATE_OWNERSHIP" />
|
||||
|
||||
<application
|
||||
android:name=".ManagerApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:largeHeap="true"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.ReVancedManager"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
tools:targetApi="34">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.ReVancedManager">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".downloader.webview.WebViewActivity" android:exported="false" android:theme="@style/Theme.WebViewActivity" />
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="patching"
|
||||
/>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<meta-data
|
||||
android:name="androidx.work.WorkManagerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<meta-data
|
||||
android:name="ru.solrudev.ackpine.AckpineInitializer"
|
||||
tools:node="remove" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,8 +0,0 @@
|
||||
// IRootService.aidl
|
||||
package app.revanced.manager;
|
||||
|
||||
// Declare any non-default types here with import statements
|
||||
|
||||
interface IRootSystemService {
|
||||
IBinder getFileSystemService();
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// ProgressEventParcel.aidl
|
||||
package app.revanced.manager.patcher;
|
||||
|
||||
parcelable ProgressEventParcel;
|
||||
@@ -1,12 +0,0 @@
|
||||
// IPatcherEvents.aidl
|
||||
package app.revanced.manager.patcher.runtime.process;
|
||||
|
||||
import app.revanced.manager.patcher.ProgressEventParcel;
|
||||
|
||||
// Interface for sending events back to the main app process.
|
||||
oneway interface IPatcherEvents {
|
||||
void log(String level, String msg);
|
||||
void event(in ProgressEventParcel event);
|
||||
// The patching process has ended. The exceptionStackTrace is null if it finished successfully.
|
||||
void finished(String exceptionStackTrace);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// IPatcherProcess.aidl
|
||||
package app.revanced.manager.patcher.runtime.process;
|
||||
|
||||
import app.revanced.manager.patcher.runtime.process.Parameters;
|
||||
import app.revanced.manager.patcher.runtime.process.IPatcherEvents;
|
||||
|
||||
interface IPatcherProcess {
|
||||
// Returns BuildConfig.BUILD_ID, which is used to ensure the main app and runner process are running the same code.
|
||||
long buildId();
|
||||
// Makes the patcher process exit with code 0
|
||||
oneway void exit();
|
||||
// Starts patching.
|
||||
oneway void start(in Parameters parameters, IPatcherEvents events);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// Parameters.aidl
|
||||
package app.revanced.manager.patcher.runtime.process;
|
||||
|
||||
parcelable Parameters;
|
||||
@@ -1,6 +0,0 @@
|
||||
id=__PKG_NAME__-ReVanced
|
||||
name=__LABEL__ ReVanced
|
||||
version=__VERSION__
|
||||
versionCode=0
|
||||
author=ReVanced
|
||||
description=Mounts the patched APK on top of the original one
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
DIR=${0%/*}
|
||||
|
||||
package_name="__PKG_NAME__"
|
||||
version="__VERSION__"
|
||||
|
||||
rm "$DIR/log"
|
||||
|
||||
{
|
||||
|
||||
until [ "$(getprop sys.boot_completed)" = 1 ]; do sleep 5; done
|
||||
sleep 5
|
||||
|
||||
base_path="$DIR/$package_name.apk"
|
||||
stock_path="$(pm path "$package_name" | grep base | sed 's/package://g')"
|
||||
stock_version="$(dumpsys package "$package_name" | grep versionName | cut -d "=" -f2)"
|
||||
|
||||
echo "base_path: $base_path"
|
||||
echo "stock_path: $stock_path"
|
||||
echo "base_version: $version"
|
||||
echo "stock_version: $stock_version"
|
||||
|
||||
if mount | grep -q "$stock_path" ; then
|
||||
echo "Not mounting as stock path is already mounted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$version" != "$stock_version" ]; then
|
||||
echo "Not mounting as versions don't match"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$stock_path" ]; then
|
||||
echo "Not mounting as app info could not be loaded"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mount -o bind "$base_path" "$stock_path"
|
||||
|
||||
} >> "$DIR/log"
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
||||
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
||||
|
||||
# Sets the minimum CMake version required for this project.
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
||||
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
||||
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
||||
# build script scope).
|
||||
project("prop_override")
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
#
|
||||
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
||||
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
||||
# is preferred for the same purpose.
|
||||
#
|
||||
# In order to load a library into your app from Java/Kotlin, you must call
|
||||
# System.loadLibrary() and pass the name of the library defined here;
|
||||
# for GameActivity/NativeActivity derived applications, the same library name must be
|
||||
# used in the AndroidManifest.xml file.
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
||||
prop_override.cpp)
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link libraries from various origins, such as libraries defined in this
|
||||
# build script, prebuilt third-party libraries, or Android system libraries.
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
# List libraries link to the target library
|
||||
android
|
||||
log)
|
||||
@@ -1,62 +0,0 @@
|
||||
// Library for overriding Android system properties via environment variables.
|
||||
//
|
||||
// Usage: LD_PRELOAD=prop_override.so PROP_dalvik.vm.heapsize=123M getprop dalvik.vm.heapsize
|
||||
// Output: 123M
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
|
||||
// Source: https://android.googlesource.com/platform/system/core/+/100b08a848d018eeb1caa5d5e7c7c2aaac65da15/libcutils/include/cutils/properties.h
|
||||
#define PROP_VALUE_MAX 92
|
||||
// This is the mangled name of "android::base::GetProperty".
|
||||
#define GET_PROPERTY_MANGLED_NAME "_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_"
|
||||
|
||||
extern "C" typedef int (*property_get_ptr)(const char *, char *, const char *);
|
||||
typedef std::string (*GetProperty_ptr)(const std::string &, const std::string &);
|
||||
|
||||
char *GetPropOverride(const std::string &key) {
|
||||
auto envKey = "PROP_" + key;
|
||||
|
||||
return getenv(envKey.c_str());
|
||||
}
|
||||
|
||||
// See: https://android.googlesource.com/platform/system/core/+/100b08a848d018eeb1caa5d5e7c7c2aaac65da15/libcutils/properties.cpp
|
||||
extern "C" int property_get(const char *key, char *value, const char *default_value) {
|
||||
auto replacement = GetPropOverride(std::string(key));
|
||||
if (replacement) {
|
||||
int len = strnlen(replacement, PROP_VALUE_MAX);
|
||||
|
||||
strncpy(value, replacement, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static property_get_ptr original = NULL;
|
||||
if (!original) {
|
||||
// Get the address of the original function.
|
||||
original = reinterpret_cast<property_get_ptr>(dlsym(RTLD_NEXT, "property_get"));
|
||||
}
|
||||
|
||||
return original(key, value, default_value);
|
||||
}
|
||||
|
||||
// Defining android::base::GetProperty ourselves won't work because std::string has a slightly different "path" in the NDK version of the C++ standard library.
|
||||
// We can get around this by forcing the function to adopt a specific name using the asm keyword.
|
||||
std::string GetProperty(const std::string &, const std::string &) asm(GET_PROPERTY_MANGLED_NAME);
|
||||
|
||||
|
||||
// See: https://android.googlesource.com/platform/system/libbase/+/1a34bb67c4f3ba0a1ea6f4f20ac9fe117ba4fe64/properties.cpp
|
||||
// This isn't used for the properties we want to override, but property_get is deprecated so that could change in the future.
|
||||
std::string GetProperty(const std::string &key, const std::string &default_value) {
|
||||
auto replacement = GetPropOverride(key);
|
||||
if (replacement) {
|
||||
return std::string(replacement);
|
||||
}
|
||||
|
||||
static GetProperty_ptr original = NULL;
|
||||
if (!original) {
|
||||
original = reinterpret_cast<GetProperty_ptr>(dlsym(RTLD_NEXT, GET_PROPERTY_MANGLED_NAME));
|
||||
}
|
||||
|
||||
return original(key, default_value);
|
||||
}
|
||||
@@ -1,350 +0,0 @@
|
||||
package app.revanced.manager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import app.revanced.manager.ui.model.navigation.Announcement
|
||||
import app.revanced.manager.ui.model.navigation.Announcements
|
||||
import app.revanced.manager.ui.model.navigation.AppSelector
|
||||
import app.revanced.manager.ui.model.navigation.ComplexParameter
|
||||
import app.revanced.manager.ui.model.navigation.Dashboard
|
||||
import app.revanced.manager.ui.model.navigation.InstalledApplicationInfo
|
||||
import app.revanced.manager.ui.model.navigation.Patcher
|
||||
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
|
||||
import app.revanced.manager.ui.model.navigation.Settings
|
||||
import app.revanced.manager.ui.model.navigation.Update
|
||||
import app.revanced.manager.ui.screen.AnnouncementScreen
|
||||
import app.revanced.manager.ui.screen.AnnouncementsScreen
|
||||
import app.revanced.manager.ui.screen.AppSelectorScreen
|
||||
import app.revanced.manager.ui.screen.DashboardScreen
|
||||
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
|
||||
import app.revanced.manager.ui.screen.PatcherScreen
|
||||
import app.revanced.manager.ui.screen.PatchesSelectorScreen
|
||||
import app.revanced.manager.ui.screen.RequiredOptionsScreen
|
||||
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
|
||||
import app.revanced.manager.ui.screen.SettingsScreen
|
||||
import app.revanced.manager.ui.screen.UpdateScreen
|
||||
import app.revanced.manager.ui.screen.settings.AboutSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.AdvancedSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.ContributorSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.DeveloperSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.DownloadsSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.GeneralSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.ImportExportSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.LicensesSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.update.ChangelogsSettingsScreen
|
||||
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
||||
import app.revanced.manager.ui.theme.ReVancedManagerTheme
|
||||
import app.revanced.manager.ui.theme.Theme
|
||||
import app.revanced.manager.ui.viewmodel.MainViewModel
|
||||
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
||||
import app.revanced.manager.util.EventEffect
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@ExperimentalAnimationApi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
enableEdgeToEdge()
|
||||
installSplashScreen()
|
||||
|
||||
val vm: MainViewModel = getActivityViewModel()
|
||||
|
||||
setContent {
|
||||
val theme by vm.prefs.theme.getAsState()
|
||||
val dynamicColor by vm.prefs.dynamicColor.getAsState()
|
||||
val pureBlackTheme by vm.prefs.pureBlackTheme.getAsState()
|
||||
|
||||
ReVancedManagerTheme(
|
||||
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
||||
dynamicColor = dynamicColor,
|
||||
pureBlackTheme = pureBlackTheme
|
||||
) {
|
||||
ReVancedManager(vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReVancedManager(vm: MainViewModel) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
EventEffect(vm.appSelectFlow) { app ->
|
||||
navController.navigateComplex(
|
||||
SelectedApplicationInfo,
|
||||
SelectedApplicationInfo.ViewModelParams(app)
|
||||
)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Dashboard,
|
||||
enterTransition = { slideInHorizontally(initialOffsetX = { it }) },
|
||||
exitTransition = { slideOutHorizontally(targetOffsetX = { -it / 3 }) },
|
||||
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it / 3 }) },
|
||||
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) }
|
||||
) {
|
||||
composable<Dashboard> {
|
||||
DashboardScreen(
|
||||
onSettingsClick = { navController.navigate(Settings) },
|
||||
onAppSelectorClick = {
|
||||
navController.navigate(AppSelector)
|
||||
},
|
||||
onUpdateClick = {
|
||||
navController.navigate(Update())
|
||||
},
|
||||
onDownloaderClick = {
|
||||
navController.navigate(Settings.Downloads)
|
||||
},
|
||||
onAppClick = { packageName ->
|
||||
navController.navigate(InstalledApplicationInfo(packageName))
|
||||
},
|
||||
onAnnouncementsClick = {
|
||||
navController.navigate(Announcements)
|
||||
},
|
||||
onAnnouncementClick = { announcement ->
|
||||
navController.navigateComplex(Announcement, announcement)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<InstalledApplicationInfo> {
|
||||
val data = it.toRoute<InstalledApplicationInfo>()
|
||||
|
||||
InstalledAppInfoScreen(
|
||||
onPatchClick = vm::selectApp,
|
||||
onBackClick = navController::popBackStack,
|
||||
viewModel = koinViewModel { parametersOf(data.packageName) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<AppSelector> {
|
||||
AppSelectorScreen(
|
||||
onSelect = vm::selectApp,
|
||||
onStorageSelect = vm::selectApp,
|
||||
onBackClick = navController::popBackStack
|
||||
)
|
||||
}
|
||||
|
||||
composable<Patcher> {
|
||||
PatcherScreen(
|
||||
onBackClick = {
|
||||
navController.navigate(route = Dashboard) {
|
||||
launchSingleTop = true
|
||||
popUpTo<Dashboard> {
|
||||
inclusive = false
|
||||
}
|
||||
}
|
||||
},
|
||||
viewModel = koinViewModel { parametersOf(it.getComplexArg<Patcher.ViewModelParams>()) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<Update> {
|
||||
val data = it.toRoute<Update>()
|
||||
|
||||
UpdateScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
vm = koinViewModel { parametersOf(data.downloadOnScreenEntry) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<Announcements> {
|
||||
AnnouncementsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onAnnouncementClick = { announcement ->
|
||||
navController.navigateComplex(Announcement, announcement)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<Announcement> {
|
||||
AnnouncementScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
announcement = it.getComplexArg()
|
||||
)
|
||||
}
|
||||
|
||||
navigation<SelectedApplicationInfo>(startDestination = SelectedApplicationInfo.Main) {
|
||||
composable<SelectedApplicationInfo.Main> {
|
||||
val parentBackStackEntry = navController.navGraphEntry(it)
|
||||
val data =
|
||||
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
|
||||
val viewModel =
|
||||
koinViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
|
||||
parametersOf(data)
|
||||
}
|
||||
|
||||
SelectedAppInfoScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onPatchClick = {
|
||||
it.lifecycleScope.launch {
|
||||
navController.navigateComplex(
|
||||
Patcher,
|
||||
viewModel.getPatcherParams()
|
||||
)
|
||||
}
|
||||
},
|
||||
onPatchSelectorClick = { app, patches, options ->
|
||||
navController.navigateComplex(
|
||||
SelectedApplicationInfo.PatchesSelector,
|
||||
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
|
||||
app,
|
||||
patches,
|
||||
options
|
||||
)
|
||||
)
|
||||
},
|
||||
onRequiredOptions = { app, patches, options ->
|
||||
navController.navigateComplex(
|
||||
SelectedApplicationInfo.RequiredOptions,
|
||||
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
|
||||
app,
|
||||
patches,
|
||||
options
|
||||
)
|
||||
)
|
||||
},
|
||||
vm = viewModel
|
||||
)
|
||||
}
|
||||
|
||||
composable<SelectedApplicationInfo.PatchesSelector> {
|
||||
val data =
|
||||
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
|
||||
val selectedAppInfoVm = koinViewModel<SelectedAppInfoViewModel>(
|
||||
viewModelStoreOwner = navController.navGraphEntry(it)
|
||||
)
|
||||
|
||||
PatchesSelectorScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onSave = { patches, options ->
|
||||
selectedAppInfoVm.updateConfiguration(patches, options)
|
||||
navController.popBackStack()
|
||||
},
|
||||
viewModel = koinViewModel { parametersOf(data) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<SelectedApplicationInfo.RequiredOptions> {
|
||||
val data =
|
||||
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
|
||||
val selectedAppInfoVm = koinViewModel<SelectedAppInfoViewModel>(
|
||||
viewModelStoreOwner = navController.navGraphEntry(it)
|
||||
)
|
||||
|
||||
RequiredOptionsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onContinue = { patches, options ->
|
||||
selectedAppInfoVm.updateConfiguration(patches, options)
|
||||
it.lifecycleScope.launch {
|
||||
navController.navigateComplex(
|
||||
Patcher,
|
||||
selectedAppInfoVm.getPatcherParams()
|
||||
)
|
||||
}
|
||||
},
|
||||
vm = koinViewModel { parametersOf(data) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
navigation<Settings>(startDestination = Settings.Main) {
|
||||
composable<Settings.Main> {
|
||||
SettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
navigate = navController::navigate
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.General> {
|
||||
GeneralSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Advanced> {
|
||||
AdvancedSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Developer> {
|
||||
DeveloperSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Updates> {
|
||||
UpdatesSettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onChangelogClick = { navController.navigate(Settings.Changelogs) },
|
||||
onUpdateClick = { navController.navigate(Update()) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.Downloads> {
|
||||
DownloadsSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.ImportExport> {
|
||||
ImportExportSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.About> {
|
||||
AboutSettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
navigate = navController::navigate
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.Changelogs> {
|
||||
ChangelogsSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Contributors> {
|
||||
ContributorSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Licenses> {
|
||||
LicensesSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavController.navGraphEntry(entry: NavBackStackEntry) =
|
||||
remember(entry) { getBackStackEntry(entry.destination.parent!!.id) }
|
||||
|
||||
// Androidx Navigation does not support storing complex types in route objects, so we have to store them inside the saved state handle of the back stack entry instead.
|
||||
private fun <T : Parcelable, R : ComplexParameter<T>> NavController.navigateComplex(
|
||||
route: R,
|
||||
data: T
|
||||
) {
|
||||
navigate(route)
|
||||
getBackStackEntry(route).savedStateHandle["args"] = data
|
||||
}
|
||||
|
||||
private fun <T : Parcelable> NavBackStackEntry.getComplexArg() = savedStateHandle.get<T>("args")!!
|
||||
@@ -1,111 +0,0 @@
|
||||
package app.revanced.manager
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import app.revanced.manager.data.platform.Filesystem
|
||||
import app.revanced.manager.di.*
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.DownloaderRepository
|
||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||
import app.revanced.manager.util.tag
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.internal.BuilderImpl
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.androidx.workmanager.koin.workManagerFactory
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
class ManagerApplication : Application() {
|
||||
private val scope = MainScope()
|
||||
private val prefs: PreferencesManager by inject()
|
||||
private val patchBundleRepository: PatchBundleRepository by inject()
|
||||
private val downloaderRepository: DownloaderRepository by inject()
|
||||
private val fs: Filesystem by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
startKoin {
|
||||
androidContext(this@ManagerApplication)
|
||||
androidLogger()
|
||||
workManagerFactory()
|
||||
modules(
|
||||
httpModule,
|
||||
preferencesModule,
|
||||
repositoryModule,
|
||||
serviceModule,
|
||||
managerModule,
|
||||
workerModule,
|
||||
viewModelModule,
|
||||
databaseModule,
|
||||
rootModule,
|
||||
ackpineModule
|
||||
)
|
||||
}
|
||||
|
||||
val pixels = 512
|
||||
Coil.setImageLoader(
|
||||
ImageLoader.Builder(this)
|
||||
.components {
|
||||
add(AppIconKeyer())
|
||||
add(AppIconFetcher.Factory(pixels, true, this@ManagerApplication))
|
||||
}
|
||||
.build()
|
||||
)
|
||||
|
||||
val shellBuilder = BuilderImpl.create().setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||
Shell.setDefaultBuilder(shellBuilder)
|
||||
|
||||
scope.launch {
|
||||
prefs.preload()
|
||||
}
|
||||
scope.launch(Dispatchers.Default) {
|
||||
downloaderRepository.reload()
|
||||
}
|
||||
scope.launch(Dispatchers.Default) {
|
||||
with(patchBundleRepository) {
|
||||
reload()
|
||||
updateCheck()
|
||||
}
|
||||
}
|
||||
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
|
||||
private var firstActivityCreated = false
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
if (firstActivityCreated) return
|
||||
firstActivityCreated = true
|
||||
|
||||
// We do not want to call onFreshProcessStart() if there is state to restore.
|
||||
// This can happen on system-initiated process death.
|
||||
if (savedInstanceState == null) {
|
||||
Log.d(tag, "Fresh process created")
|
||||
onFreshProcessStart()
|
||||
} else Log.d(tag, "System-initiated process death detected")
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
override fun onActivityResumed(activity: Activity) {}
|
||||
override fun onActivityPaused(activity: Activity) {}
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun onFreshProcessStart() {
|
||||
fs.uiTempDir.apply {
|
||||
deleteRecursively()
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package app.revanced.manager.data.platform
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import app.revanced.manager.util.RequestManageStorageContract
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
class Filesystem(private val app: Application) {
|
||||
val contentResolver = app.contentResolver // TODO: move Content Resolver operations to here.
|
||||
|
||||
/**
|
||||
* A directory that gets cleared when the app restarts.
|
||||
* Do not store paths to this directory in a parcel.
|
||||
*/
|
||||
val tempDir: File = app.getDir("ephemeral", Context.MODE_PRIVATE).apply {
|
||||
deleteRecursively()
|
||||
mkdirs()
|
||||
}
|
||||
|
||||
/**
|
||||
* A directory for storing temporary files related to UI.
|
||||
* This is the same as [tempDir], but does not get cleared on system-initiated process death.
|
||||
* Paths to this directory can be safely stored in parcels.
|
||||
*/
|
||||
val uiTempDir: File = app.getDir("ui_ephemeral", Context.MODE_PRIVATE)
|
||||
|
||||
fun externalFilesDir(): Path = Environment.getExternalStorageDirectory().toPath()
|
||||
|
||||
private fun usesManagePermission() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
|
||||
private val storagePermissionName =
|
||||
if (usesManagePermission()) Manifest.permission.MANAGE_EXTERNAL_STORAGE else Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
|
||||
fun permissionContract(): Pair<ActivityResultContract<String, Boolean>, String> {
|
||||
val contract =
|
||||
if (usesManagePermission()) RequestManageStorageContract() else ActivityResultContracts.RequestPermission()
|
||||
return contract to storagePermissionName
|
||||
}
|
||||
|
||||
fun hasStoragePermission() =
|
||||
if (usesManagePermission()) Environment.isExternalStorageManager() else app.checkSelfPermission(
|
||||
storagePermissionName
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package app.revanced.manager.data.platform
|
||||
|
||||
import android.app.Application
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import androidx.core.content.getSystemService
|
||||
|
||||
class NetworkInfo(app: Application) {
|
||||
private val connectivityManager = app.getSystemService<ConnectivityManager>()!!
|
||||
|
||||
private fun getCapabilities() = connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) }
|
||||
fun isConnected() = connectivityManager.activeNetwork != null
|
||||
fun isUnmetered() = getCapabilities()?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ?: true
|
||||
|
||||
/**
|
||||
* Returns true if it is safe to download large files.
|
||||
*/
|
||||
fun isSafe(ignoreMetered: Boolean) = isConnected() && (ignoreMetered || isUnmetered())
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package app.revanced.manager.data.redux
|
||||
|
||||
import android.util.Log
|
||||
import app.revanced.manager.util.tag
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
// This file implements React Redux-like state management.
|
||||
|
||||
class Store<S>(private val coroutineScope: CoroutineScope, initialState: S) : ActionContext {
|
||||
private val _state = MutableStateFlow(initialState)
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
// Do not touch these without the lock.
|
||||
private var isRunningActions = false
|
||||
private val queueChannel = Channel<Action<S>>(capacity = 10)
|
||||
private val lock = Mutex()
|
||||
|
||||
suspend fun dispatch(action: Action<S>) = lock.withLock {
|
||||
Log.d(tag, "Dispatching $action")
|
||||
queueChannel.send(action)
|
||||
|
||||
if (isRunningActions) return@withLock
|
||||
isRunningActions = true
|
||||
coroutineScope.launch {
|
||||
runActions()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private suspend fun runActions() {
|
||||
while (true) {
|
||||
val action = withTimeoutOrNull(200L) { queueChannel.receive() }
|
||||
if (action == null) {
|
||||
Log.d(tag, "Stopping action runner")
|
||||
lock.withLock {
|
||||
// New actions may be dispatched during the timeout.
|
||||
isRunningActions = !queueChannel.isEmpty
|
||||
if (!isRunningActions) return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
Log.d(tag, "Running $action")
|
||||
_state.value = try {
|
||||
with(action) { this@Store.execute(_state.value) }
|
||||
} catch (c: CancellationException) {
|
||||
// This is done without the lock, but cancellation usually means the store is no longer needed.
|
||||
isRunningActions = false
|
||||
throw c
|
||||
} catch (e: Exception) {
|
||||
action.catch(e)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionContext
|
||||
|
||||
interface Action<S> {
|
||||
suspend fun ActionContext.execute(current: S): S
|
||||
suspend fun catch(exception: Exception) {
|
||||
Log.e(tag, "Got exception while executing $this", exception)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package app.revanced.manager.data.room
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import app.revanced.manager.data.room.apps.downloaded.DownloadedAppDao
|
||||
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
|
||||
import app.revanced.manager.data.room.apps.installed.AppliedPatch
|
||||
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
||||
import app.revanced.manager.data.room.apps.installed.InstalledAppDao
|
||||
import app.revanced.manager.data.room.selection.PatchSelection
|
||||
import app.revanced.manager.data.room.selection.SelectedPatch
|
||||
import app.revanced.manager.data.room.selection.SelectionDao
|
||||
import app.revanced.manager.data.room.bundles.PatchBundleDao
|
||||
import app.revanced.manager.data.room.bundles.PatchBundleEntity
|
||||
import app.revanced.manager.data.room.options.Option
|
||||
import app.revanced.manager.data.room.options.OptionDao
|
||||
import app.revanced.manager.data.room.options.OptionGroup
|
||||
import app.revanced.manager.data.room.downloader.TrustedDownloader
|
||||
import app.revanced.manager.data.room.downloader.TrustedDownloaderDao
|
||||
import kotlin.random.Random
|
||||
|
||||
@Database(
|
||||
entities = [PatchBundleEntity::class, PatchSelection::class, SelectedPatch::class, DownloadedApp::class, InstalledApp::class, AppliedPatch::class, OptionGroup::class, Option::class, TrustedDownloader::class],
|
||||
version = 1
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun patchBundleDao(): PatchBundleDao
|
||||
abstract fun selectionDao(): SelectionDao
|
||||
abstract fun downloadedAppDao(): DownloadedAppDao
|
||||
abstract fun installedAppDao(): InstalledAppDao
|
||||
abstract fun optionDao(): OptionDao
|
||||
abstract fun trustedDownloaderDao(): TrustedDownloaderDao
|
||||
|
||||
companion object {
|
||||
fun generateUid() = Random.Default.nextInt()
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package app.revanced.manager.data.room
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import app.revanced.manager.data.room.bundles.Source
|
||||
import app.revanced.manager.data.room.options.Option.SerializedValue
|
||||
import java.io.File
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun sourceFromString(value: String) = Source.from(value)
|
||||
|
||||
@TypeConverter
|
||||
fun sourceToString(value: Source) = value.toString()
|
||||
|
||||
@TypeConverter
|
||||
fun fileFromString(value: String) = File(value)
|
||||
|
||||
@TypeConverter
|
||||
fun fileToString(file: File): String = file.path
|
||||
|
||||
@TypeConverter
|
||||
fun serializedOptionFromString(value: String) = SerializedValue.fromJsonString(value)
|
||||
|
||||
@TypeConverter
|
||||
fun serializedOptionToString(value: SerializedValue) = value.toJsonString()
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package app.revanced.manager.data.room.apps.downloaded
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import java.io.File
|
||||
|
||||
@Entity(
|
||||
tableName = "downloaded_app",
|
||||
primaryKeys = ["package_name", "version"]
|
||||
)
|
||||
data class DownloadedApp(
|
||||
@ColumnInfo(name = "package_name") val packageName: String,
|
||||
@ColumnInfo(name = "version") val version: String,
|
||||
@ColumnInfo(name = "directory") val directory: File,
|
||||
@ColumnInfo(name = "last_used") val lastUsed: Long = System.currentTimeMillis()
|
||||
)
|
||||