mirror of
https://github.com/ReVanced/revanced-manager-downloaders.git
synced 2026-01-24 03:31:05 +00:00
Compare commits
4 Commits
dev
...
ci/attesta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c63caac27 | ||
|
|
19a94d3e77 | ||
|
|
0ee34f028f | ||
|
|
17d7ed164c |
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,18 +11,18 @@ body:
|
|||||||
<source
|
<source
|
||||||
width="256px"
|
width="256px"
|
||||||
media="(prefers-color-scheme: dark)"
|
media="(prefers-color-scheme: dark)"
|
||||||
srcset="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
srcset="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
width="256px"
|
width="256px"
|
||||||
src="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
src="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||||
>
|
>
|
||||||
</picture>
|
</picture>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://revanced.app/">
|
<a href="https://revanced.app/">
|
||||||
<picture>
|
<picture>
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-logo/revanced-logo.svg" />
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-logo/revanced-logo.svg" />
|
||||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-logo/revanced-logo.svg" />
|
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-logo/revanced-logo.svg" />
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ReVanced">
|
<a href="https://github.com/ReVanced">
|
||||||
@@ -68,8 +68,8 @@ body:
|
|||||||
|
|
||||||
Before creating a new bug report, please keep the following in mind:
|
Before creating a new bug report, please keep the following in mind:
|
||||||
|
|
||||||
- **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-manager-downloaders/issues?q=label%3A%22Bug+report%22).
|
- **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/issues?q=label%3A%22Bug+report%22).
|
||||||
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager-downloaders/blob/main/CONTRIBUTING.md).
|
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/blob/main/CONTRIBUTING.md).
|
||||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
@@ -79,7 +79,7 @@ body:
|
|||||||
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
||||||
- Add images and videos if possible
|
- Add images and videos if possible
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Error logs
|
label: Error logs
|
||||||
|
|||||||
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -11,18 +11,18 @@ body:
|
|||||||
<source
|
<source
|
||||||
width="256px"
|
width="256px"
|
||||||
media="(prefers-color-scheme: dark)"
|
media="(prefers-color-scheme: dark)"
|
||||||
srcset="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
srcset="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
width="256px"
|
width="256px"
|
||||||
src="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
src="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||||
>
|
>
|
||||||
</picture>
|
</picture>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://revanced.app/">
|
<a href="https://revanced.app/">
|
||||||
<picture>
|
<picture>
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-logo/revanced-logo.svg" />
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-logo/revanced-logo.svg" />
|
||||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-manager-downloaders/main/assets/revanced-logo/revanced-logo.svg" />
|
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-manager-apkmirror-downloader/main/assets/revanced-logo/revanced-logo.svg" />
|
||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ReVanced">
|
<a href="https://github.com/ReVanced">
|
||||||
@@ -68,8 +68,8 @@ body:
|
|||||||
|
|
||||||
Before creating a new feature request, please keep the following in mind:
|
Before creating a new feature request, please keep the following in mind:
|
||||||
|
|
||||||
- **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-manager-downloaders/issues?q=label%3A%22Feature+request%22).
|
- **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/issues?q=label%3A%22Feature+request%22).
|
||||||
- **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager-downloaders/blob/main/CONTRIBUTING.md).
|
- **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/blob/main/CONTRIBUTING.md).
|
||||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
10
.github/workflows/build_pull_request.yml
vendored
10
.github/workflows/build_pull_request.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -23,15 +23,9 @@ jobs:
|
|||||||
java-version: "17"
|
java-version: "17"
|
||||||
|
|
||||||
- name: Cache Gradle
|
- name: Cache Gradle
|
||||||
uses: burrunan/gradle-cache-action@v3
|
uses: burrunan/gradle-cache-action@v2
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew build --no-daemon
|
run: ./gradlew build --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: revanced-manager-downloaders
|
|
||||||
path: downloaders/*/build/outputs/apk/release/*.apk
|
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -11,15 +11,15 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
|
||||||
id-token: write
|
id-token: write
|
||||||
|
contents: write
|
||||||
attestations: write
|
attestations: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew assembleRelease --no-daemon
|
run: ./gradlew build --no-daemon
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -63,10 +63,10 @@ jobs:
|
|||||||
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
KEYSTORE_ENTRY_ALIAS: ${{ secrets.KEYSTORE_ENTRY_ALIAS }}
|
KEYSTORE_ENTRY_ALIAS: ${{ secrets.KEYSTORE_ENTRY_ALIAS }}
|
||||||
KEYSTORE_ENTRY_PASSWORD: ${{ secrets.KEYSTORE_ENTRY_PASSWORD }}
|
KEYSTORE_ENTRY_PASSWORD: ${{ secrets.KEYSTORE_ENTRY_PASSWORD }}
|
||||||
|
|
||||||
- name: Attest
|
- name: Attest
|
||||||
if: steps.release.outputs.new_release_published == 'true'
|
if: steps.release.outputs.new_release_published == 'true'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v2
|
||||||
with:
|
with:
|
||||||
subject-name: 'ReVanced Downloader ${{ steps.release.outputs.new_release_git_tag }}'
|
subject-name: 'ReVanced Manager APKMirror Downloader ${{ steps.release.outputs.new_release_git_tag }}'
|
||||||
subject-path: downloaders/*/build/outputs/apk/release/*.apk
|
subject-path: build/outputs/apk/release/*.apk
|
||||||
|
|||||||
140
.gitignore
vendored
140
.gitignore
vendored
@@ -1,16 +1,130 @@
|
|||||||
|
### Java template
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# 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
|
||||||
*.iml
|
*.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
|
.gradle
|
||||||
/local.properties
|
**/build/
|
||||||
/.idea/caches
|
!src/**/build/
|
||||||
/.idea/libraries
|
|
||||||
/.idea/modules.xml
|
# Ignore Gradle GUI config
|
||||||
/.idea/workspace.xml
|
gradle-app.setting
|
||||||
/.idea/navEditor.xml
|
|
||||||
/.idea/assetWizardSettings.xml
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
.DS_Store
|
!gradle-wrapper.jar
|
||||||
**/build
|
|
||||||
/captures
|
# Cache of project
|
||||||
.externalNativeBuild
|
.gradletasknamecache
|
||||||
.cxx
|
|
||||||
local.properties
|
# # 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/
|
.idea/
|
||||||
|
|
||||||
|
.kotlin/
|
||||||
|
|
||||||
|
local.properties
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "arsclib"]
|
|
||||||
path = arsclib
|
|
||||||
url = https://github.com/REAndroid/ARSCLib
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
{
|
{
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
"path": "downloaders/*/build/outputs/apk/release/revanced-manager-*.apk?(.asc)",
|
"path": "build/outputs/apk/release/*.apk?(.asc)",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
successComment: false
|
successComment: false
|
||||||
|
|||||||
56
CHANGELOG.md
56
CHANGELOG.md
@@ -1,59 +1,3 @@
|
|||||||
# [1.1.0-dev.8](https://github.com/ReVanced/revanced-manager-downloaders/compare/v1.1.0-dev.7...v1.1.0-dev.8) (2025-07-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Prefixing APKs with `revanced-manager-` ([f4c91f0](https://github.com/ReVanced/revanced-manager-downloaders/commit/f4c91f0455e19e240a1527ff05f8f987fbf8e054))
|
|
||||||
|
|
||||||
# [1.1.0-dev.7](https://github.com/ReVanced/revanced-manager-downloaders/compare/v1.1.0-dev.6...v1.1.0-dev.7) (2025-07-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Refactor of shared module + minor fixes ([5f6c7e5](https://github.com/ReVanced/revanced-manager-downloaders/commit/5f6c7e53331d65b18898fb2997e2ff1ab5c30b87))
|
|
||||||
|
|
||||||
# [1.1.0-dev.6](https://github.com/ReVanced/revanced-manager-downloaders/compare/v1.1.0-dev.5...v1.1.0-dev.6) (2025-07-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Showing progress if downloading an APK + minor fixes ([d46f002](https://github.com/ReVanced/revanced-manager-downloaders/commit/d46f002aa234135ad54768d0bdf15eb9d17428c0))
|
|
||||||
|
|
||||||
# [1.1.0-dev.5](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/compare/v1.1.0-dev.4...v1.1.0-dev.5) (2025-07-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Correctly removing bundle attributes from manifest ([6433d98](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/commit/6433d984950582d36d29ef758325d50016a62ae8))
|
|
||||||
|
|
||||||
# [1.1.0-dev.4](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/compare/v1.1.0-dev.3...v1.1.0-dev.4) (2025-07-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Add gradle sha256 checksum ([f259821](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/commit/f259821f7c1075aeca89759cc7d73ae93d91922a))
|
|
||||||
|
|
||||||
# [1.1.0-dev.3](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/compare/v1.1.0-dev.2...v1.1.0-dev.3) (2025-07-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix release workflow (again) ([5bde601](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/commit/5bde601bdda26d1b7704172eab239ff9ae41fb20))
|
|
||||||
|
|
||||||
# [1.1.0-dev.2](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/compare/v1.1.0-dev.1...v1.1.0-dev.2) (2025-07-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix release workflow ([847bcdb](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/commit/847bcdb8dff27c3a0d012772a9946311cc9825d9))
|
|
||||||
|
|
||||||
# [1.1.0-dev.1](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/compare/v1.0.0...v1.1.0-dev.1) (2025-07-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add play store downloader ([#10](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/issues/10)) ([38c6aa4](https://github.com/ReVanced/revanced-manager-apkmirror-downloader/commit/38c6aa4f6dbaa230212cd6e229609faecd615349))
|
|
||||||
|
|
||||||
# 1.0.0 (2025-01-13)
|
# 1.0.0 (2025-01-13)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,38 +60,38 @@
|
|||||||
|
|
||||||
# 👋 Contribution guidelines
|
# 👋 Contribution guidelines
|
||||||
|
|
||||||
This document describes how to contribute to ReVanced Manager Downloaders.
|
This document describes how to contribute to ReVanced Manager downloader template.
|
||||||
|
|
||||||
## 📖 Resources to help you get started
|
## 📖 Resources to help you get started
|
||||||
|
|
||||||
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
||||||
* [Issues](https://github.com/ReVanced/revanced-manager-downloaders/issues) are where we keep track of bugs and feature requests
|
* [Issues](https://github.com/ReVanced/revanced-manager-downloader-template/issues) are where we keep track of bugs and feature requests
|
||||||
|
|
||||||
## 🙏 Submitting a feature request
|
## 🙏 Submitting a feature request
|
||||||
|
|
||||||
Features can be requested by opening an issue using the
|
Features can be requested by opening an issue using the
|
||||||
[Feature request issue template](https://github.com/ReVanced/revanced-manager-downloaders/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
|
[Feature request issue template](https://github.com/ReVanced/revanced-manager-downloader-template/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Manager Downloaders.
|
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Manager downloader template.
|
||||||
> Good motivation has to be provided for a request to be accepted.
|
> Good motivation has to be provided for a request to be accepted.
|
||||||
|
|
||||||
## 🐞 Submitting a bug report
|
## 🐞 Submitting a bug report
|
||||||
|
|
||||||
If you encounter a bug while using ReVanced Manager Downloaders, open an issue using the
|
If you encounter a bug while using ReVanced Manager downloader template, open an issue using the
|
||||||
[Bug report issue template](https://github.com/ReVanced/revanced-manager-downloaders/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
|
[Bug report issue template](https://github.com/ReVanced/revanced-manager-downloader-template/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
|
||||||
|
|
||||||
## 📝 How to contribute
|
## 📝 How to contribute
|
||||||
|
|
||||||
1. Before contributing, it is recommended to open an issue to discuss your change
|
1. Before contributing, it is recommended to open an issue to discuss your change
|
||||||
with the maintainers of ReVanced Manager Downloaders. This will help you determine whether your change is acceptable
|
with the maintainers of ReVanced Manager downloader template. This will help you determine whether your change is acceptable
|
||||||
and whether it is worth your time to implement it
|
and whether it is worth your time to implement it
|
||||||
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
||||||
3. Commit your changes
|
3. Commit your changes
|
||||||
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
||||||
that your pull request closes in the description of your pull request
|
that your pull request closes in the description of your pull request
|
||||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
||||||
it will be merged into the `dev` branch and will be included in the next release of ReVanced Manager Downloaders
|
it will be merged into the `dev` branch and will be included in the next release of ReVanced Manager downloader template
|
||||||
|
|
||||||
❤️ Thank you for considering contributing to ReVanced Manager Downloaders,
|
❤️ Thank you for considering contributing to ReVanced Manager downloader template,
|
||||||
ReVanced
|
ReVanced
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -58,44 +58,34 @@
|
|||||||
Continuing the legacy of Vanced
|
Continuing the legacy of Vanced
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# 👋🔌 ReVanced Manager Downloaders
|
# 👋🔌 ReVanced Manager APKMirror downloader
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
The collection of ReVanced downloaders.
|
An [APKMirror](https://www.apkmirror.com/) plugin for ReVanced Manager.
|
||||||
|
|
||||||
## 🧑💻 Usage
|
## 🧑💻 Usage
|
||||||
|
|
||||||
- Downloaders are managed as Android apps. Download and install the APK file from the [releases page](https://github.com/ReVanced/revanced-manager-downloaders/releases/).
|
- Plugins are managed as Android apps. Download and install the APK file from the releases page.
|
||||||
- After installing, restart ReVanced Manager and enable the downloader in the settings.
|
- After installing, restart ReVanced Manager and enable the plugin in the settings.
|
||||||
- The downloader will now be usable.
|
- The plugin will now be usable.
|
||||||
|
|
||||||
|
The plugin works by opening the APKMirror website in an embedded browser. If the search string contains a version, you must select that version. Selecting another version in that situation will cause patching to fail.
|
||||||
### APKMirror Downloader
|
|
||||||
This downloader will open an [APKMirror](https://www.apkmirror.com/) page where you can download the apk as you would normally do.
|
|
||||||
If the chosen file is a bundle (`apkm` file), the downloader will automatically merge it into an `apk` file.
|
|
||||||
|
|
||||||
### Play Store Downloader
|
|
||||||
When you get prompted, log in to Google with your account. After that, you will be able to download apps from the Play Store.
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> Due to technical limitations, it is only possible to download the latest version of the app. If the suggested version differs from the latest, the installation will fail.
|
|
||||||
> This plugin is not officially endorsed by or affiliated with Google. All usage is at your own risk.
|
|
||||||
|
|
||||||
## 📚 Everything else
|
## 📚 Everything else
|
||||||
|
|
||||||
### 📙 Contributing
|
### 📙 Contributing
|
||||||
|
|
||||||
Thank you for considering contributing to ReVanced Manager Downloaders.
|
Thank you for considering contributing to ReVanced Manager APKMirror downloader.
|
||||||
You can find the contribution guidelines [here](CONTRIBUTING.md).
|
You can find the contribution guidelines [here](CONTRIBUTING.md).
|
||||||
|
|
||||||
### 🛠️ Building
|
### 🛠️ Building
|
||||||
|
|
||||||
To build ReVanced Manager Downloaders, a Java Development Kit (JDK) and Git must be installed.
|
To build ReVanced Manager downloader template, a Java Development Kit (JDK) and Git must be installed.
|
||||||
Follow the steps below to build ReVanced Manager Downloaders:
|
Follow the steps below to build ReVanced Manager downloader template:
|
||||||
|
|
||||||
1. Run `git clone git@github.com:ReVanced/revanced-manager-downloaders.git` to clone the repository
|
1. Run `git clone git@github.com:ReVanced/revanced-manager-apkmirror-downloader.git` to clone the repository
|
||||||
2. Run `gradlew assembleRelease` to build the project
|
2. Run `gradlew assembleRelease` to build the project
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
@@ -109,7 +99,7 @@ Follow the steps below to build ReVanced Manager Downloaders:
|
|||||||
> gpr.key = key
|
> gpr.key = key
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
## 📜 License
|
## 📜 Licence
|
||||||
|
|
||||||
This project is licensed under the GPLv3 licence.
|
This project is licensed under the GPLv3 licence.
|
||||||
Please see the [license file](LICENSE) for more information.
|
Please see the [license file](LICENSE) for more information.
|
||||||
|
|||||||
1
arsclib
1
arsclib
Submodule arsclib deleted from a44577e73f
183
build.gradle.kts
183
build.gradle.kts
@@ -1,111 +1,88 @@
|
|||||||
import com.android.build.gradle.AppExtension
|
|
||||||
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
|
||||||
import org.gradle.plugins.signing.SigningExtension
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.android.library) apply false
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.parcelize) apply false
|
publishing
|
||||||
alias(libs.plugins.kotlin.android) apply false
|
signing
|
||||||
alias(libs.plugins.compose.compiler) apply false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
dependencies {
|
||||||
repositories {
|
compileOnly(libs.plugin.api)
|
||||||
google()
|
}
|
||||||
mavenCentral()
|
|
||||||
maven {
|
android {
|
||||||
name = "GitHubPackages"
|
val packageName = "app.revanced.manager.plugin.downloader.apkmirror"
|
||||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
|
||||||
credentials {
|
namespace = packageName
|
||||||
username = providers.gradleProperty("gpr.user")
|
compileSdk = 35
|
||||||
.getOrElse(System.getenv("GITHUB_ACTOR"))
|
|
||||||
password =
|
defaultConfig {
|
||||||
providers.gradleProperty("gpr.key").getOrElse(System.getenv("GITHUB_TOKEN"))
|
applicationId = packageName
|
||||||
|
minSdk = 26
|
||||||
|
targetSdk = 35
|
||||||
|
versionName = version.toString()
|
||||||
|
versionCode = versionName!!.filter { it.isDigit() }.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro",
|
||||||
|
)
|
||||||
|
|
||||||
|
val keystoreFile = file("keystore.jks")
|
||||||
|
signingConfig =
|
||||||
|
if (keystoreFile.exists()) {
|
||||||
|
signingConfigs.create("release") {
|
||||||
|
storeFile = keystoreFile
|
||||||
|
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||||||
|
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
||||||
|
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
signingConfigs["debug"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationVariants.all {
|
||||||
|
outputs.all {
|
||||||
|
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
||||||
|
|
||||||
|
outputFileName = "${rootProject.name}-$version.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
val assembleReleaseSignApk by registering {
|
||||||
|
dependsOn("assembleRelease")
|
||||||
|
|
||||||
|
val apk = layout.buildDirectory.file("outputs/apk/release/${rootProject.name}-$version.apk")
|
||||||
|
|
||||||
|
inputs.file(apk).withPropertyName("input")
|
||||||
|
outputs.file(apk.map { it.asFile.resolveSibling("${it.asFile.name}.asc") })
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(*inputs.files.files.toTypedArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.path.startsWith(":downloaders:")) {
|
// Used by gradle-semantic-release-plugin.
|
||||||
apply(plugin = "com.android.application")
|
// Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435.
|
||||||
apply(plugin = "org.jetbrains.kotlin.android")
|
publish {
|
||||||
apply(plugin = "maven-publish")
|
dependsOn(assembleReleaseSignApk)
|
||||||
apply(plugin = "signing")
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
"compileOnly"(rootProject.libs.plugin.api)
|
|
||||||
}
|
|
||||||
|
|
||||||
configure<AppExtension> {
|
|
||||||
compileSdkVersion(35)
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 26
|
|
||||||
targetSdk = 35
|
|
||||||
versionName = version.toString()
|
|
||||||
versionCode = versionName!!.filter { it.isDigit() }.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
getByName("release") {
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
|
|
||||||
val keystoreFile = file("${rootDir}/keystore.jks")
|
|
||||||
signingConfig =
|
|
||||||
if (keystoreFile.exists()) {
|
|
||||||
signingConfigs.create("release") {
|
|
||||||
storeFile = keystoreFile
|
|
||||||
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
|
||||||
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
|
|
||||||
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
signingConfigs.getByName("debug")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationVariants.all {
|
|
||||||
outputs.all {
|
|
||||||
this as ApkVariantOutputImpl
|
|
||||||
outputFileName = "revanced-manager-${project.name}-downloader-$version.apk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<KotlinCompile>().configureEach {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("assembleReleaseSignApk") {
|
|
||||||
dependsOn("assembleRelease")
|
|
||||||
|
|
||||||
val apk = layout.buildDirectory.file("outputs/apk/release/revanced-manager-${project.name}-downloader-$version.apk")
|
|
||||||
|
|
||||||
inputs.file(apk).withPropertyName("input")
|
|
||||||
outputs.file(apk.map { it.asFile.resolveSibling("${it.asFile.name}.asc") })
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
project.configure<SigningExtension> {
|
|
||||||
useGpgCmd()
|
|
||||||
sign(*inputs.files.files.toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("publish") {
|
|
||||||
dependsOn("assembleReleaseSignApk")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlin.parcelize)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
val packageName = "app.revanced.manager.plugin.downloader.apkmirror"
|
|
||||||
namespace = packageName
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = packageName
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":shared"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
@file:Suppress("Unused")
|
|
||||||
|
|
||||||
package app.revanced.manager.plugin.downloader.apkmirror
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import app.revanced.manager.plugin.downloader.DownloadUrl
|
|
||||||
import app.revanced.manager.plugin.downloader.Downloader
|
|
||||||
import app.revanced.manager.plugin.downloader.download
|
|
||||||
import app.revanced.manager.plugin.downloader.webview.runWebView
|
|
||||||
import app.revanced.manager.plugin.shared.Merger
|
|
||||||
import java.net.URI
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.UUID
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import kotlin.io.path.ExperimentalPathApi
|
|
||||||
import kotlin.io.path.deleteRecursively
|
|
||||||
import kotlin.io.path.outputStream
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPathApi::class)
|
|
||||||
val ApkMirrorDownloader = Downloader<DownloadUrl> {
|
|
||||||
get { packageName, version ->
|
|
||||||
runWebView("APKMirror") {
|
|
||||||
download { url, _, userAgent ->
|
|
||||||
finish(
|
|
||||||
DownloadUrl(
|
|
||||||
url,
|
|
||||||
mapOf("User-Agent" to userAgent)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri.Builder()
|
|
||||||
.scheme("https")
|
|
||||||
.authority("www.apkmirror.com")
|
|
||||||
.appendQueryParameter("post_type", "app_release")
|
|
||||||
.appendQueryParameter("searchtype", "apk")
|
|
||||||
.appendQueryParameter("s", version?.let { "$packageName $it" } ?: packageName)
|
|
||||||
.appendQueryParameter("bundles%5B%5D" /* bundles[] */, "apk_files")
|
|
||||||
.toString()
|
|
||||||
} to version
|
|
||||||
}
|
|
||||||
|
|
||||||
download { downloadUrl, outputStream ->
|
|
||||||
val workingDir = Files.createTempDirectory("apkmirror_dl")
|
|
||||||
try {
|
|
||||||
if (URI(downloadUrl.url).path.substringAfterLast('/').endsWith(".apk")) {
|
|
||||||
val (inputStream, size) = downloadUrl.toDownloadResult()
|
|
||||||
inputStream.use {
|
|
||||||
if (size != null) reportSize(size)
|
|
||||||
it.copyTo(outputStream)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val downloadedFile = workingDir.resolve(UUID.randomUUID().toString()).also {
|
|
||||||
it.outputStream().use { output ->
|
|
||||||
downloadUrl.toDownloadResult().first.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val xapkWorkingDir = workingDir.resolve("xapk").also { it.toFile().mkdirs() }
|
|
||||||
|
|
||||||
ZipFile(downloadedFile.toString()).use { zip ->
|
|
||||||
zip.entries().asSequence().forEach { entry ->
|
|
||||||
xapkWorkingDir.resolve(entry.name).also { it.parent.toFile().mkdirs() }.also { outputFile ->
|
|
||||||
zip.getInputStream(entry).use { input ->
|
|
||||||
Files.copy(input, outputFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Merger.merge(xapkWorkingDir).writeApk(outputStream)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
workingDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.kotlin.parcelize)
|
|
||||||
alias(libs.plugins.compose.compiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
val packageName = "app.revanced.manager.plugin.downloader.play.store"
|
|
||||||
namespace = packageName
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = packageName
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.gplayapi)
|
|
||||||
implementation(project(":shared"))
|
|
||||||
|
|
||||||
implementation(libs.ktor.core)
|
|
||||||
implementation(libs.ktor.logging)
|
|
||||||
implementation(libs.ktor.okhttp)
|
|
||||||
|
|
||||||
implementation(libs.compose.activity)
|
|
||||||
implementation(platform(libs.compose.bom))
|
|
||||||
implementation(libs.compose.ui)
|
|
||||||
implementation(libs.compose.ui.tooling)
|
|
||||||
implementation(libs.compose.material3)
|
|
||||||
implementation(libs.compose.webview)
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
aidl = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +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">
|
|
||||||
|
|
||||||
<uses-feature android:name="app.revanced.manager.plugin.downloader" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
tools:targetApi="34">
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.AuthActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="app.revanced.manager.permission.PLUGIN_HOST"
|
|
||||||
android:theme="@android:style/Theme.DeviceDefault" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".service.CredentialProviderService"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="app.revanced.manager.permission.PLUGIN_HOST" />
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="app.revanced.manager.plugin.downloader.class"
|
|
||||||
android:value="app.revanced.manager.plugin.downloader.play.store.PlayStorePluginKt" />
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// ICredentialProvider.aidl
|
|
||||||
package app.revanced.manager.plugin.downloader.play.store;
|
|
||||||
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.Credentials;
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.ParcelProperties;
|
|
||||||
|
|
||||||
interface ICredentialProvider {
|
|
||||||
@nullable Credentials retrieveCredentials();
|
|
||||||
ParcelProperties getProperties();
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// Credentials.aidl
|
|
||||||
package app.revanced.manager.plugin.downloader.play.store.data;
|
|
||||||
|
|
||||||
parcelable Credentials;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// ParcelProperties.aidl
|
|
||||||
package app.revanced.manager.plugin.downloader.play.store.data;
|
|
||||||
|
|
||||||
parcelable ParcelProperties;
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import android.util.Log
|
|
||||||
import app.revanced.manager.plugin.downloader.*
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.Credentials
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.Http
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.service.CredentialProviderService
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.ui.AuthActivity
|
|
||||||
import app.revanced.manager.plugin.shared.Merger
|
|
||||||
import com.aurora.gplayapi.data.models.File as GPlayFile
|
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
|
||||||
import com.aurora.gplayapi.helpers.PurchaseHelper
|
|
||||||
import io.ktor.client.request.url
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.StandardOpenOption
|
|
||||||
import java.util.Properties
|
|
||||||
import kotlin.io.path.ExperimentalPathApi
|
|
||||||
import kotlin.io.path.deleteRecursively
|
|
||||||
import kotlin.io.path.listDirectoryEntries
|
|
||||||
import kotlin.io.path.outputStream
|
|
||||||
|
|
||||||
private val allowedFileTypes = arrayOf(GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT)
|
|
||||||
const val LOG_TAG = "PlayStorePlugin"
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
class GPlayApp(
|
|
||||||
val files: List<GPlayFile>
|
|
||||||
) : Parcelable
|
|
||||||
|
|
||||||
@Suppress("Unused")
|
|
||||||
@OptIn(ExperimentalPathApi::class)
|
|
||||||
val playStoreDownloader = Downloader<GPlayApp> {
|
|
||||||
get { packageName, version ->
|
|
||||||
val (credentials, deviceProps) = useService<CredentialProviderService, Pair<Credentials, Properties>> { binder ->
|
|
||||||
val credentialProvider = ICredentialProvider.Stub.asInterface(binder)
|
|
||||||
val props = credentialProvider.properties.value
|
|
||||||
credentialProvider.retrieveCredentials()?.let { return@useService it to props }
|
|
||||||
|
|
||||||
try {
|
|
||||||
requestStartActivity<AuthActivity>()
|
|
||||||
} catch (e: UserInteractionException.Activity.NotCompleted) {
|
|
||||||
if (e.resultCode == AuthActivity.RESULT_FAILED) throw Exception(
|
|
||||||
"Login failed: ${
|
|
||||||
e.intent?.getStringExtra(
|
|
||||||
AuthActivity.FAILURE_MESSAGE_KEY
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialProvider.retrieveCredentials()?.let { it to props } ?: throw Exception("Could not get credentials")
|
|
||||||
}
|
|
||||||
val authData = credentials.toAuthData(deviceProps)
|
|
||||||
|
|
||||||
val app = try {
|
|
||||||
AppDetailsHelper(authData).using(Http).getAppByPackageName(packageName)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(LOG_TAG, "Got exception while trying to get app", e)
|
|
||||||
null
|
|
||||||
}?.takeUnless { version != null && it.versionName != version } ?: return@get null
|
|
||||||
if (!app.isFree) return@get null
|
|
||||||
|
|
||||||
GPlayApp(
|
|
||||||
app.fileList.filterNot { it.url.isBlank() }.ifEmpty {
|
|
||||||
PurchaseHelper(authData).using(Http).purchase(
|
|
||||||
app.packageName,
|
|
||||||
app.versionCode,
|
|
||||||
app.offerType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) to app.versionName
|
|
||||||
}
|
|
||||||
|
|
||||||
download { app, outputStream ->
|
|
||||||
val apkDir = Files.createTempDirectory("play_dl")
|
|
||||||
try {
|
|
||||||
if (app.files.isEmpty()) error("No valid files to download")
|
|
||||||
app.files.forEach { file ->
|
|
||||||
if (file.type !in allowedFileTypes) error("${file.name} could not be downloaded because it has an unsupported type: ${file.type.name}")
|
|
||||||
apkDir.resolve(file.name).outputStream(StandardOpenOption.CREATE_NEW)
|
|
||||||
.use { stream ->
|
|
||||||
Http.download(stream) {
|
|
||||||
url(file.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val apkFiles = apkDir.listDirectoryEntries()
|
|
||||||
if (apkFiles.size == 1)
|
|
||||||
Files.copy(apkFiles.first(), outputStream)
|
|
||||||
else
|
|
||||||
Merger.merge(apkDir).writeApk(outputStream)
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
apkDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.data
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Parcelable
|
|
||||||
import com.aurora.gplayapi.helpers.AuthHelper
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.util.Properties
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class Credentials(val email: String, val aasToken: String) : Parcelable {
|
|
||||||
fun toAuthData(deviceProperties: Properties) =
|
|
||||||
AuthHelper.using(Http)
|
|
||||||
.build(email, aasToken, AuthHelper.Token.AAS, properties = deviceProperties)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Context.credentialsSharedPrefs() =
|
|
||||||
getSharedPreferences("credentials", Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
fun Context.saveCredentials(credentials: Credentials) {
|
|
||||||
with(credentialsSharedPrefs().edit()) {
|
|
||||||
putString(EMAIL_KEY, credentials.email)
|
|
||||||
putString(AAS_TOKEN_KEY, credentials.aasToken)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.readSavedCredentials() = with(credentialsSharedPrefs()) {
|
|
||||||
val email = getString(EMAIL_KEY, null) ?: return@with null
|
|
||||||
val aasToken = getString(AAS_TOKEN_KEY, null) ?: return@with null
|
|
||||||
|
|
||||||
Credentials(email, aasToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val EMAIL_KEY = "email"
|
|
||||||
private const val AAS_TOKEN_KEY = "aas_token"
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.data
|
|
||||||
|
|
||||||
import com.aurora.gplayapi.data.models.PlayResponse
|
|
||||||
import com.aurora.gplayapi.network.IHttpClient
|
|
||||||
import io.ktor.client.HttpClient
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.engine.okhttp.OkHttp
|
|
||||||
import io.ktor.client.plugins.HttpTimeout
|
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
|
||||||
import io.ktor.client.request.headers
|
|
||||||
import io.ktor.client.request.parameter
|
|
||||||
import io.ktor.client.request.prepareGet
|
|
||||||
import io.ktor.client.request.request
|
|
||||||
import io.ktor.client.request.setBody
|
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
|
||||||
import io.ktor.http.HttpMethod
|
|
||||||
import io.ktor.utils.io.jvm.javaio.copyTo
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
object Http : IHttpClient {
|
|
||||||
val client = HttpClient(OkHttp) {
|
|
||||||
install(HttpTimeout) {
|
|
||||||
socketTimeoutMillis = 10000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _responseCode = MutableStateFlow(100)
|
|
||||||
override val responseCode = _responseCode.asStateFlow()
|
|
||||||
|
|
||||||
suspend inline fun download(
|
|
||||||
outputStream: OutputStream,
|
|
||||||
block: HttpRequestBuilder.() -> Unit
|
|
||||||
) = client.prepareGet(block).execute {
|
|
||||||
it.bodyAsChannel().copyTo(outputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun playRequest(
|
|
||||||
method: HttpMethod,
|
|
||||||
url: String,
|
|
||||||
headers: Map<String, String>,
|
|
||||||
builder: suspend HttpRequestBuilder.() -> Unit = {}
|
|
||||||
) = runBlocking {
|
|
||||||
val response = client.request(url) {
|
|
||||||
this.method = method
|
|
||||||
this.headers {
|
|
||||||
headers.forEach { (name, value) ->
|
|
||||||
append(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder()
|
|
||||||
}
|
|
||||||
|
|
||||||
_responseCode.apply {
|
|
||||||
// State flows will not emit the same value twice
|
|
||||||
value = 0
|
|
||||||
value = response.status.value
|
|
||||||
}
|
|
||||||
|
|
||||||
val success = response.status.value in 200..299
|
|
||||||
PlayResponse(
|
|
||||||
code = response.status.value,
|
|
||||||
isSuccessful = success,
|
|
||||||
responseBytes = response.body(),
|
|
||||||
errorString = if (!success) response.status.description else ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(url: String, headers: Map<String, String>) =
|
|
||||||
playRequest(HttpMethod.Get, url, headers)
|
|
||||||
|
|
||||||
override fun get(
|
|
||||||
url: String,
|
|
||||||
headers: Map<String, String>,
|
|
||||||
params: Map<String, String>
|
|
||||||
) = playRequest(HttpMethod.Get, url, headers) {
|
|
||||||
params.forEach { (name, value) ->
|
|
||||||
parameter(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(url: String, headers: Map<String, String>, paramString: String) = playRequest(
|
|
||||||
HttpMethod.Get, url + paramString, headers
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getAuth(url: String) = PlayResponse(
|
|
||||||
isSuccessful = false,
|
|
||||||
code = 444
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun post(url: String, headers: Map<String, String>, body: ByteArray) = playRequest(
|
|
||||||
HttpMethod.Post, url, headers
|
|
||||||
) {
|
|
||||||
setBody(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun post(
|
|
||||||
url: String,
|
|
||||||
headers: Map<String, String>,
|
|
||||||
params: Map<String, String>
|
|
||||||
) = playRequest(HttpMethod.Post, url, headers) {
|
|
||||||
params.forEach { (name, value) ->
|
|
||||||
parameter(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun postAuth(url: String, body: ByteArray) = getAuth(url)
|
|
||||||
}
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.data
|
|
||||||
|
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.opengl.EGL14
|
|
||||||
import android.opengl.GLES10
|
|
||||||
import javax.microedition.khronos.egl.EGL10
|
|
||||||
import javax.microedition.khronos.egl.EGLConfig
|
|
||||||
import javax.microedition.khronos.egl.EGLContext
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.core.content.getSystemService
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.util.Properties
|
|
||||||
import javax.microedition.khronos.egl.EGLDisplay
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class ParcelProperties(val value: Properties) : Parcelable
|
|
||||||
|
|
||||||
object PropertiesProvider {
|
|
||||||
fun createDeviceProperties(context: Context) = with(context) {
|
|
||||||
Properties().apply {
|
|
||||||
//Build Props
|
|
||||||
setProperty("UserReadableName", "${Build.DEVICE}-default")
|
|
||||||
setProperty("Build.HARDWARE", Build.HARDWARE)
|
|
||||||
setProperty("Build.RADIO", Build.getRadioVersion() ?: "unknown")
|
|
||||||
setProperty("Build.FINGERPRINT", Build.FINGERPRINT)
|
|
||||||
setProperty("Build.BRAND", Build.BRAND)
|
|
||||||
setProperty("Build.DEVICE", Build.DEVICE)
|
|
||||||
setProperty("Build.VERSION.SDK_INT", "${Build.VERSION.SDK_INT}")
|
|
||||||
setProperty("Build.VERSION.RELEASE", Build.VERSION.RELEASE)
|
|
||||||
setProperty("Build.MODEL", Build.MODEL)
|
|
||||||
setProperty("Build.MANUFACTURER", Build.MANUFACTURER)
|
|
||||||
setProperty("Build.PRODUCT", Build.PRODUCT)
|
|
||||||
setProperty("Build.ID", Build.ID)
|
|
||||||
setProperty("Build.BOOTLOADER", Build.BOOTLOADER)
|
|
||||||
|
|
||||||
val config = resources.configuration
|
|
||||||
setProperty("TouchScreen", "${config.touchscreen}")
|
|
||||||
setProperty("Keyboard", "${config.keyboard}")
|
|
||||||
setProperty("Navigation", "${config.navigation}")
|
|
||||||
setProperty("ScreenLayout", "${config.screenLayout and 15}")
|
|
||||||
setProperty("HasHardKeyboard", "${config.keyboard == Configuration.KEYBOARD_QWERTY}")
|
|
||||||
setProperty(
|
|
||||||
"HasFiveWayNavigation",
|
|
||||||
"${config.navigation == Configuration.NAVIGATIONHIDDEN_YES}"
|
|
||||||
)
|
|
||||||
|
|
||||||
//Display Metrics
|
|
||||||
val metrics = resources.displayMetrics
|
|
||||||
setProperty("Screen.Density", "${metrics.densityDpi}")
|
|
||||||
setProperty("Screen.Width", "${metrics.widthPixels}")
|
|
||||||
setProperty("Screen.Height", "${metrics.heightPixels}")
|
|
||||||
|
|
||||||
|
|
||||||
//Supported Platforms
|
|
||||||
setProperty("Platforms", Build.SUPPORTED_ABIS.commaSeparated())
|
|
||||||
//Supported Features
|
|
||||||
setProperty("Features", features.commaSeparated())
|
|
||||||
//Shared Locales
|
|
||||||
setProperty("Locales", locales.commaSeparated())
|
|
||||||
//Shared Libraries
|
|
||||||
setProperty("SharedLibraries", sharedLibraries.commaSeparated())
|
|
||||||
//GL Extensions
|
|
||||||
setProperty(
|
|
||||||
"GL.Version",
|
|
||||||
getSystemService<ActivityManager>()!!.deviceConfigurationInfo.reqGlEsVersion.toString()
|
|
||||||
)
|
|
||||||
setProperty(
|
|
||||||
"GL.Extensions",
|
|
||||||
getEglExtensions().commaSeparated()
|
|
||||||
)
|
|
||||||
|
|
||||||
//Google Related Props
|
|
||||||
setProperty("Client", "android-google")
|
|
||||||
setProperty("GSF.version", "203615037")
|
|
||||||
setProperty("Vending.version", "82201710")
|
|
||||||
setProperty("Vending.versionString", "22.0.17-21 [0] [PR] 332555730")
|
|
||||||
|
|
||||||
//MISC
|
|
||||||
setProperty("Roaming", "mobile-notroaming")
|
|
||||||
setProperty("TimeZone", "UTC-10")
|
|
||||||
|
|
||||||
//Telephony (USA 3650 AT&T)
|
|
||||||
setProperty("CellOperator", "310")
|
|
||||||
setProperty("SimOperator", "38")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Array<String>.commaSeparated() = joinToString(separator = ",")
|
|
||||||
private fun Iterable<String>.commaSeparated() = joinToString(separator = ",")
|
|
||||||
|
|
||||||
private val Context.features
|
|
||||||
get() = packageManager.systemAvailableFeatures.map { it.name }
|
|
||||||
.filterNot(String::isNullOrEmpty)
|
|
||||||
private val Context.locales
|
|
||||||
get() = assets.locales.filter(String::isNullOrEmpty).map { it.replace("-", "_") }
|
|
||||||
private val Context.sharedLibraries
|
|
||||||
get() = packageManager.systemSharedLibraryNames.orEmpty().filterNotNull()
|
|
||||||
|
|
||||||
private fun getEglExtensions() = buildSet {
|
|
||||||
val egl = EGLContext.getEGL() as EGL10
|
|
||||||
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
|
||||||
egl.eglInitialize(display, IntArray(2))
|
|
||||||
try {
|
|
||||||
val numConfigsContainer = IntArray(1)
|
|
||||||
if (!egl.eglGetConfigs(display, null, 0, numConfigsContainer)) return@buildSet
|
|
||||||
val numConfigs = numConfigsContainer[0]
|
|
||||||
val configs = arrayOfNulls<EGLConfig>(numConfigs).apply {
|
|
||||||
if (!egl.eglGetConfigs(
|
|
||||||
display,
|
|
||||||
this,
|
|
||||||
numConfigs,
|
|
||||||
numConfigsContainer
|
|
||||||
)
|
|
||||||
) return@buildSet
|
|
||||||
}.requireNoNulls()
|
|
||||||
|
|
||||||
configs.forEach {
|
|
||||||
val resultContainer = IntArray(1)
|
|
||||||
egl.eglGetConfigAttrib(display, it, EGL10.EGL_CONFIG_CAVEAT, resultContainer)
|
|
||||||
if (resultContainer[0] == EGL10.EGL_SLOW_CONFIG) return@forEach
|
|
||||||
|
|
||||||
egl.eglGetConfigAttrib(display, it, EGL10.EGL_SURFACE_TYPE, resultContainer)
|
|
||||||
if (EGL10.EGL_PBUFFER_BIT and resultContainer[0] == 0) return@forEach
|
|
||||||
|
|
||||||
egl.eglGetConfigAttrib(display, it, EGL10.EGL_RENDERABLE_TYPE, resultContainer)
|
|
||||||
if (EGL14.EGL_OPENGL_ES_BIT and resultContainer[0] != 0) addEglExtensions(
|
|
||||||
egl,
|
|
||||||
display,
|
|
||||||
it,
|
|
||||||
null,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
if (EGL14.EGL_OPENGL_ES2_BIT and resultContainer[0] != 0) addEglExtensions(
|
|
||||||
egl,
|
|
||||||
display,
|
|
||||||
it,
|
|
||||||
openGlEs2AttribList,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
egl.eglTerminate(display)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEglExtensions(
|
|
||||||
egl: EGL10,
|
|
||||||
eglDisplay: EGLDisplay,
|
|
||||||
eglConfig: EGLConfig,
|
|
||||||
eglContextAttribList: IntArray?,
|
|
||||||
target: MutableSet<String>
|
|
||||||
) {
|
|
||||||
val eglContext =
|
|
||||||
egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, eglContextAttribList)
|
|
||||||
if (eglContext === EGL10.EGL_NO_CONTEXT) return
|
|
||||||
|
|
||||||
val eglSurface = egl.eglCreatePbufferSurface(eglDisplay, eglConfig, eglSurfaceAttribList)
|
|
||||||
if (eglSurface === EGL10.EGL_NO_SURFACE) {
|
|
||||||
egl.eglDestroyContext(eglDisplay, eglContext)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)
|
|
||||||
val extensionsString = GLES10.glGetString(GLES10.GL_EXTENSIONS)
|
|
||||||
if (!extensionsString.isNullOrEmpty()) {
|
|
||||||
val extensions = extensionsString.split(" ".toRegex())
|
|
||||||
target.addAll(extensions)
|
|
||||||
}
|
|
||||||
egl.eglMakeCurrent(
|
|
||||||
eglDisplay,
|
|
||||||
EGL10.EGL_NO_SURFACE,
|
|
||||||
EGL10.EGL_NO_SURFACE,
|
|
||||||
EGL10.EGL_NO_CONTEXT
|
|
||||||
)
|
|
||||||
egl.eglDestroySurface(eglDisplay, eglSurface)
|
|
||||||
egl.eglDestroyContext(eglDisplay, eglContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val eglSurfaceAttribList
|
|
||||||
get() = intArrayOf(
|
|
||||||
EGL10.EGL_WIDTH,
|
|
||||||
EGL10.EGL_PBUFFER_BIT,
|
|
||||||
EGL10.EGL_HEIGHT,
|
|
||||||
EGL10.EGL_PBUFFER_BIT,
|
|
||||||
EGL10.EGL_NONE
|
|
||||||
)
|
|
||||||
|
|
||||||
private val openGlEs2AttribList
|
|
||||||
get() = intArrayOf(
|
|
||||||
EGL14.EGL_CONTEXT_CLIENT_VERSION,
|
|
||||||
EGL10.EGL_PIXMAP_BIT,
|
|
||||||
EGL10.EGL_NONE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.service
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.util.Log
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.ICredentialProvider
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.ParcelProperties
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.PropertiesProvider
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.readSavedCredentials
|
|
||||||
|
|
||||||
class CredentialProviderService : Service() {
|
|
||||||
private val binder = object : ICredentialProvider.Stub() {
|
|
||||||
override fun retrieveCredentials() = try {
|
|
||||||
readSavedCredentials()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("CredentialService", "Got exception while retrieving credentials", e)
|
|
||||||
throw IllegalStateException("An exception was raised when reading credentials")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getProperties() =
|
|
||||||
ParcelProperties(PropertiesProvider.createDeviceProperties(this@CredentialProviderService))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder = binder.asBinder()
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.darkColorScheme
|
|
||||||
import androidx.compose.material3.lightColorScheme
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.kevinnzou.web.WebView
|
|
||||||
import com.kevinnzou.web.rememberWebViewState
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class AuthActivity : ComponentActivity() {
|
|
||||||
private val vm: AuthActivityViewModel by viewModels()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
val (code, intent) = vm.awaitActivityResultCode()
|
|
||||||
setResult(code, intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
|
|
||||||
|
|
||||||
MaterialTheme(colorScheme) {
|
|
||||||
Scaffold { paddingValues ->
|
|
||||||
Column(modifier = Modifier.padding(paddingValues)) {
|
|
||||||
val state =
|
|
||||||
rememberWebViewState(url = AuthActivityViewModel.EMBEDDED_SETUP_URL)
|
|
||||||
|
|
||||||
WebView(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
state = state,
|
|
||||||
onCreated = vm::setupWebView,
|
|
||||||
client = vm.webViewClient
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val RESULT_FAILED = RESULT_FIRST_USER + 1
|
|
||||||
const val FAILURE_MESSAGE_KEY = "FAIL_MESSAGE"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.play.store.ui
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
|
||||||
import android.webkit.CookieManager
|
|
||||||
import android.webkit.WebSettings
|
|
||||||
import android.webkit.WebView
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.LOG_TAG
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.Credentials
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.Http
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.PropertiesProvider
|
|
||||||
import app.revanced.manager.plugin.downloader.play.store.data.saveCredentials
|
|
||||||
import com.aurora.gplayapi.helpers.AuthValidator
|
|
||||||
import com.kevinnzou.web.AccompanistWebViewClient
|
|
||||||
import io.ktor.client.request.headers
|
|
||||||
import io.ktor.client.request.post
|
|
||||||
import io.ktor.client.request.setBody
|
|
||||||
import io.ktor.client.statement.bodyAsText
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.HttpHeaders
|
|
||||||
import io.ktor.http.append
|
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.StringTokenizer
|
|
||||||
|
|
||||||
class AuthActivityViewModel(app: Application) : AndroidViewModel(app) {
|
|
||||||
private val cookieManager = CookieManager.getInstance()!!
|
|
||||||
private val activityResultCode =
|
|
||||||
CompletableDeferred<Pair<Int, Intent?>>(parent = viewModelScope.coroutineContext.job)
|
|
||||||
|
|
||||||
val webViewClient = object : AccompanistWebViewClient() {
|
|
||||||
override fun onPageFinished(view: WebView, url: String?) {
|
|
||||||
super.onPageFinished(view, url)
|
|
||||||
|
|
||||||
val cookieString = cookieManager.getCookie(url) ?: return
|
|
||||||
val cookies = buildMap {
|
|
||||||
putAll(cookiePattern.findAll(cookieString).map {
|
|
||||||
val (key, value) = it.destructured
|
|
||||||
key to value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
cookies[OAUTH_COOKIE]?.let { token ->
|
|
||||||
view.evaluateJavascript(EXTRACT_EMAIL_JS) {
|
|
||||||
val email = it.replace("\"".toRegex(), "")
|
|
||||||
getAndSaveAasToken(email, token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
cookieManager.removeAllCookies(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun awaitActivityResultCode() = activityResultCode.await()
|
|
||||||
private fun finishActivity(code: Int, intent: Intent? = null) =
|
|
||||||
activityResultCode.complete(code to intent)
|
|
||||||
|
|
||||||
private fun getAndSaveAasToken(email: String, oauthToken: String) = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
val response = getAC2DMResponse(email, oauthToken)
|
|
||||||
val aasToken = response["Token"] ?: throw Exception("AC2DM did not return a token")
|
|
||||||
val credentials = Credentials(email, aasToken)
|
|
||||||
val context = getApplication<Application>()
|
|
||||||
val deviceProps = PropertiesProvider.createDeviceProperties(context)
|
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val authData = credentials.toAuthData(deviceProps)
|
|
||||||
val validator = AuthValidator(authData).using(Http)
|
|
||||||
|
|
||||||
if (!validator.isValid()) throw Exception("Credential validation failed")
|
|
||||||
context.saveCredentials(credentials)
|
|
||||||
}
|
|
||||||
|
|
||||||
finishActivity(Activity.RESULT_OK)
|
|
||||||
} catch (e: CancellationException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(LOG_TAG, "Could not get and save token", e)
|
|
||||||
finishActivity(AuthActivity.RESULT_FAILED, Intent().apply {
|
|
||||||
putExtra(AuthActivity.FAILURE_MESSAGE_KEY, e.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getAC2DMResponse(email: String, oauthToken: String): Map<String, String> {
|
|
||||||
val locale = Locale.getDefault()
|
|
||||||
val formData = mapOf(
|
|
||||||
"lang" to locale.toString().replace("_", "-"),
|
|
||||||
"google_play_services_version" to PLAY_SERVICES_VERSION_CODE,
|
|
||||||
"sdk_version" to BUILD_VERSION_SDK,
|
|
||||||
"device_country" to locale.country.lowercase(Locale.US),
|
|
||||||
"Email" to email,
|
|
||||||
"service" to "ac2dm",
|
|
||||||
"get_accountid" to 1,
|
|
||||||
"ACCESS_TOKEN" to 1,
|
|
||||||
"callerPkg" to GMS_PACKAGE_NAME,
|
|
||||||
"add_account" to 1,
|
|
||||||
"Token" to oauthToken,
|
|
||||||
"callerSig" to CALLER_SIGNATURE
|
|
||||||
).entries.joinToString(separator = "&") { (key, value) -> "$key=$value" }
|
|
||||||
|
|
||||||
val response = Http.client.post(TOKEN_AUTH_URL) {
|
|
||||||
headers {
|
|
||||||
set("app", GMS_PACKAGE_NAME)
|
|
||||||
set(HttpHeaders.UserAgent, "")
|
|
||||||
append(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
setBody(formData)
|
|
||||||
}
|
|
||||||
|
|
||||||
val body = response.bodyAsText()
|
|
||||||
return buildMap {
|
|
||||||
val st = StringTokenizer(body, "\n\r")
|
|
||||||
while (st.hasMoreTokens()) {
|
|
||||||
val (key, value) = st.nextToken().split("=", limit = 2)
|
|
||||||
put(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
fun setupWebView(webView: WebView) = webView.apply {
|
|
||||||
cookieManager.acceptThirdPartyCookies(this)
|
|
||||||
cookieManager.setAcceptThirdPartyCookies(this, true)
|
|
||||||
|
|
||||||
settings.apply {
|
|
||||||
safeBrowsingEnabled = false
|
|
||||||
allowContentAccess = true
|
|
||||||
databaseEnabled = true
|
|
||||||
domStorageEnabled = true
|
|
||||||
javaScriptEnabled = true
|
|
||||||
cacheMode = WebSettings.LOAD_DEFAULT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
companion object Constants {
|
|
||||||
private const val OAUTH_COOKIE = "oauth_token"
|
|
||||||
private const val EXTRACT_EMAIL_JS =
|
|
||||||
"(function() { return document.querySelector('[data-profile-identifier]').innerText; })();"
|
|
||||||
const val EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup"
|
|
||||||
private const val TOKEN_AUTH_URL = "https://android.clients.google.com/auth"
|
|
||||||
private const val BUILD_VERSION_SDK = 28
|
|
||||||
private const val PLAY_SERVICES_VERSION_CODE = 19629032
|
|
||||||
private const val GMS_PACKAGE_NAME = "com.google.android.gms"
|
|
||||||
private const val CALLER_SIGNATURE = "38918a453d07199354f8b19af05ec6562ced5788"
|
|
||||||
private val cookiePattern = "([^=]+)=([^;]*);?\\s?".toRegex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:fillColor="#3DDC84"
|
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="85.84757"
|
|
||||||
android:endY="92.4963"
|
|
||||||
android:startX="42.9492"
|
|
||||||
android:startY="49.59793"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name">ReVanced Manager: Play Store downloader</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample backup rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/guide/topics/data/autobackup
|
|
||||||
for details.
|
|
||||||
Note: This file is ignored for devices older that API 31
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore
|
|
||||||
-->
|
|
||||||
<full-backup-content>
|
|
||||||
<!--
|
|
||||||
<include domain="sharedpref" path="."/>
|
|
||||||
<exclude domain="sharedpref" path="device.xml"/>
|
|
||||||
-->
|
|
||||||
</full-backup-content>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample data extraction rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
|
||||||
for details.
|
|
||||||
-->
|
|
||||||
<data-extraction-rules>
|
|
||||||
<cloud-backup>
|
|
||||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
-->
|
|
||||||
</cloud-backup>
|
|
||||||
<!--
|
|
||||||
<device-transfer>
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
</device-transfer>
|
|
||||||
-->
|
|
||||||
</data-extraction-rules>
|
|
||||||
@@ -1,24 +1,7 @@
|
|||||||
# Project-wide Gradle settings.
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
org.gradle.jvmargs = -Xmx2048m -Dfile.encoding=UTF-8
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. For more details, visit
|
|
||||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
||||||
# Android operating system, and which are packaged with your app's APK
|
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
|
||||||
kotlin.code.style = official
|
|
||||||
# Enables namespacing of each library's R class so that its R class includes only the
|
|
||||||
# resources declared in the library itself and none from the library's dependencies,
|
|
||||||
# thereby reducing the size of the R class for that library
|
|
||||||
android.nonTransitiveRClass = true
|
android.nonTransitiveRClass = true
|
||||||
version = 1.1.0-dev.8
|
android.nonFinalResIds = false
|
||||||
|
kotlin.code.style = official
|
||||||
|
org.gradle.parallel = true
|
||||||
|
org.gradle.caching = true
|
||||||
|
version = 1.0.0
|
||||||
|
|||||||
@@ -1,35 +1,11 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.3"
|
|
||||||
kotlin = "2.1.10"
|
|
||||||
gplayapi = "3.4.4"
|
|
||||||
kotlinx-coroutines-core = "1.9.0"
|
|
||||||
ktor = "2.3.9"
|
|
||||||
|
|
||||||
compose-activity = "1.10.1"
|
|
||||||
ui-tooling = "1.8.2"
|
|
||||||
compose-bom = "2025.06.00"
|
|
||||||
material3 = "1.3.2"
|
|
||||||
compose-webview = "0.33.6"
|
|
||||||
|
|
||||||
plugin-api = "1.0.0"
|
plugin-api = "1.0.0"
|
||||||
|
android-gradle-plugin = "8.7.3"
|
||||||
|
kotlin = "2.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" }
|
|
||||||
plugin-api = { group = "app.revanced", name = "revanced-manager-downloader-api", version.ref = "plugin-api" }
|
plugin-api = { group = "app.revanced", name = "revanced-manager-downloader-api", version.ref = "plugin-api" }
|
||||||
gplayapi = { group = "com.auroraoss", name = "gplayapi", version.ref = "gplayapi" }
|
|
||||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
|
||||||
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
||||||
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "ui-tooling" }
|
|
||||||
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
|
||||||
compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "compose-activity" }
|
|
||||||
compose-webview = { group = "io.github.kevinnzou", name = "compose-webview", version.ref = "compose-webview" }
|
|
||||||
ktor-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
|
|
||||||
ktor-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" }
|
|
||||||
ktor-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
||||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version = "2.0.21" }
|
|
||||||
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
|
||||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,8 @@
|
|||||||
#Tue Jul 23 17:28:58 CEST 2024
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
|
||||||
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
297
gradlew
vendored
297
gradlew
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -15,69 +15,104 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
37
gradlew.bat
vendored
37
gradlew.bat
vendored
@@ -13,8 +13,10 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +27,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
1426
package-lock.json
generated
1426
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,6 @@
|
|||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.10.1",
|
"gradle-semantic-release-plugin": "^1.10.1",
|
||||||
"semantic-release": "^24.2.6"
|
"semantic-release": "^24.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
proguard-rules.pro
vendored
22
proguard-rules.pro
vendored
@@ -1,21 +1 @@
|
|||||||
# Add project specific ProGuard rules here.
|
-keep public class app.revanced.manager.plugin.downloader.apkmirror.*
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
rootProject.name = "revanced-manager-downloaders"
|
rootProject.name = "revanced-manager-apkmirror-downloader"
|
||||||
|
|
||||||
pluginManagement {
|
pluginManagement.repositories {
|
||||||
repositories {
|
gradlePluginPortal()
|
||||||
gradlePluginPortal()
|
google()
|
||||||
google()
|
}
|
||||||
maven {
|
|
||||||
name = "GitHubPackages"
|
dependencyResolutionManagement.repositories {
|
||||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
mavenCentral()
|
||||||
credentials {
|
google()
|
||||||
username = providers.gradleProperty("gpr.user").getOrElse(System.getenv("GITHUB_ACTOR"))
|
maven {
|
||||||
password = providers.gradleProperty("gpr.key").getOrElse(System.getenv("GITHUB_TOKEN"))
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":shared")
|
|
||||||
include(":arsclib")
|
|
||||||
file("downloaders").listFiles()
|
|
||||||
?.forEach {
|
|
||||||
include(":downloaders:${it.name}")
|
|
||||||
project(":downloaders:${it.name}").projectDir = file("downloaders/${it.name}")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.library)
|
|
||||||
alias(libs.plugins.kotlin.android)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.manager.plugin.shared"
|
|
||||||
compileSdk = 35
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 26
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
|
||||||
api(project(":arsclib"))
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.shared
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.reandroid.apk.APKLogger
|
|
||||||
import com.reandroid.apk.ApkBundle
|
|
||||||
import com.reandroid.apk.ApkModule
|
|
||||||
import com.reandroid.app.AndroidManifest
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
private object ArscLogger : APKLogger {
|
|
||||||
const val TAG = "ARSCLib"
|
|
||||||
|
|
||||||
override fun logMessage(msg: String) {
|
|
||||||
Log.i(TAG, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun logError(msg: String, tr: Throwable?) {
|
|
||||||
Log.e(TAG, msg, tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun logVerbose(msg: String) {
|
|
||||||
Log.v(TAG, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Merger {
|
|
||||||
companion object Factory {
|
|
||||||
suspend fun merge(apkDir: Path): ApkModule {
|
|
||||||
val closeables = mutableSetOf<Closeable>()
|
|
||||||
try {
|
|
||||||
// Merge splits
|
|
||||||
val merged = withContext(Dispatchers.Default) {
|
|
||||||
with(ApkBundle()) {
|
|
||||||
setAPKLogger(ArscLogger)
|
|
||||||
loadApkDirectory(apkDir.toFile())
|
|
||||||
closeables.addAll(modules)
|
|
||||||
mergeModules().also(closeables::add)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
merged.androidManifest.apply {
|
|
||||||
arrayOf(
|
|
||||||
AndroidManifest.ID_isSplitRequired,
|
|
||||||
AndroidManifest.ID_extractNativeLibs
|
|
||||||
).forEach {
|
|
||||||
applicationElement.removeAttributesWithId(it)
|
|
||||||
manifestElement.removeAttributesWithId(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
arrayOf(
|
|
||||||
AndroidManifest.NAME_requiredSplitTypes,
|
|
||||||
AndroidManifest.NAME_splitTypes
|
|
||||||
).forEach {
|
|
||||||
manifestElement.removeAttributeIf{ attribute -> attribute.name == it }
|
|
||||||
}
|
|
||||||
|
|
||||||
val pattern = "^com\\.android\\.(stamp|vending)\\.".toRegex()
|
|
||||||
applicationElement.removeElementsIf { element ->
|
|
||||||
if (element.name != AndroidManifest.TAG_meta_data) return@removeElementsIf false
|
|
||||||
val nameAttr =
|
|
||||||
element.getAttributes { it.nameId == AndroidManifest.ID_name }
|
|
||||||
.asSequence().single()
|
|
||||||
|
|
||||||
pattern.containsMatchIn(nameAttr.valueString)
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged
|
|
||||||
} finally {
|
|
||||||
closeables.forEach(Closeable::close)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@file:Suppress("Unused")
|
||||||
|
|
||||||
|
package app.revanced.manager.plugin.downloader.apkmirror
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import app.revanced.manager.plugin.downloader.webview.WebViewDownloader
|
||||||
|
|
||||||
|
val apkMirrorDownloader = WebViewDownloader { packageName, version ->
|
||||||
|
with(Uri.Builder()) {
|
||||||
|
scheme("https")
|
||||||
|
authority("www.apkmirror.com")
|
||||||
|
mapOf(
|
||||||
|
"post_type" to "app_release",
|
||||||
|
"searchtype" to "apk",
|
||||||
|
"s" to (version?.let { "$packageName $it" } ?: packageName),
|
||||||
|
"bundles%5B%5D" to "apk_files" // bundles[]
|
||||||
|
).forEach { (key, value) ->
|
||||||
|
appendQueryParameter(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
build().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user