mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-23 02:31:03 +00:00
Compare commits
1 Commits
dev
...
feat/strip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34664c9384 |
3
.github/workflows/build_pull_request.yml
vendored
3
.github/workflows/build_pull_request.yml
vendored
@@ -25,8 +25,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ env.GITHUB_ACTOR }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew :patches:buildAndroid --no-daemon
|
run: ./gradlew :patches:buildAndroid --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
|
|||||||
2
.github/workflows/pull_strings.yml
vendored
2
.github/workflows/pull_strings.yml
vendored
@@ -2,7 +2,7 @@ name: Pull strings
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 0"
|
- cron: "0 */12 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
7
.github/workflows/push_strings.yml
vendored
7
.github/workflows/push_strings.yml
vendored
@@ -16,11 +16,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Process strings
|
- name: Preprocess strings
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
run: ./gradlew clean preprocessCrowdinStrings
|
||||||
run: ./gradlew processStringsForCrowdin
|
|
||||||
|
|
||||||
- name: Push strings
|
- name: Push strings
|
||||||
uses: crowdin/github-action@v2
|
uses: crowdin/github-action@v2
|
||||||
|
|||||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -31,8 +31,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew :patches:buildAndroid clean
|
run: ./gradlew :patches:buildAndroid clean
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -56,8 +55,6 @@ jobs:
|
|||||||
id: release
|
id: release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Attest
|
- name: Attest
|
||||||
if: steps.release.outputs.new_release_published == 'true'
|
if: steps.release.outputs.new_release_published == 'true'
|
||||||
|
|||||||
164
CHANGELOG.md
164
CHANGELOG.md
@@ -1,167 +1,3 @@
|
|||||||
# [5.50.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.49.0-dev.1...v5.50.0-dev.1) (2026-01-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Nothing X:** Add `Show K1 token(s)` patch ([#6490](https://github.com/ReVanced/revanced-patches/issues/6490)) ([421cb28](https://github.com/ReVanced/revanced-patches/commit/421cb2899ef5c0f100fb8007bae8b89137d0e41c))
|
|
||||||
* **Strava:** Add `Hide distractions` patch ([#6479](https://github.com/ReVanced/revanced-patches/issues/6479)) ([66b0852](https://github.com/ReVanced/revanced-patches/commit/66b0852f8fa57c82b09997337a304374883d8ba5))
|
|
||||||
* **YouTube Music:** Add `Unlock Android Auto Media Browser` patch ([#6477](https://github.com/ReVanced/revanced-patches/issues/6477)) ([5edd9dc](https://github.com/ReVanced/revanced-patches/commit/5edd9dccae3b1ab4edf19771a771812e3c9ccf80))
|
|
||||||
|
|
||||||
# [5.50.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.0...v5.50.0-dev.1) (2026-01-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **YouTube Music:** Add `Unlock Android Auto Media Browser` patch ([#6477](https://github.com/ReVanced/revanced-patches/issues/6477)) ([89645dc](https://github.com/ReVanced/revanced-patches/commit/89645dcc2e13603b8f2fedb5e16231cb396e5965))
|
|
||||||
|
|
||||||
# [5.49.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.1-dev.1...v5.49.0-dev.1) (2026-01-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **YouTube Music:** Add `Hide layout components` patch ([#6365](https://github.com/ReVanced/revanced-patches/issues/6365)) ([71ce823](https://github.com/ReVanced/revanced-patches/commit/71ce8230a959dcaf2d8cd5dad1a4f21b88819aa0))
|
|
||||||
|
|
||||||
## [5.48.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.0...v5.48.1-dev.1) (2026-01-21)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Disable `Prevent screenshot detection` by default ([#6511](https://github.com/ReVanced/revanced-patches/issues/6511)) ([5b5c502](https://github.com/ReVanced/revanced-patches/commit/5b5c50254d533faa0e04d542f4859cbef610713e))
|
|
||||||
|
|
||||||
# [5.48.0](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0) (2026-01-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **Boost for Reddit - Fix missing audio in video downloads:** Make it work again by reflecting Reddits latest changes ([#6500](https://github.com/ReVanced/revanced-patches/issues/6500)) ([eecc44b](https://github.com/ReVanced/revanced-patches/commit/eecc44b9567bf2ca72ac99e0dafa483a6803c0f9))
|
|
||||||
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
|
|
||||||
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
|
|
||||||
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
|
|
||||||
* Fix compilation error introduced in dc69f243 ([#6392](https://github.com/ReVanced/revanced-patches/issues/6392)) ([a429824](https://github.com/ReVanced/revanced-patches/commit/a429824bb77b49aea14b0b54f2204ae24d5209a1))
|
|
||||||
* **Instagram:** `Sanitize sharing links` ([#6483](https://github.com/ReVanced/revanced-patches/issues/6483)) ([8724759](https://github.com/ReVanced/revanced-patches/commit/87247590de3db74680cb02ba1d87bf683b2269e2))
|
|
||||||
* **YouTube - Hide layout components:** Hide new type of crowdfunding box ([#6380](https://github.com/ReVanced/revanced-patches/issues/6380)) ([dc69f24](https://github.com/ReVanced/revanced-patches/commit/dc69f2433e2650654e2dffdd76b0b0c8a52bf515))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](https://github.com/ReVanced/revanced-patches/commit/4cc315952db557c565872de9e8484805f2e42305))
|
|
||||||
* Add `Prevent screenshot detection` patch ([#6482](https://github.com/ReVanced/revanced-patches/issues/6482)) ([83c0127](https://github.com/ReVanced/revanced-patches/commit/83c0127ebb8f53ab8a067758619faaac5596c145))
|
|
||||||
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](https://github.com/ReVanced/revanced-patches/commit/6312fe8d60da24465c0c1b0fa4e94ceb79873d9c))
|
|
||||||
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
|
|
||||||
* **Instagram:** Add `Hide highlights tray` patch ([#6489](https://github.com/ReVanced/revanced-patches/issues/6489)) ([8725a49](https://github.com/ReVanced/revanced-patches/commit/8725a49ba3a06fee0280ffcf4be62cd960cd301e))
|
|
||||||
* **Instagram:** Add `Remove build expired popup` patch ([#6488](https://github.com/ReVanced/revanced-patches/issues/6488)) ([18c0b04](https://github.com/ReVanced/revanced-patches/commit/18c0b04f0cd1bf8cd78b05af3b8ebe3a6a5f9e48))
|
|
||||||
* **Instagram:** Disable `Disable Reels scrolling` by default ([3401467](https://github.com/ReVanced/revanced-patches/commit/3401467a6d49fc75b6757a15e5c848330c1b7307))
|
|
||||||
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
|
|
||||||
* **ProtonVPN:** Add `Unlock split tunneling` patch ([#6353](https://github.com/ReVanced/revanced-patches/issues/6353)) ([e0f3346](https://github.com/ReVanced/revanced-patches/commit/e0f33468e6e96b9f10cf35ec67622d6488528c90))
|
|
||||||
* **SBS On Demand:** Add `Remove ads` patch ([#6378](https://github.com/ReVanced/revanced-patches/issues/6378)) ([315931c](https://github.com/ReVanced/revanced-patches/commit/315931cbf8f61cd4b3a54ace1ff03685d748614c))
|
|
||||||
* **Strava:** Add `Add 'Give Kudos' button to 'Group Activity'` patch ([#6475](https://github.com/ReVanced/revanced-patches/issues/6475)) ([4c4ba1c](https://github.com/ReVanced/revanced-patches/commit/4c4ba1c78c9f4568a2b572f5c69e9c6c734e1a7f))
|
|
||||||
* **Strava:** Add `Add media download` patch ([#6449](https://github.com/ReVanced/revanced-patches/issues/6449)) ([778d13c](https://github.com/ReVanced/revanced-patches/commit/778d13ce8b28ca6df3a665530320e4a21a27ae44))
|
|
||||||
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
|
|
||||||
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
|
|
||||||
* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](https://github.com/ReVanced/revanced-patches/commit/8f3f4c95bb8f151fc9a2c272bf7d0e905c2f01fc))
|
|
||||||
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
|
|
||||||
* **YouTube:** Add `Pause on audio interrupt` patch ([#6464](https://github.com/ReVanced/revanced-patches/issues/6464)) ([19f146c](https://github.com/ReVanced/revanced-patches/commit/19f146c01dc381b3cccd61e61ba4901872ff12d8))
|
|
||||||
|
|
||||||
# [5.48.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.12...v5.48.0-dev.13) (2026-01-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `Prevent screenshot detection` patch ([#6482](https://github.com/ReVanced/revanced-patches/issues/6482)) ([83c0127](https://github.com/ReVanced/revanced-patches/commit/83c0127ebb8f53ab8a067758619faaac5596c145))
|
|
||||||
|
|
||||||
# [5.48.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.11...v5.48.0-dev.12) (2026-01-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Instagram:** Add `Remove build expired popup` patch ([#6488](https://github.com/ReVanced/revanced-patches/issues/6488)) ([18c0b04](https://github.com/ReVanced/revanced-patches/commit/18c0b04f0cd1bf8cd78b05af3b8ebe3a6a5f9e48))
|
|
||||||
* **Strava:** Add `Add 'Give Kudos' button to 'Group Activity'` patch ([#6475](https://github.com/ReVanced/revanced-patches/issues/6475)) ([4c4ba1c](https://github.com/ReVanced/revanced-patches/commit/4c4ba1c78c9f4568a2b572f5c69e9c6c734e1a7f))
|
|
||||||
|
|
||||||
# [5.48.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.10...v5.48.0-dev.11) (2026-01-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Instagram:** Add `Hide highlights tray` patch ([#6489](https://github.com/ReVanced/revanced-patches/issues/6489)) ([8725a49](https://github.com/ReVanced/revanced-patches/commit/8725a49ba3a06fee0280ffcf4be62cd960cd301e))
|
|
||||||
|
|
||||||
# [5.48.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.9...v5.48.0-dev.10) (2026-01-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **Boost for Reddit - Fix missing audio in video downloads:** Make it work again by reflecting Reddits latest changes ([#6500](https://github.com/ReVanced/revanced-patches/issues/6500)) ([eecc44b](https://github.com/ReVanced/revanced-patches/commit/eecc44b9567bf2ca72ac99e0dafa483a6803c0f9))
|
|
||||||
* **Instagram:** `Sanitize sharing links` ([#6483](https://github.com/ReVanced/revanced-patches/issues/6483)) ([8724759](https://github.com/ReVanced/revanced-patches/commit/87247590de3db74680cb02ba1d87bf683b2269e2))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Instagram:** Disable `Disable Reels scrolling` by default ([3401467](https://github.com/ReVanced/revanced-patches/commit/3401467a6d49fc75b6757a15e5c848330c1b7307))
|
|
||||||
* **Strava:** Add `Add media download` patch ([#6449](https://github.com/ReVanced/revanced-patches/issues/6449)) ([778d13c](https://github.com/ReVanced/revanced-patches/commit/778d13ce8b28ca6df3a665530320e4a21a27ae44))
|
|
||||||
* **YouTube:** Add `Pause on audio interrupt` patch ([#6464](https://github.com/ReVanced/revanced-patches/issues/6464)) ([19f146c](https://github.com/ReVanced/revanced-patches/commit/19f146c01dc381b3cccd61e61ba4901872ff12d8))
|
|
||||||
|
|
||||||
# [5.48.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.8...v5.48.0-dev.9) (2026-01-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](https://github.com/ReVanced/revanced-patches/commit/4cc315952db557c565872de9e8484805f2e42305))
|
|
||||||
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](https://github.com/ReVanced/revanced-patches/commit/6312fe8d60da24465c0c1b0fa4e94ceb79873d9c))
|
|
||||||
|
|
||||||
# [5.48.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.7...v5.48.0-dev.8) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
|
|
||||||
|
|
||||||
# [5.48.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.6...v5.48.0-dev.7) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
|
|
||||||
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
|
|
||||||
|
|
||||||
# [5.48.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.5...v5.48.0-dev.6) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
|
|
||||||
|
|
||||||
# [5.48.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.4...v5.48.0-dev.5) (2025-12-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
|
|
||||||
|
|
||||||
# [5.48.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.3...v5.48.0-dev.4) (2025-12-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
|
|
||||||
|
|
||||||
# [5.48.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.2...v5.48.0-dev.3) (2025-12-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
|
|
||||||
|
|
||||||
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](https://github.com/ReVanced/revanced-patches/commit/8f3f4c95bb8f151fc9a2c272bf7d0e905c2f01fc))
|
|
||||||
|
|
||||||
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)
|
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
project_id_env: "CROWDIN_PROJECT_ID"
|
project_id_env: "CROWDIN_PROJECT_ID"
|
||||||
api_token_env: "CROWDIN_PERSONAL_TOKEN"
|
api_token_env: "CROWDIN_PERSONAL_TOKEN"
|
||||||
|
|
||||||
preserve_hierarchy: true
|
preserve_hierarchy: false
|
||||||
files:
|
files:
|
||||||
- source: patches/src/main/resources/addresources/values/strings.xml
|
- source: patches/src/main/resources/addresources/values/strings.xml
|
||||||
dest: patches.xml
|
|
||||||
translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
|
translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
|
||||||
skip_untranslated_strings: true
|
skip_untranslated_strings: true
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 21
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
aidl = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(libs.annotation)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
|
|
||||||
|
|
||||||
interface IExpressIntegrityService {
|
|
||||||
oneway void requestIntegrityToken(in Bundle request, IExpressIntegrityServiceCallback callback) = 2;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
interface IExpressIntegrityServiceCallback {
|
|
||||||
oneway void onRequestExpressIntegrityTokenResult(in Bundle result) = 2;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
|
|
||||||
|
|
||||||
interface IIntegrityService {
|
|
||||||
oneway void requestIntegrityToken(in Bundle request, IIntegrityServiceCallback callback) = 1;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
interface IIntegrityServiceCallback {
|
|
||||||
oneway void onResult(in Bundle result) = 1;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package android.ext;
|
|
||||||
/** @hide */
|
|
||||||
// Int values that are assigned to packages in this interface can be retrieved at runtime from
|
|
||||||
// ApplicationInfo.ext().getPackageId() or from AndroidPackage.ext().getPackageId() (in system_server).
|
|
||||||
//
|
|
||||||
// PackageIds are assigned to parsed APKs only after they are verified, either by a certificate check
|
|
||||||
// or by a check that the APK is stored on an immutable OS partition.
|
|
||||||
public interface PackageId {
|
|
||||||
String PLAY_STORE_NAME = "com.android.vending";
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package android.os;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
public class BinderWrapper implements IBinder {
|
|
||||||
protected final IBinder base;
|
|
||||||
|
|
||||||
public BinderWrapper(IBinder base) {
|
|
||||||
this.base = base;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
|
|
||||||
return base.transact(code, data, reply, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public IInterface queryLocalInterface(@NonNull String descriptor) {
|
|
||||||
return base.queryLocalInterface(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getInterfaceDescriptor() throws RemoteException {
|
|
||||||
return base.getInterfaceDescriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pingBinder() {
|
|
||||||
return base.pingBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBinderAlive() {
|
|
||||||
return base.isBinderAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
|
|
||||||
base.dump(fd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
|
|
||||||
base.dumpAsync(fd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException {
|
|
||||||
base.linkToDeath(recipient, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
|
|
||||||
return base.unlinkToDeath(recipient, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.internal.os.FakeBackgroundHandler;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityService;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
|
|
||||||
|
|
||||||
class ClassicPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
|
|
||||||
|
|
||||||
ClassicPlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
requestIntegrityTokenTxnCode = 2; // IIntegrityService.Stub.TRANSACTION_requestIntegrityToken
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TokenRequestStub extends IIntegrityService.Stub {
|
|
||||||
public void requestIntegrityToken(Bundle request, IIntegrityServiceCallback callback) {
|
|
||||||
Runnable r = () -> {
|
|
||||||
var result = new Bundle();
|
|
||||||
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityErrorCode.html#API_NOT_AVAILABLE
|
|
||||||
final int API_NOT_AVAILABLE = -1;
|
|
||||||
result.putInt("error", API_NOT_AVAILABLE);
|
|
||||||
try {
|
|
||||||
callback.onResult(result);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e("IIntegrityService.Stub", "", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Binder createTokenRequestStub() {
|
|
||||||
return new TokenRequestStub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.BinderWrapper;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
abstract class PlayIntegrityServiceWrapper extends BinderWrapper {
|
|
||||||
final String TAG;
|
|
||||||
protected int requestIntegrityTokenTxnCode;
|
|
||||||
|
|
||||||
public PlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
TAG = getClass().getSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Binder createTokenRequestStub();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean transact(int code, Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
|
|
||||||
if (code == requestIntegrityTokenTxnCode) {
|
|
||||||
if (maybeStubOutIntegrityTokenRequest(code, data, reply, flags)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.transact(code, data, reply, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean maybeStubOutIntegrityTokenRequest(int code, Parcel data, @Nullable Parcel reply, int flags) {
|
|
||||||
Log.d(TAG, "integrity token request detected");
|
|
||||||
|
|
||||||
try {
|
|
||||||
createTokenRequestStub().transact(code, data, reply, flags);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// this is a local call
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static long getTokenRequestResultDelay() {
|
|
||||||
return 500L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.ext.PackageId;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import app.grapheneos.gmscompat.lib.util.ServiceConnectionWrapper;
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class PlayIntegrityUtils {
|
|
||||||
|
|
||||||
public static @Nullable ServiceConnection maybeReplaceServiceConnection(Intent service, ServiceConnection orig) {
|
|
||||||
if (PackageId.PLAY_STORE_NAME.equals(service.getPackage())) {
|
|
||||||
UnaryOperator<IBinder> binderOverride = null;
|
|
||||||
|
|
||||||
final String CLASSIC_SERVICE =
|
|
||||||
"com.google.android.play.core.integrityservice.BIND_INTEGRITY_SERVICE";
|
|
||||||
final String STANDARD_SERVICE =
|
|
||||||
"com.google.android.play.core.expressintegrityservice.BIND_EXPRESS_INTEGRITY_SERVICE";
|
|
||||||
|
|
||||||
String action = service.getAction();
|
|
||||||
if (STANDARD_SERVICE.equals(action)) {
|
|
||||||
binderOverride = StandardPlayIntegrityServiceWrapper::new;
|
|
||||||
} else if (CLASSIC_SERVICE.equals(action)) {
|
|
||||||
binderOverride = ClassicPlayIntegrityServiceWrapper::new;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binderOverride != null) {
|
|
||||||
return new ServiceConnectionWrapper(orig, binderOverride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.android.internal.os.FakeBackgroundHandler;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityService;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
|
|
||||||
|
|
||||||
@SuppressLint("LongLogTag")
|
|
||||||
class StandardPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
|
|
||||||
|
|
||||||
StandardPlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
requestIntegrityTokenTxnCode = 3; // IExpressIntegrityService.Stub.TRANSACTION_requestIntegrityToken
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TokenRequestStub extends IExpressIntegrityService.Stub {
|
|
||||||
public void requestIntegrityToken(Bundle request, IExpressIntegrityServiceCallback callback) {
|
|
||||||
Runnable r = () -> {
|
|
||||||
var result = new Bundle();
|
|
||||||
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/StandardIntegrityErrorCode.html#API_NOT_AVAILABLE
|
|
||||||
final int API_NOT_AVAILABLE = -1;
|
|
||||||
result.putInt("error", API_NOT_AVAILABLE);
|
|
||||||
try {
|
|
||||||
callback.onRequestExpressIntegrityTokenResult(result);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e("IExpressIntegrityService.Stub", "", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Binder createTokenRequestStub() {
|
|
||||||
return new TokenRequestStub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.util;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IBinder;
|
|
||||||
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class ServiceConnectionWrapper implements ServiceConnection {
|
|
||||||
private final ServiceConnection base;
|
|
||||||
private final UnaryOperator<IBinder> binderOverride;
|
|
||||||
|
|
||||||
public ServiceConnectionWrapper(ServiceConnection base, UnaryOperator<IBinder> binderOverride) {
|
|
||||||
this.base = base;
|
|
||||||
this.binderOverride = binderOverride;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
IBinder override = binderOverride.apply(service);
|
|
||||||
if (override != null) {
|
|
||||||
service = override;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.onServiceConnected(name, service);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
base.onServiceDisconnected(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindingDied(ComponentName name) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
base.onBindingDied(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNullBinding(ComponentName name) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
base.onNullBinding(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.extension.playintegrity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import app.grapheneos.gmscompat.lib.playintegrity.PlayIntegrityUtils;
|
|
||||||
|
|
||||||
public class DisablePlayIntegrityPatch {
|
|
||||||
public static boolean bindService(Context context, Intent service, ServiceConnection conn, int flags) {
|
|
||||||
ServiceConnection override = PlayIntegrityUtils.maybeReplaceServiceConnection(service, conn);
|
|
||||||
if (override != null) {
|
|
||||||
conn = override;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.bindService(service, conn, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.android.internal.os;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
public class FakeBackgroundHandler {
|
|
||||||
|
|
||||||
public static Handler getHandler() {
|
|
||||||
return new Handler(Looper.getMainLooper());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,12 +4,12 @@ import static java.lang.Boolean.FALSE;
|
|||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.shared.settings.EnumSetting;
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
import app.revanced.extension.shared.spoof.ClientType;
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
public class Settings extends YouTubeAndMusicSettings {
|
public class Settings extends BaseSettings {
|
||||||
|
|
||||||
// Ads
|
// Ads
|
||||||
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
|
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
dependencies {
|
|
||||||
compileOnly(project(":extensions:shared:library"))
|
|
||||||
compileOnly(project(":extensions:nothingx:stub"))
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 26
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,590 +0,0 @@
|
|||||||
package app.revanced.extension.nothingx.patches;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Patches to expose the K1 token for Nothing X app to enable pairing with GadgetBridge.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class ShowK1TokensPatch {
|
|
||||||
|
|
||||||
private static final String TAG = "ReVanced";
|
|
||||||
private static final String PACKAGE_NAME = "com.nothing.smartcenter";
|
|
||||||
private static final String EMPTY_MD5 = "d41d8cd98f00b204e9800998ecf8427e";
|
|
||||||
private static final String PREFS_NAME = "revanced_nothingx_prefs";
|
|
||||||
private static final String KEY_DONT_SHOW_DIALOG = "dont_show_k1_dialog";
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
private static final int COLOR_BG = 0xFF1E1E1E;
|
|
||||||
private static final int COLOR_CARD = 0xFF2D2D2D;
|
|
||||||
private static final int COLOR_TEXT_PRIMARY = 0xFFFFFFFF;
|
|
||||||
private static final int COLOR_TEXT_SECONDARY = 0xFFB0B0B0;
|
|
||||||
private static final int COLOR_ACCENT = 0xFFFF9500;
|
|
||||||
private static final int COLOR_TOKEN_BG = 0xFF3A3A3A;
|
|
||||||
private static final int COLOR_BUTTON_POSITIVE = 0xFFFF9500;
|
|
||||||
private static final int COLOR_BUTTON_NEGATIVE = 0xFFFF6B6B;
|
|
||||||
|
|
||||||
// Match standalone K1: k1:, K1:, k1>, etc.
|
|
||||||
private static final Pattern K1_STANDALONE_PATTERN = Pattern.compile("(?i)(?:k1\\s*[:>]\\s*)([0-9a-f]{32})");
|
|
||||||
// Match combined r3+k1: format (64 chars = r3(32) + k1(32))
|
|
||||||
private static final Pattern K1_COMBINED_PATTERN = Pattern.compile("(?i)r3\\+k1\\s*:\\s*([0-9a-f]{64})");
|
|
||||||
|
|
||||||
private static volatile boolean k1Logged = false;
|
|
||||||
private static volatile boolean lifecycleCallbacksRegistered = false;
|
|
||||||
private static Context appContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get K1 tokens from database and log files.
|
|
||||||
* Call this after the app initializes.
|
|
||||||
*
|
|
||||||
* @param context Application context
|
|
||||||
*/
|
|
||||||
public static void showK1Tokens(Context context) {
|
|
||||||
if (k1Logged) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
appContext = context.getApplicationContext();
|
|
||||||
|
|
||||||
Set<String> allTokens = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
// First try to get from database.
|
|
||||||
String dbToken = getK1TokensFromDatabase();
|
|
||||||
if (dbToken != null) {
|
|
||||||
allTokens.add(dbToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then get from log files.
|
|
||||||
Set<String> logTokens = getK1TokensFromLogFiles();
|
|
||||||
allTokens.addAll(logTokens);
|
|
||||||
|
|
||||||
if (allTokens.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log all found tokens.
|
|
||||||
int index = 1;
|
|
||||||
for (String token : allTokens) {
|
|
||||||
Log.i(TAG, "#" + index++ + ": " + token.toUpperCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register lifecycle callbacks to show dialog when an Activity is ready.
|
|
||||||
registerLifecycleCallbacks(allTokens);
|
|
||||||
|
|
||||||
k1Logged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register ActivityLifecycleCallbacks to show dialog when first Activity resumes.
|
|
||||||
*
|
|
||||||
* @param tokens Set of K1 tokens to display
|
|
||||||
*/
|
|
||||||
private static void registerLifecycleCallbacks(Set<String> tokens) {
|
|
||||||
if (lifecycleCallbacksRegistered || !(appContext instanceof Application)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Application application = (Application) appContext;
|
|
||||||
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityStarted(Activity activity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResumed(Activity activity) {
|
|
||||||
// Check if user chose not to show dialog.
|
|
||||||
SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
||||||
if (prefs.getBoolean(KEY_DONT_SHOW_DIALOG, false)) {
|
|
||||||
application.unregisterActivityLifecycleCallbacks(this);
|
|
||||||
lifecycleCallbacksRegistered = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show dialog on first Activity resume.
|
|
||||||
if (tokens != null && !tokens.isEmpty()) {
|
|
||||||
activity.runOnUiThread(() -> showK1TokensDialog(activity, tokens));
|
|
||||||
// Unregister after showing
|
|
||||||
application.unregisterActivityLifecycleCallbacks(this);
|
|
||||||
lifecycleCallbacksRegistered = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityPaused(Activity activity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityStopped(Activity activity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityDestroyed(Activity activity) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lifecycleCallbacksRegistered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show dialog with K1 tokens.
|
|
||||||
*
|
|
||||||
* @param activity Activity context
|
|
||||||
* @param tokens Set of K1 tokens
|
|
||||||
*/
|
|
||||||
private static void showK1TokensDialog(Activity activity, Set<String> tokens) {
|
|
||||||
try {
|
|
||||||
// Create main container.
|
|
||||||
LinearLayout mainLayout = new LinearLayout(activity);
|
|
||||||
mainLayout.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
mainLayout.setBackgroundColor(COLOR_BG);
|
|
||||||
mainLayout.setPadding(dpToPx(activity, 24), dpToPx(activity, 16),
|
|
||||||
dpToPx(activity, 24), dpToPx(activity, 16));
|
|
||||||
|
|
||||||
// Title.
|
|
||||||
TextView titleView = new TextView(activity);
|
|
||||||
titleView.setText("K1 Token(s) Found");
|
|
||||||
titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
|
||||||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
|
||||||
titleView.setTextColor(COLOR_TEXT_PRIMARY);
|
|
||||||
titleView.setGravity(Gravity.CENTER);
|
|
||||||
mainLayout.addView(titleView, new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
));
|
|
||||||
|
|
||||||
// Subtitle.
|
|
||||||
TextView subtitleView = new TextView(activity);
|
|
||||||
subtitleView.setText(tokens.size() == 1 ? "1 token found • Tap to copy" : tokens.size() + " tokens found • Tap to copy");
|
|
||||||
subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
subtitleView.setTextColor(COLOR_TEXT_SECONDARY);
|
|
||||||
subtitleView.setGravity(Gravity.CENTER);
|
|
||||||
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
subtitleParams.topMargin = dpToPx(activity, 4);
|
|
||||||
subtitleParams.bottomMargin = dpToPx(activity, 16);
|
|
||||||
mainLayout.addView(subtitleView, subtitleParams);
|
|
||||||
|
|
||||||
// Scrollable content.
|
|
||||||
ScrollView scrollView = new ScrollView(activity);
|
|
||||||
scrollView.setVerticalScrollBarEnabled(false);
|
|
||||||
LinearLayout.LayoutParams scrollParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
0,
|
|
||||||
1.0f
|
|
||||||
);
|
|
||||||
scrollParams.topMargin = dpToPx(activity, 8);
|
|
||||||
scrollParams.bottomMargin = dpToPx(activity, 16);
|
|
||||||
mainLayout.addView(scrollView, scrollParams);
|
|
||||||
|
|
||||||
LinearLayout tokensContainer = new LinearLayout(activity);
|
|
||||||
tokensContainer.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
scrollView.addView(tokensContainer);
|
|
||||||
|
|
||||||
// Add each token as a card.
|
|
||||||
boolean singleToken = tokens.size() == 1;
|
|
||||||
int index = 1;
|
|
||||||
for (String token : tokens) {
|
|
||||||
LinearLayout tokenCard = createTokenCard(activity, token, index++, singleToken);
|
|
||||||
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
cardParams.bottomMargin = dpToPx(activity, 12);
|
|
||||||
tokensContainer.addView(tokenCard, cardParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info text.
|
|
||||||
TextView infoView = new TextView(activity);
|
|
||||||
infoView.setText(tokens.size() == 1 ? "Tap the token to copy it" : "Tap any token to copy it");
|
|
||||||
infoView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
|
||||||
infoView.setTextColor(COLOR_TEXT_SECONDARY);
|
|
||||||
infoView.setGravity(Gravity.CENTER);
|
|
||||||
infoView.setAlpha(0.7f);
|
|
||||||
LinearLayout.LayoutParams infoParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
infoParams.topMargin = dpToPx(activity, 8);
|
|
||||||
mainLayout.addView(infoView, infoParams);
|
|
||||||
|
|
||||||
// Button row.
|
|
||||||
LinearLayout buttonRow = new LinearLayout(activity);
|
|
||||||
buttonRow.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
buttonRow.setGravity(Gravity.END);
|
|
||||||
LinearLayout.LayoutParams buttonRowParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
buttonRowParams.topMargin = dpToPx(activity, 16);
|
|
||||||
mainLayout.addView(buttonRow, buttonRowParams);
|
|
||||||
|
|
||||||
// "Don't show again" button.
|
|
||||||
Button dontShowButton = new Button(activity);
|
|
||||||
dontShowButton.setText("Don't show again");
|
|
||||||
dontShowButton.setTextColor(Color.WHITE);
|
|
||||||
dontShowButton.setBackgroundColor(Color.TRANSPARENT);
|
|
||||||
dontShowButton.setAllCaps(false);
|
|
||||||
dontShowButton.setTypeface(Typeface.DEFAULT);
|
|
||||||
dontShowButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
dontShowButton.setPadding(dpToPx(activity, 16), dpToPx(activity, 8),
|
|
||||||
dpToPx(activity, 16), dpToPx(activity, 8));
|
|
||||||
LinearLayout.LayoutParams dontShowParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
dontShowParams.rightMargin = dpToPx(activity, 8);
|
|
||||||
buttonRow.addView(dontShowButton, dontShowParams);
|
|
||||||
|
|
||||||
// "OK" button.
|
|
||||||
Button okButton = new Button(activity);
|
|
||||||
okButton.setText("OK");
|
|
||||||
okButton.setTextColor(Color.BLACK);
|
|
||||||
okButton.setBackgroundColor(COLOR_BUTTON_POSITIVE);
|
|
||||||
okButton.setAllCaps(false);
|
|
||||||
okButton.setTypeface(Typeface.DEFAULT_BOLD);
|
|
||||||
okButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
okButton.setPadding(dpToPx(activity, 24), dpToPx(activity, 12),
|
|
||||||
dpToPx(activity, 24), dpToPx(activity, 12));
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
okButton.setElevation(dpToPx(activity, 4));
|
|
||||||
}
|
|
||||||
buttonRow.addView(okButton, new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
));
|
|
||||||
|
|
||||||
// Build dialog.
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
|
||||||
builder.setView(mainLayout);
|
|
||||||
|
|
||||||
final AlertDialog dialog = builder.create();
|
|
||||||
|
|
||||||
// Style the dialog with dark background.
|
|
||||||
if (dialog.getWindow() != null) {
|
|
||||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
// Set button click listeners after dialog is created.
|
|
||||||
dontShowButton.setOnClickListener(v -> {
|
|
||||||
SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
||||||
prefs.edit().putBoolean(KEY_DONT_SHOW_DIALOG, true).apply();
|
|
||||||
Toast.makeText(activity, "Dialog disabled. Clear app data to re-enable.",
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
|
|
||||||
okButton.setOnClickListener(v -> {
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Failed to show K1 dialog", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a card view for a single token.
|
|
||||||
*/
|
|
||||||
private static LinearLayout createTokenCard(Activity activity, String token, int index, boolean singleToken) {
|
|
||||||
LinearLayout card = new LinearLayout(activity);
|
|
||||||
card.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
card.setBackgroundColor(COLOR_TOKEN_BG);
|
|
||||||
card.setPadding(dpToPx(activity, 16), dpToPx(activity, 12),
|
|
||||||
dpToPx(activity, 16), dpToPx(activity, 12));
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
card.setElevation(dpToPx(activity, 2));
|
|
||||||
}
|
|
||||||
card.setClickable(true);
|
|
||||||
card.setFocusable(true);
|
|
||||||
|
|
||||||
// Token label (only show if multiple tokens).
|
|
||||||
if (!singleToken) {
|
|
||||||
TextView labelView = new TextView(activity);
|
|
||||||
labelView.setText("Token #" + index);
|
|
||||||
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
|
||||||
labelView.setTextColor(COLOR_ACCENT);
|
|
||||||
labelView.setTypeface(Typeface.DEFAULT_BOLD);
|
|
||||||
card.addView(labelView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Token value.
|
|
||||||
TextView tokenView = new TextView(activity);
|
|
||||||
tokenView.setText(token.toUpperCase());
|
|
||||||
tokenView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
|
|
||||||
tokenView.setTextColor(COLOR_TEXT_PRIMARY);
|
|
||||||
tokenView.setTypeface(Typeface.MONOSPACE);
|
|
||||||
tokenView.setLetterSpacing(0.05f);
|
|
||||||
LinearLayout.LayoutParams tokenParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
if (!singleToken) {
|
|
||||||
tokenParams.topMargin = dpToPx(activity, 8);
|
|
||||||
}
|
|
||||||
card.addView(tokenView, tokenParams);
|
|
||||||
|
|
||||||
// Click to copy.
|
|
||||||
card.setOnClickListener(v -> {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
if (clipboard != null) {
|
|
||||||
clipboard.setText(token.toUpperCase());
|
|
||||||
Toast.makeText(activity, "Token copied!", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert dp to pixels.
|
|
||||||
*/
|
|
||||||
private static int dpToPx(Context context, float dp) {
|
|
||||||
return (int) TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
dp,
|
|
||||||
context.getResources().getDisplayMetrics()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get K1 tokens from log files.
|
|
||||||
* Prioritizes pairing K1 tokens over reconnect tokens.
|
|
||||||
*/
|
|
||||||
private static Set<String> getK1TokensFromLogFiles() {
|
|
||||||
Set<String> pairingTokens = new LinkedHashSet<>();
|
|
||||||
Set<String> reconnectTokens = new LinkedHashSet<>();
|
|
||||||
try {
|
|
||||||
File logDir = new File("/data/data/" + PACKAGE_NAME + "/files/log");
|
|
||||||
if (!logDir.exists() || !logDir.isDirectory()) {
|
|
||||||
return pairingTokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
File[] logFiles = logDir.listFiles((dir, name) ->
|
|
||||||
name.endsWith(".log") || name.endsWith(".log.") || name.matches(".*\\.log\\.\\d+"));
|
|
||||||
|
|
||||||
if (logFiles == null || logFiles.length == 0) {
|
|
||||||
return pairingTokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File logFile : logFiles) {
|
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
// Determine if this is a pairing or reconnect context.
|
|
||||||
boolean isPairingContext = line.toLowerCase().contains("watchbind");
|
|
||||||
boolean isReconnectContext = line.toLowerCase().contains("watchreconnect");
|
|
||||||
|
|
||||||
String k1Token = null;
|
|
||||||
|
|
||||||
// First check for combined r3+k1 format (priority).
|
|
||||||
Matcher combinedMatcher = K1_COMBINED_PATTERN.matcher(line);
|
|
||||||
if (combinedMatcher.find()) {
|
|
||||||
String combined = combinedMatcher.group(1);
|
|
||||||
if (combined.length() == 64) {
|
|
||||||
// Second half is the actual K1
|
|
||||||
k1Token = combined.substring(32).toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for standalone K1 format (only if not found in combined).
|
|
||||||
if (k1Token == null) {
|
|
||||||
Matcher standaloneMatcher = K1_STANDALONE_PATTERN.matcher(line);
|
|
||||||
if (standaloneMatcher.find()) {
|
|
||||||
String token = standaloneMatcher.group(1);
|
|
||||||
if (token != null && token.length() == 32) {
|
|
||||||
k1Token = token.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to appropriate set.
|
|
||||||
if (k1Token != null) {
|
|
||||||
if (isPairingContext && !isReconnectContext) {
|
|
||||||
pairingTokens.add(k1Token);
|
|
||||||
} else {
|
|
||||||
reconnectTokens.add(k1Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Skip unreadable files.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// Fail silently.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return pairing tokens first, add reconnect tokens if no pairing tokens found.
|
|
||||||
if (!pairingTokens.isEmpty()) {
|
|
||||||
Log.i(TAG, "Found " + pairingTokens.size() + " pairing K1 token(s)");
|
|
||||||
return pairingTokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reconnectTokens.isEmpty()) {
|
|
||||||
Log.i(TAG, "Found " + reconnectTokens.size() + " reconnect K1 token(s) (may not work for initial pairing)");
|
|
||||||
}
|
|
||||||
return reconnectTokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to get K1 tokens from the database.
|
|
||||||
*/
|
|
||||||
private static String getK1TokensFromDatabase() {
|
|
||||||
try {
|
|
||||||
File dbDir = new File("/data/data/" + PACKAGE_NAME + "/databases");
|
|
||||||
if (!dbDir.exists() || !dbDir.isDirectory()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
File[] dbFiles = dbDir.listFiles((dir, name) ->
|
|
||||||
name.endsWith(".db") && !name.startsWith("google_app_measurement") && !name.contains("firebase"));
|
|
||||||
|
|
||||||
if (dbFiles == null || dbFiles.length == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File dbFile : dbFiles) {
|
|
||||||
String token = getK1TokensFromDatabase(dbFile);
|
|
||||||
if (token != null) {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract K1 tokens from a database file.
|
|
||||||
*/
|
|
||||||
private static String getK1TokensFromDatabase(File dbFile) {
|
|
||||||
SQLiteDatabase db = null;
|
|
||||||
try {
|
|
||||||
db = SQLiteDatabase.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
|
|
||||||
|
|
||||||
// Get all tables.
|
|
||||||
Cursor cursor = db.rawQuery(
|
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
List<String> tables = new ArrayList<>();
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
tables.add(cursor.getString(0));
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
|
|
||||||
// Scan all columns for 32-char hex strings.
|
|
||||||
for (String table : tables) {
|
|
||||||
Cursor schemaCursor = null;
|
|
||||||
try {
|
|
||||||
schemaCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
|
|
||||||
List<String> columns = new ArrayList<>();
|
|
||||||
while (schemaCursor.moveToNext()) {
|
|
||||||
columns.add(schemaCursor.getString(1));
|
|
||||||
}
|
|
||||||
schemaCursor.close();
|
|
||||||
|
|
||||||
for (String column : columns) {
|
|
||||||
Cursor dataCursor = null;
|
|
||||||
try {
|
|
||||||
dataCursor = db.query(table, new String[]{column}, null, null, null, null, null);
|
|
||||||
while (dataCursor.moveToNext()) {
|
|
||||||
String value = dataCursor.getString(0);
|
|
||||||
if (value != null && value.length() == 32 && value.matches("[0-9a-fA-F]{32}")) {
|
|
||||||
// Skip obviously fake tokens (MD5 of empty string).
|
|
||||||
if (!value.equalsIgnoreCase(EMPTY_MD5)) {
|
|
||||||
dataCursor.close();
|
|
||||||
db.close();
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Skip non-string columns.
|
|
||||||
} finally {
|
|
||||||
if (dataCursor != null) {
|
|
||||||
dataCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Continue to next table.
|
|
||||||
} finally {
|
|
||||||
if (schemaCursor != null && !schemaCursor.isClosed()) {
|
|
||||||
schemaCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
if (db != null && db.isOpen()) {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the logged flag (useful for testing or re-pairing).
|
|
||||||
*/
|
|
||||||
public static void resetK1Logged() {
|
|
||||||
k1Logged = false;
|
|
||||||
lifecycleCallbacksRegistered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the "don't show again" preference.
|
|
||||||
*/
|
|
||||||
public static void resetDontShowPreference() {
|
|
||||||
if (appContext != null) {
|
|
||||||
SharedPreferences prefs = appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
||||||
prefs.edit().putBoolean(KEY_DONT_SHOW_DIALOG, false).apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.library)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 26
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -311,10 +311,6 @@ public class Utils {
|
|||||||
return resourceId;
|
return resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getResourceString(int id) throws Resources.NotFoundException {
|
|
||||||
return getContext().getResources().getString(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException {
|
public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException {
|
||||||
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer"));
|
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,213 +0,0 @@
|
|||||||
package app.revanced.extension.shared.patches.litho;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
|
||||||
|
|
||||||
public abstract class FilterGroup<T> {
|
|
||||||
public final static class FilterGroupResult {
|
|
||||||
private BooleanSetting setting;
|
|
||||||
private int matchedIndex;
|
|
||||||
private int matchedLength;
|
|
||||||
// In the future it might be useful to include which pattern matched,
|
|
||||||
// but for now that is not needed.
|
|
||||||
|
|
||||||
FilterGroupResult() {
|
|
||||||
this(null, -1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
FilterGroupResult(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
|
||||||
setValues(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValues(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.matchedIndex = matchedIndex;
|
|
||||||
this.matchedLength = matchedLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A null value if the group has no setting,
|
|
||||||
* or if no match is returned from {@link FilterGroupList#check(Object)}.
|
|
||||||
*/
|
|
||||||
public BooleanSetting getSetting() {
|
|
||||||
return setting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFiltered() {
|
|
||||||
return matchedIndex >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matched index of first pattern that matched, or -1 if nothing matched.
|
|
||||||
*/
|
|
||||||
public int getMatchedIndex() {
|
|
||||||
return matchedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the matched filter pattern.
|
|
||||||
*/
|
|
||||||
public int getMatchedLength() {
|
|
||||||
return matchedLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final BooleanSetting setting;
|
|
||||||
protected final T[] filters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new filter group.
|
|
||||||
*
|
|
||||||
* @param setting The associated setting.
|
|
||||||
* @param filters The filters.
|
|
||||||
*/
|
|
||||||
@SafeVarargs
|
|
||||||
public FilterGroup(final BooleanSetting setting, final T... filters) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.filters = filters;
|
|
||||||
if (filters.length == 0) {
|
|
||||||
throw new IllegalArgumentException("Must use one or more filter patterns (zero specified)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return setting == null || setting.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return If {@link FilterGroupList} should include this group when searching.
|
|
||||||
* By default, all filters are included except non enabled settings that require reboot.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
public boolean includeInSearch() {
|
|
||||||
return isEnabled() || !setting.rebootApp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": " + (setting == null ? "(null setting)" : setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract FilterGroupResult check(final T stack);
|
|
||||||
|
|
||||||
|
|
||||||
public static class StringFilterGroup extends FilterGroup<String> {
|
|
||||||
|
|
||||||
public StringFilterGroup(final BooleanSetting setting, final String... filters) {
|
|
||||||
super(setting, filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FilterGroupResult check(final String string) {
|
|
||||||
int matchedIndex = -1;
|
|
||||||
int matchedLength = 0;
|
|
||||||
if (isEnabled()) {
|
|
||||||
for (String pattern : filters) {
|
|
||||||
if (!string.isEmpty()) {
|
|
||||||
final int indexOf = string.indexOf(pattern);
|
|
||||||
if (indexOf >= 0) {
|
|
||||||
matchedIndex = indexOf;
|
|
||||||
matchedLength = pattern.length();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If you have more than 1 filter patterns, then all instances of
|
|
||||||
* this class should filtered using {@link FilterGroupList.ByteArrayFilterGroupList#check(byte[])},
|
|
||||||
* which uses a prefix tree to give better performance.
|
|
||||||
*/
|
|
||||||
public static class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
|
||||||
|
|
||||||
private volatile int[][] failurePatterns;
|
|
||||||
|
|
||||||
// Modified implementation from https://stackoverflow.com/a/1507813
|
|
||||||
private static int indexOf(final byte[] data, final byte[] pattern, final int[] failure) {
|
|
||||||
// Finds the first occurrence of the pattern in the byte array using
|
|
||||||
// KMP matching algorithm.
|
|
||||||
int patternLength = pattern.length;
|
|
||||||
for (int i = 0, j = 0, dataLength = data.length; i < dataLength; i++) {
|
|
||||||
while (j > 0 && pattern[j] != data[i]) {
|
|
||||||
j = failure[j - 1];
|
|
||||||
}
|
|
||||||
if (pattern[j] == data[i]) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
if (j == patternLength) {
|
|
||||||
return i - patternLength + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] createFailurePattern(byte[] pattern) {
|
|
||||||
// Computes the failure function using a boot-strapping process,
|
|
||||||
// where the pattern is matched against itself.
|
|
||||||
final int patternLength = pattern.length;
|
|
||||||
final int[] failure = new int[patternLength];
|
|
||||||
|
|
||||||
for (int i = 1, j = 0; i < patternLength; i++) {
|
|
||||||
while (j > 0 && pattern[j] != pattern[i]) {
|
|
||||||
j = failure[j - 1];
|
|
||||||
}
|
|
||||||
if (pattern[j] == pattern[i]) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
failure[i] = j;
|
|
||||||
}
|
|
||||||
return failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteArrayFilterGroup(BooleanSetting setting, byte[]... filters) {
|
|
||||||
super(setting, filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the Strings into byte arrays. Used to search for text in binary data.
|
|
||||||
*/
|
|
||||||
public ByteArrayFilterGroup(BooleanSetting setting, String... filters) {
|
|
||||||
super(setting, ByteTrieSearch.convertStringsToBytes(filters));
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void buildFailurePatterns() {
|
|
||||||
if (failurePatterns != null) return; // Thread race and another thread already initialized the search.
|
|
||||||
Logger.printDebug(() -> "Building failure array for: " + this);
|
|
||||||
int[][] failurePatterns = new int[filters.length][];
|
|
||||||
int i = 0;
|
|
||||||
for (byte[] pattern : filters) {
|
|
||||||
failurePatterns[i++] = createFailurePattern(pattern);
|
|
||||||
}
|
|
||||||
this.failurePatterns = failurePatterns; // Must set after initialization finishes.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FilterGroupResult check(final byte[] bytes) {
|
|
||||||
int matchedLength = 0;
|
|
||||||
int matchedIndex = -1;
|
|
||||||
if (isEnabled()) {
|
|
||||||
int[][] failures = failurePatterns;
|
|
||||||
if (failures == null) {
|
|
||||||
buildFailurePatterns(); // Lazy load.
|
|
||||||
failures = failurePatterns;
|
|
||||||
}
|
|
||||||
for (int i = 0, length = filters.length; i < length; i++) {
|
|
||||||
byte[] filter = filters[i];
|
|
||||||
matchedIndex = indexOf(bytes, filter, failures[i]);
|
|
||||||
if (matchedIndex >= 0) {
|
|
||||||
matchedLength = filter.length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package app.revanced.extension.shared.settings;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
|
||||||
import static java.lang.Boolean.FALSE;
|
|
||||||
|
|
||||||
public class YouTubeAndMusicSettings extends BaseSettings {
|
|
||||||
// Custom filter
|
|
||||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
|
||||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
|
||||||
|
|
||||||
// Miscellaneous
|
|
||||||
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
|
|
||||||
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
dependencies {
|
|
||||||
compileOnly(project(":extensions:shared:library"))
|
|
||||||
compileOnly(project(":extensions:strava:stub"))
|
|
||||||
compileOnly(libs.okhttp)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
package app.revanced.extension.strava;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
|
|
||||||
import com.strava.core.data.MediaType;
|
|
||||||
import com.strava.photos.data.Media;
|
|
||||||
|
|
||||||
import okhttp3.*;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public final class AddMediaDownloadPatch {
|
|
||||||
public static final int ACTION_DOWNLOAD = -1;
|
|
||||||
public static final int ACTION_OPEN_LINK = -2;
|
|
||||||
public static final int ACTION_COPY_LINK = -3;
|
|
||||||
|
|
||||||
private static final OkHttpClient client = new OkHttpClient();
|
|
||||||
|
|
||||||
public static boolean handleAction(int actionId, Media media) {
|
|
||||||
String url = getUrl(media);
|
|
||||||
switch (actionId) {
|
|
||||||
case ACTION_DOWNLOAD:
|
|
||||||
String name = media.getId();
|
|
||||||
if (media.getType() == MediaType.VIDEO) {
|
|
||||||
downloadVideo(url, name);
|
|
||||||
} else {
|
|
||||||
downloadPhoto(url, name);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case ACTION_OPEN_LINK:
|
|
||||||
Utils.openLink(url);
|
|
||||||
return true;
|
|
||||||
case ACTION_COPY_LINK:
|
|
||||||
copyLink(url);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void copyLink(CharSequence url) {
|
|
||||||
Utils.setClipboard(url);
|
|
||||||
showInfoToast("link_copied_to_clipboard", "🔗");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void downloadPhoto(String url, String name) {
|
|
||||||
showInfoToast("loading", "⏳");
|
|
||||||
Utils.runOnBackgroundThread(() -> {
|
|
||||||
try (Response response = fetch(url)) {
|
|
||||||
ResponseBody body = response.body();
|
|
||||||
String mimeType = body.contentType().toString();
|
|
||||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
|
||||||
ContentResolver resolver = Utils.getContext().getContentResolver();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, name + '.' + extension);
|
|
||||||
values.put(MediaStore.Images.Media.IS_PENDING, 1);
|
|
||||||
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
|
|
||||||
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Strava");
|
|
||||||
Uri collection = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
||||||
? MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
|
||||||
: MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
Uri row = resolver.insert(collection, values);
|
|
||||||
try (OutputStream outputStream = resolver.openOutputStream(row)) {
|
|
||||||
transferTo(body.byteStream(), outputStream);
|
|
||||||
} finally {
|
|
||||||
values.clear();
|
|
||||||
values.put(MediaStore.Images.Media.IS_PENDING, 0);
|
|
||||||
resolver.update(row, values, null);
|
|
||||||
}
|
|
||||||
showInfoToast("yis_2024_local_save_image_success", "✔️");
|
|
||||||
} catch (IOException e) {
|
|
||||||
showErrorToast("download_failure", "❌", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a video in the M3U8 / HLS (HTTP Live Streaming) format.
|
|
||||||
*/
|
|
||||||
public static void downloadVideo(String url, String name) {
|
|
||||||
// The first request yields multiple URLs with different stream options.
|
|
||||||
// In case of Strava, the first one is always of highest quality.
|
|
||||||
// Each stream can consist of multiple chunks.
|
|
||||||
// The second request yields the URLs of all of these chunks.
|
|
||||||
// Fetch all of them concurrently and pipe their streams into the file in order.
|
|
||||||
showInfoToast("loading", "⏳");
|
|
||||||
Utils.runOnBackgroundThread(() -> {
|
|
||||||
try {
|
|
||||||
String highestQualityStreamUrl;
|
|
||||||
try (Response response = fetch(url)) {
|
|
||||||
highestQualityStreamUrl = replaceFileName(url, lines(response).findFirst().get());
|
|
||||||
}
|
|
||||||
List<Future<Response>> futures;
|
|
||||||
try (Response response = fetch(highestQualityStreamUrl)) {
|
|
||||||
futures = lines(response)
|
|
||||||
.map(line -> replaceFileName(highestQualityStreamUrl, line))
|
|
||||||
.map(chunkUrl -> Utils.submitOnBackgroundThread(() -> fetch(chunkUrl)))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
ContentResolver resolver = Utils.getContext().getContentResolver();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Video.Media.DISPLAY_NAME, name + '.' + "mp4");
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 1);
|
|
||||||
values.put(MediaStore.Video.Media.MIME_TYPE, MimeTypeMap.getSingleton().getMimeTypeFromExtension("mp4"));
|
|
||||||
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/Strava");
|
|
||||||
Uri collection = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
||||||
? MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
|
||||||
: MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
Uri row = resolver.insert(collection, values);
|
|
||||||
try (OutputStream outputStream = resolver.openOutputStream(row)) {
|
|
||||||
Throwable error = null;
|
|
||||||
for (Future<Response> future : futures) {
|
|
||||||
if (error != null) {
|
|
||||||
if (future.cancel(true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try (Response response = future.get()) {
|
|
||||||
if (error == null) {
|
|
||||||
transferTo(response.body().byteStream(), outputStream);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException | IOException e) {
|
|
||||||
error = e;
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
error = e.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error != null) {
|
|
||||||
throw new IOException(error);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
values.clear();
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 0);
|
|
||||||
resolver.update(row, values, null);
|
|
||||||
}
|
|
||||||
showInfoToast("yis_2024_local_save_video_success", "✔️");
|
|
||||||
} catch (IOException e) {
|
|
||||||
showErrorToast("download_failure", "❌", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getUrl(Media media) {
|
|
||||||
return media.getType() == MediaType.VIDEO
|
|
||||||
? ((Media.Video) media).getVideoUrl()
|
|
||||||
: media.getLargestUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getString(String name, String fallback) {
|
|
||||||
int id = Utils.getResourceIdentifier(name, "string");
|
|
||||||
return id != 0
|
|
||||||
? Utils.getResourceString(id)
|
|
||||||
: fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showInfoToast(String resourceName, String fallback) {
|
|
||||||
String text = getString(resourceName, fallback);
|
|
||||||
Utils.showToastShort(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showErrorToast(String resourceName, String fallback, IOException exception) {
|
|
||||||
String text = getString(resourceName, fallback);
|
|
||||||
Utils.showToastLong(text + ' ' + exception.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Response fetch(String url) throws IOException {
|
|
||||||
Request request = new Request.Builder().url(url).build();
|
|
||||||
Response response = client.newCall(request).execute();
|
|
||||||
if (!response.isSuccessful()) {
|
|
||||||
throw new IOException("Got HTTP status code " + response.code());
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code inputStream.transferTo(outputStream)} is "too new".
|
|
||||||
*/
|
|
||||||
private static void transferTo(InputStream in, OutputStream out) throws IOException {
|
|
||||||
byte[] buffer = new byte[1024 * 8];
|
|
||||||
int length;
|
|
||||||
while ((length = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all file names.
|
|
||||||
*/
|
|
||||||
private static Stream<String> lines(Response response) {
|
|
||||||
BufferedReader reader = new BufferedReader(response.body().charStream());
|
|
||||||
return reader.lines().filter(line -> !line.startsWith("#"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String replaceFileName(String uri, String newName) {
|
|
||||||
return uri.substring(0, uri.lastIndexOf('/') + 1) + newName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
package app.revanced.extension.strava;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
|
|
||||||
import com.strava.modularframework.data.Destination;
|
|
||||||
import com.strava.modularframework.data.GenericLayoutModule;
|
|
||||||
import com.strava.modularframework.data.GenericModuleField;
|
|
||||||
import com.strava.modularframework.data.ListField;
|
|
||||||
import com.strava.modularframework.data.ListProperties;
|
|
||||||
import com.strava.modularframework.data.ModularComponent;
|
|
||||||
import com.strava.modularframework.data.ModularEntry;
|
|
||||||
import com.strava.modularframework.data.ModularEntryContainer;
|
|
||||||
import com.strava.modularframework.data.ModularMenuItem;
|
|
||||||
import com.strava.modularframework.data.Module;
|
|
||||||
import com.strava.modularframework.data.MultiStateFieldDescriptor;
|
|
||||||
import com.strava.modularframeworknetwork.ModularEntryNetworkContainer;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public class HideDistractionsPatch {
|
|
||||||
public static boolean upselling;
|
|
||||||
public static boolean promo;
|
|
||||||
public static boolean followSuggestions;
|
|
||||||
public static boolean challengeSuggestions;
|
|
||||||
public static boolean joinChallenge;
|
|
||||||
public static boolean joinClub;
|
|
||||||
public static boolean activityLookback;
|
|
||||||
|
|
||||||
public static List<ModularEntry> filterChildrenEntries(ModularEntry modularEntry) {
|
|
||||||
if (hideModularEntry(modularEntry)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularEntry.getChildrenEntries$original().stream()
|
|
||||||
.filter(childrenEntry -> !hideModularEntry(childrenEntry))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ModularEntry> filterEntries(ModularEntryContainer modularEntryContainer) {
|
|
||||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularEntryContainer.getEntries$original().stream()
|
|
||||||
.filter(entry -> !hideModularEntry(entry))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ModularEntry> filterEntries(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
|
||||||
if (hideModularEntryNetworkContainer(modularEntryNetworkContainer)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularEntryNetworkContainer.getEntries$original().stream()
|
|
||||||
.filter(entry -> !hideModularEntry(entry))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ModularMenuItem> filterMenuItems(ModularEntryContainer modularEntryContainer) {
|
|
||||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularEntryContainer.getMenuItems$original().stream()
|
|
||||||
.filter(menuItem -> !hideModularMenuItem(menuItem))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ListProperties filterProperties(ModularEntryContainer modularEntryContainer) {
|
|
||||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return modularEntryContainer.getProperties$original();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ListProperties filterProperties(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
|
||||||
if (hideModularEntryNetworkContainer(modularEntryNetworkContainer)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return modularEntryNetworkContainer.getProperties$original();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ListField filterField(ListProperties listProperties, String key) {
|
|
||||||
ListField listField = listProperties.getField$original(key);
|
|
||||||
if (hideListField(listField)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return listField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ListField> filterFields(ListField listField) {
|
|
||||||
if (hideListField(listField)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return listField.getFields$original().stream()
|
|
||||||
.filter(field -> !hideListField(field))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Module> filterModules(ModularEntry modularEntry) {
|
|
||||||
if (hideModularEntry(modularEntry)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularEntry.getModules$original().stream()
|
|
||||||
.filter(module -> !hideModule(module))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GenericModuleField filterField(GenericLayoutModule genericLayoutModule, String key) {
|
|
||||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
GenericModuleField field = genericLayoutModule.getField$original(key);
|
|
||||||
if (hideGenericModuleField(field)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GenericModuleField[] filterFields(GenericLayoutModule genericLayoutModule) {
|
|
||||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
|
||||||
return new GenericModuleField[0];
|
|
||||||
}
|
|
||||||
return Arrays.stream(genericLayoutModule.getFields$original())
|
|
||||||
.filter(field -> !hideGenericModuleField(field))
|
|
||||||
.toArray(GenericModuleField[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GenericLayoutModule[] filterSubmodules(GenericLayoutModule genericLayoutModule) {
|
|
||||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
|
||||||
return new GenericLayoutModule[0];
|
|
||||||
}
|
|
||||||
return Arrays.stream(genericLayoutModule.getSubmodules$original())
|
|
||||||
.filter(submodule -> !hideGenericLayoutModule(submodule))
|
|
||||||
.toArray(GenericLayoutModule[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Module> filterSubmodules(ModularComponent modularComponent) {
|
|
||||||
if (hideByName(modularComponent.getPage()) || hideByName(modularComponent.getElement())) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return modularComponent.getSubmodules$original().stream()
|
|
||||||
.filter(submodule -> !hideModule(submodule))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, GenericModuleField> filterStateMap(MultiStateFieldDescriptor multiStateFieldDescriptor) {
|
|
||||||
return multiStateFieldDescriptor.getStateMap$original().entrySet().stream()
|
|
||||||
.filter(entry -> !hideGenericModuleField(entry.getValue()))
|
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideModule(Module module) {
|
|
||||||
return module == null ||
|
|
||||||
hideByName(module.getPage()) ||
|
|
||||||
hideByName(module.getElement());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideModularEntry(ModularEntry modularEntry) {
|
|
||||||
return modularEntry == null ||
|
|
||||||
hideByName(modularEntry.getPage()) ||
|
|
||||||
hideByName(modularEntry.getElement()) ||
|
|
||||||
hideByDestination(modularEntry.getDestination());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideGenericLayoutModule(GenericLayoutModule genericLayoutModule) {
|
|
||||||
try {
|
|
||||||
return genericLayoutModule == null ||
|
|
||||||
hideByName(genericLayoutModule.getPage()) ||
|
|
||||||
hideByName(genericLayoutModule.getElement()) ||
|
|
||||||
hideByDestination(genericLayoutModule.getDestination());
|
|
||||||
} catch (RuntimeException getParentEntryOrThrowException) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideListField(ListField listField) {
|
|
||||||
return listField == null ||
|
|
||||||
hideByName(listField.getElement()) ||
|
|
||||||
hideByDestination(listField.getDestination());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideGenericModuleField(GenericModuleField genericModuleField) {
|
|
||||||
return genericModuleField == null ||
|
|
||||||
hideByName(genericModuleField.getElement()) ||
|
|
||||||
hideByDestination(genericModuleField.getDestination());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideModularEntryContainer(ModularEntryContainer modularEntryContainer) {
|
|
||||||
return modularEntryContainer == null ||
|
|
||||||
hideByName(modularEntryContainer.getPage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideModularEntryNetworkContainer(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
|
||||||
return modularEntryNetworkContainer == null ||
|
|
||||||
hideByName(modularEntryNetworkContainer.getPage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideModularMenuItem(ModularMenuItem modularMenuItem) {
|
|
||||||
return modularMenuItem == null ||
|
|
||||||
hideByName(modularMenuItem.getElementName()) ||
|
|
||||||
hideByDestination(modularMenuItem.getDestination());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideByName(String name) {
|
|
||||||
return name != null && (
|
|
||||||
upselling && name.contains("_upsell") ||
|
|
||||||
promo && (name.equals("promo") || name.equals("top_of_tab_promo")) ||
|
|
||||||
followSuggestions && name.equals("suggested_follows") ||
|
|
||||||
challengeSuggestions && name.equals("suggested_challenges") ||
|
|
||||||
joinChallenge && name.equals("challenge") ||
|
|
||||||
joinClub && name.equals("club") ||
|
|
||||||
activityLookback && name.equals("highlighted_activity_lookback")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hideByDestination(Destination destination) {
|
|
||||||
if (destination == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String url = destination.getUrl();
|
|
||||||
return url != null && (
|
|
||||||
upselling && url.startsWith("strava://subscription/checkout")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.library)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 21
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public interface MediaContent extends Serializable {
|
|
||||||
String getCaption();
|
|
||||||
|
|
||||||
String getId();
|
|
||||||
|
|
||||||
String getReferenceId();
|
|
||||||
|
|
||||||
MediaType getType();
|
|
||||||
|
|
||||||
void setCaption(String caption);
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public final class MediaDimension implements Comparable<MediaDimension>, Serializable {
|
|
||||||
private final int height;
|
|
||||||
private final int width;
|
|
||||||
|
|
||||||
public MediaDimension(int width, int height) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getHeightScale() {
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
return height / width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getWidthScale() {
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
return width / height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLandscape() {
|
|
||||||
return width > 0 && width >= height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(MediaDimension other) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
public enum MediaType {
|
|
||||||
PHOTO(1),
|
|
||||||
VIDEO(2);
|
|
||||||
|
|
||||||
private final int remoteValue;
|
|
||||||
|
|
||||||
private MediaType(int remoteValue) {
|
|
||||||
this.remoteValue = remoteValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteValue() {
|
|
||||||
return remoteValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.util.SortedMap;
|
|
||||||
|
|
||||||
public interface RemoteMediaContent extends MediaContent {
|
|
||||||
MediaDimension getLargestSize();
|
|
||||||
|
|
||||||
String getLargestUrl();
|
|
||||||
|
|
||||||
SortedMap<Integer, MediaDimension> getSizes();
|
|
||||||
|
|
||||||
String getSmallestUrl();
|
|
||||||
|
|
||||||
RemoteMediaStatus getStatus();
|
|
||||||
|
|
||||||
SortedMap<Integer, String> getUrls();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
public enum RemoteMediaStatus {
|
|
||||||
NEW,
|
|
||||||
PENDING,
|
|
||||||
PROCESSED,
|
|
||||||
REPORTED,
|
|
||||||
REINSTATED,
|
|
||||||
DELETED,
|
|
||||||
FAILED
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public abstract class Destination implements Serializable {
|
|
||||||
public abstract String getUrl();
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public abstract class GenericLayoutModule implements Serializable, Module {
|
|
||||||
public abstract Destination getDestination();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract String getElement();
|
|
||||||
|
|
||||||
public abstract GenericModuleField getField(String key);
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract GenericModuleField getField$original(String key);
|
|
||||||
|
|
||||||
public abstract GenericModuleField[] getFields();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract GenericModuleField[] getFields$original();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract String getPage();
|
|
||||||
|
|
||||||
public abstract GenericLayoutModule[] getSubmodules();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract GenericLayoutModule[] getSubmodules$original();
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public abstract class GenericModuleField implements Serializable {
|
|
||||||
public abstract Destination getDestination();
|
|
||||||
|
|
||||||
public abstract String getElement();
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class ListField {
|
|
||||||
public abstract Destination getDestination();
|
|
||||||
|
|
||||||
public abstract String getElement();
|
|
||||||
|
|
||||||
public abstract List<ListField> getFields();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract List<ListField> getFields$original();
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
public abstract class ListProperties {
|
|
||||||
public abstract ListField getField(String key);
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract ListField getField$original(String key);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class ModularComponent implements Module {
|
|
||||||
@Override
|
|
||||||
public abstract String getElement();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract String getPage();
|
|
||||||
|
|
||||||
public abstract List<Module> getSubmodules();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract List<Module> getSubmodules$original();
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface ModularEntry {
|
|
||||||
List<ModularEntry> getChildrenEntries();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
List<ModularEntry> getChildrenEntries$original();
|
|
||||||
|
|
||||||
Destination getDestination();
|
|
||||||
|
|
||||||
String getElement();
|
|
||||||
|
|
||||||
List<Module> getModules();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
List<Module> getModules$original();
|
|
||||||
|
|
||||||
String getPage();
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class ModularEntryContainer {
|
|
||||||
public abstract List<ModularEntry> getEntries();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract List<ModularEntry> getEntries$original();
|
|
||||||
|
|
||||||
public abstract List<ModularMenuItem> getMenuItems();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract List<ModularMenuItem> getMenuItems$original();
|
|
||||||
|
|
||||||
public abstract String getPage();
|
|
||||||
|
|
||||||
public abstract ListProperties getProperties();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract ListProperties getProperties$original();
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
public abstract class ModularMenuItem {
|
|
||||||
public abstract Destination getDestination();
|
|
||||||
|
|
||||||
public abstract String getElementName();
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
public interface Module {
|
|
||||||
String getElement();
|
|
||||||
|
|
||||||
String getPage();
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.strava.modularframework.data;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class MultiStateFieldDescriptor {
|
|
||||||
public abstract Map<String, GenericModuleField> getStateMap();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract Map<String, GenericModuleField> getStateMap$original();
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.strava.modularframeworknetwork;
|
|
||||||
|
|
||||||
import com.strava.modularframework.data.ListProperties;
|
|
||||||
import com.strava.modularframework.data.ModularEntry;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class ModularEntryNetworkContainer {
|
|
||||||
public abstract List<ModularEntry> getEntries();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract List<ModularEntry> getEntries$original();
|
|
||||||
|
|
||||||
public abstract String getPage();
|
|
||||||
|
|
||||||
public abstract ListProperties getProperties();
|
|
||||||
|
|
||||||
// Added by patch.
|
|
||||||
public abstract ListProperties getProperties$original();
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
package com.strava.photos.data;
|
|
||||||
|
|
||||||
import com.strava.core.data.MediaDimension;
|
|
||||||
import com.strava.core.data.MediaType;
|
|
||||||
import com.strava.core.data.RemoteMediaContent;
|
|
||||||
import com.strava.core.data.RemoteMediaStatus;
|
|
||||||
import java.util.SortedMap;
|
|
||||||
|
|
||||||
public abstract class Media implements RemoteMediaContent {
|
|
||||||
public static final class Photo extends Media {
|
|
||||||
private final Long activityId;
|
|
||||||
private final String activityName;
|
|
||||||
private final long athleteId;
|
|
||||||
private String caption;
|
|
||||||
private final String createdAt;
|
|
||||||
private final String createdAtLocal;
|
|
||||||
private final String cursor;
|
|
||||||
private final String id;
|
|
||||||
private final SortedMap<Integer, MediaDimension> sizes;
|
|
||||||
private final RemoteMediaStatus status;
|
|
||||||
private final String tag;
|
|
||||||
private final MediaType type;
|
|
||||||
private final SortedMap<Integer, String> urls;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getActivityId() {
|
|
||||||
return activityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActivityName() {
|
|
||||||
return activityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getAthleteId() {
|
|
||||||
return athleteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCaption() {
|
|
||||||
return caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAt() {
|
|
||||||
return createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAtLocal() {
|
|
||||||
return createdAtLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCursor() {
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, MediaDimension> getSizes() {
|
|
||||||
return sizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteMediaStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, String> getUrls() {
|
|
||||||
return urls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCaption(String caption) {
|
|
||||||
this.caption = caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Photo(String id,
|
|
||||||
String caption,
|
|
||||||
SortedMap<Integer, String> urls,
|
|
||||||
SortedMap<Integer, MediaDimension> sizes,
|
|
||||||
long athleteId,
|
|
||||||
String createdAt,
|
|
||||||
String createdAtLocal,
|
|
||||||
Long activityId,
|
|
||||||
String activityName,
|
|
||||||
RemoteMediaStatus status,
|
|
||||||
String tag,
|
|
||||||
String cursor) {
|
|
||||||
this.id = id;
|
|
||||||
this.caption = caption;
|
|
||||||
this.urls = urls;
|
|
||||||
this.sizes = sizes;
|
|
||||||
this.athleteId = athleteId;
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
this.createdAtLocal = createdAtLocal;
|
|
||||||
this.activityId = activityId;
|
|
||||||
this.activityName = activityName;
|
|
||||||
this.status = status;
|
|
||||||
this.tag = tag;
|
|
||||||
this.cursor = cursor;
|
|
||||||
this.type = MediaType.PHOTO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Video extends Media {
|
|
||||||
private final Long activityId;
|
|
||||||
private final String activityName;
|
|
||||||
private final long athleteId;
|
|
||||||
private String caption;
|
|
||||||
private final String createdAt;
|
|
||||||
private final String createdAtLocal;
|
|
||||||
private final String cursor;
|
|
||||||
private final Float durationSeconds;
|
|
||||||
private final String id;
|
|
||||||
private final SortedMap<Integer, MediaDimension> sizes;
|
|
||||||
private final RemoteMediaStatus status;
|
|
||||||
private final String tag;
|
|
||||||
private final MediaType type;
|
|
||||||
private final SortedMap<Integer, String> urls;
|
|
||||||
private final String videoUrl;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getActivityId() {
|
|
||||||
return activityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActivityName() {
|
|
||||||
return activityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getAthleteId() {
|
|
||||||
return athleteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCaption() {
|
|
||||||
return caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAt() {
|
|
||||||
return createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAtLocal() {
|
|
||||||
return createdAtLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCursor() {
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Float getDurationSeconds() {
|
|
||||||
return durationSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, MediaDimension> getSizes() {
|
|
||||||
return sizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteMediaStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, String> getUrls() {
|
|
||||||
return urls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getVideoUrl() {
|
|
||||||
return videoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCaption(String caption) {
|
|
||||||
this.caption = caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Video(String id,
|
|
||||||
String caption,
|
|
||||||
SortedMap<Integer, String> urls,
|
|
||||||
SortedMap<Integer, MediaDimension> sizes,
|
|
||||||
long athleteId,
|
|
||||||
String createdAt,
|
|
||||||
String createdAtLocal,
|
|
||||||
Long activityId,
|
|
||||||
String activityName,
|
|
||||||
RemoteMediaStatus status,
|
|
||||||
String videoUrl,
|
|
||||||
Float durationSeconds,
|
|
||||||
String tag,
|
|
||||||
String cursor) {
|
|
||||||
this.id = id;
|
|
||||||
this.caption = caption;
|
|
||||||
this.urls = urls;
|
|
||||||
this.sizes = sizes;
|
|
||||||
this.athleteId = athleteId;
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
this.createdAtLocal = createdAtLocal;
|
|
||||||
this.activityId = activityId;
|
|
||||||
this.activityName = activityName;
|
|
||||||
this.status = status;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.durationSeconds = durationSeconds;
|
|
||||||
this.tag = tag;
|
|
||||||
this.cursor = cursor;
|
|
||||||
this.type = MediaType.VIDEO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Long getActivityId();
|
|
||||||
|
|
||||||
public abstract String getActivityName();
|
|
||||||
|
|
||||||
public abstract long getAthleteId();
|
|
||||||
|
|
||||||
public abstract String getCreatedAt();
|
|
||||||
|
|
||||||
public abstract String getCreatedAtLocal();
|
|
||||||
|
|
||||||
public abstract String getCursor();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaDimension getLargestSize() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLargestUrl() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReferenceId() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSmallestUrl() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getTag();
|
|
||||||
|
|
||||||
private Media() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class PauseOnAudioInterruptPatch {
|
|
||||||
|
|
||||||
private static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3;
|
|
||||||
private static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point for AudioFocusRequest builder.
|
|
||||||
* Returns true if audio ducking should be disabled (willPauseWhenDucked = true).
|
|
||||||
*/
|
|
||||||
public static boolean shouldPauseOnAudioInterrupt() {
|
|
||||||
return Settings.PAUSE_ON_AUDIO_INTERRUPT.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point for onAudioFocusChange callback.
|
|
||||||
* Converts AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK to AUDIOFOCUS_LOSS_TRANSIENT
|
|
||||||
* when the setting is enabled, causing YouTube to pause instead of ducking.
|
|
||||||
*/
|
|
||||||
public static int overrideAudioFocusChange(int focusChange) {
|
|
||||||
if (Settings.PAUSE_ON_AUDIO_INTERRUPT.get() && focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
|
||||||
return AUDIOFOCUS_LOSS_TRANSIENT;
|
|
||||||
}
|
|
||||||
return focusChange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,9 +11,6 @@ import java.util.List;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -156,7 +153,7 @@ public final class AdsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == playerShoppingShelf) {
|
if (matchedGroup == playerShoppingShelf) {
|
||||||
return contentIndex == 0 && playerShoppingShelfBuffer.check(buffer).isFiltered();
|
return contentIndex == 0 && playerShoppingShelfBuffer.check(buffer).isFiltered();
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
|
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ public final class AdvancedVideoQualityMenuFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
isVideoQualityMenuVisible = true;
|
isVideoQualityMenuVisible = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList;
|
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class ButtonsFilter extends Filter {
|
final class ButtonsFilter extends Filter {
|
||||||
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.e";
|
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.e";
|
||||||
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.e";
|
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.e";
|
||||||
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.e";
|
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.e";
|
||||||
@@ -122,7 +118,7 @@ public final class ButtonsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == likeSubscribeGlow) {
|
if (matchedGroup == likeSubscribeGlow) {
|
||||||
return (path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
return (path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class CommentsFilter extends Filter {
|
final class CommentsFilter extends Filter {
|
||||||
|
|
||||||
private static final String COMMENT_COMPOSER_PATH = "comment_composer.e";
|
private static final String COMMENT_COMPOSER_PATH = "comment_composer.e";
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ public final class CommentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == chipBar) {
|
if (matchedGroup == chipBar) {
|
||||||
// Playlist sort button uses same components and must only filter if the player is opened.
|
// Playlist sort button uses same components and must only filter if the player is opened.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.shared.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
@@ -15,15 +15,13 @@ import java.util.regex.Pattern;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows custom filtering using a path and optionally a proto buffer string.
|
* Allows custom filtering using a path and optionally a proto buffer string.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class CustomFilter extends Filter {
|
final class CustomFilter extends Filter {
|
||||||
|
|
||||||
private static void showInvalidSyntaxToast(@NonNull String expression) {
|
private static void showInvalidSyntaxToast(@NonNull String expression) {
|
||||||
Utils.showToastLong(str("revanced_custom_filter_toast_invalid_syntax", expression));
|
Utils.showToastLong(str("revanced_custom_filter_toast_invalid_syntax", expression));
|
||||||
@@ -47,7 +45,7 @@ public final class CustomFilter extends Filter {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
static Collection<CustomFilterGroup> parseCustomFilterGroups() {
|
static Collection<CustomFilterGroup> parseCustomFilterGroups() {
|
||||||
String rawCustomFilterText = YouTubeAndMusicSettings.CUSTOM_FILTER_STRINGS.get();
|
String rawCustomFilterText = Settings.CUSTOM_FILTER_STRINGS.get();
|
||||||
if (rawCustomFilterText.isBlank()) {
|
if (rawCustomFilterText.isBlank()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -102,7 +100,7 @@ public final class CustomFilter extends Filter {
|
|||||||
ByteTrieSearch bufferSearch;
|
ByteTrieSearch bufferSearch;
|
||||||
|
|
||||||
CustomFilterGroup(boolean startsWith, @NonNull String path) {
|
CustomFilterGroup(boolean startsWith, @NonNull String path) {
|
||||||
super(YouTubeAndMusicSettings.CUSTOM_FILTER, path);
|
super(Settings.CUSTOM_FILTER, path);
|
||||||
this.startsWith = startsWith;
|
this.startsWith = startsWith;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +145,7 @@ public final class CustomFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
// All callbacks are custom filter groups.
|
// All callbacks are custom filter groups.
|
||||||
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
|
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class DescriptionComponentsFilter extends Filter {
|
final class DescriptionComponentsFilter extends Filter {
|
||||||
|
|
||||||
private static final String INFOCARDS_SECTION_PATH = "infocards_section.e";
|
private static final String INFOCARDS_SECTION_PATH = "infocards_section.e";
|
||||||
|
|
||||||
@@ -131,7 +128,7 @@ public final class DescriptionComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
|
|
||||||
if (matchedGroup == aiGeneratedVideoSummarySection || matchedGroup == hypePoints) {
|
if (matchedGroup == aiGeneratedVideoSummarySection || matchedGroup == hypePoints) {
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package app.revanced.extension.shared.patches.litho;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters litho based components.
|
* Filters litho based components.
|
||||||
*
|
*
|
||||||
@@ -17,11 +14,11 @@ import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGr
|
|||||||
* either an identifier or a path.
|
* either an identifier or a path.
|
||||||
* Then inside {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
* Then inside {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
||||||
* search for the buffer content using either a {@link ByteArrayFilterGroup} (if searching for 1 pattern)
|
* search for the buffer content using either a {@link ByteArrayFilterGroup} (if searching for 1 pattern)
|
||||||
* or a {@link FilterGroupList.ByteArrayFilterGroupList} (if searching for more than 1 pattern).
|
* or a {@link ByteArrayFilterGroupList} (if searching for more than 1 pattern).
|
||||||
*
|
*
|
||||||
* All callbacks must be registered before the constructor completes.
|
* All callbacks must be registered before the constructor completes.
|
||||||
*/
|
*/
|
||||||
public abstract class Filter {
|
abstract class Filter {
|
||||||
|
|
||||||
public enum FilterContentType {
|
public enum FilterContentType {
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
@@ -68,7 +65,7 @@ public abstract class Filter {
|
|||||||
* @param contentIndex Matched index of the identifier or path.
|
* @param contentIndex Matched index of the identifier or path.
|
||||||
* @return True if the litho component should be filtered out.
|
* @return True if the litho component should be filtered out.
|
||||||
*/
|
*/
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
|
|
||||||
|
abstract class FilterGroup<T> {
|
||||||
|
final static class FilterGroupResult {
|
||||||
|
private BooleanSetting setting;
|
||||||
|
private int matchedIndex;
|
||||||
|
private int matchedLength;
|
||||||
|
// In the future it might be useful to include which pattern matched,
|
||||||
|
// but for now that is not needed.
|
||||||
|
|
||||||
|
FilterGroupResult() {
|
||||||
|
this(null, -1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterGroupResult(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
||||||
|
setValues(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValues(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.matchedIndex = matchedIndex;
|
||||||
|
this.matchedLength = matchedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A null value if the group has no setting,
|
||||||
|
* or if no match is returned from {@link FilterGroupList#check(Object)}.
|
||||||
|
*/
|
||||||
|
public BooleanSetting getSetting() {
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFiltered() {
|
||||||
|
return matchedIndex >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matched index of first pattern that matched, or -1 if nothing matched.
|
||||||
|
*/
|
||||||
|
public int getMatchedIndex() {
|
||||||
|
return matchedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the matched filter pattern.
|
||||||
|
*/
|
||||||
|
public int getMatchedLength() {
|
||||||
|
return matchedLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final BooleanSetting setting;
|
||||||
|
protected final T[] filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new filter group.
|
||||||
|
*
|
||||||
|
* @param setting The associated setting.
|
||||||
|
* @param filters The filters.
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public FilterGroup(final BooleanSetting setting, final T... filters) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.filters = filters;
|
||||||
|
if (filters.length == 0) {
|
||||||
|
throw new IllegalArgumentException("Must use one or more filter patterns (zero specified)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return setting == null || setting.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If {@link FilterGroupList} should include this group when searching.
|
||||||
|
* By default, all filters are included except non enabled settings that require reboot.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
public boolean includeInSearch() {
|
||||||
|
return isEnabled() || !setting.rebootApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": " + (setting == null ? "(null setting)" : setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract FilterGroupResult check(final T stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringFilterGroup extends FilterGroup<String> {
|
||||||
|
|
||||||
|
public StringFilterGroup(final BooleanSetting setting, final String... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult check(final String string) {
|
||||||
|
int matchedIndex = -1;
|
||||||
|
int matchedLength = 0;
|
||||||
|
if (isEnabled()) {
|
||||||
|
for (String pattern : filters) {
|
||||||
|
if (!string.isEmpty()) {
|
||||||
|
final int indexOf = string.indexOf(pattern);
|
||||||
|
if (indexOf >= 0) {
|
||||||
|
matchedIndex = indexOf;
|
||||||
|
matchedLength = pattern.length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you have more than 1 filter patterns, then all instances of
|
||||||
|
* this class should filtered using {@link ByteArrayFilterGroupList#check(byte[])},
|
||||||
|
* which uses a prefix tree to give better performance.
|
||||||
|
*/
|
||||||
|
class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||||
|
|
||||||
|
private volatile int[][] failurePatterns;
|
||||||
|
|
||||||
|
// Modified implementation from https://stackoverflow.com/a/1507813
|
||||||
|
private static int indexOf(final byte[] data, final byte[] pattern, final int[] failure) {
|
||||||
|
// Finds the first occurrence of the pattern in the byte array using
|
||||||
|
// KMP matching algorithm.
|
||||||
|
int patternLength = pattern.length;
|
||||||
|
for (int i = 0, j = 0, dataLength = data.length; i < dataLength; i++) {
|
||||||
|
while (j > 0 && pattern[j] != data[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == data[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j == patternLength) {
|
||||||
|
return i - patternLength + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] createFailurePattern(byte[] pattern) {
|
||||||
|
// Computes the failure function using a boot-strapping process,
|
||||||
|
// where the pattern is matched against itself.
|
||||||
|
final int patternLength = pattern.length;
|
||||||
|
final int[] failure = new int[patternLength];
|
||||||
|
|
||||||
|
for (int i = 1, j = 0; i < patternLength; i++) {
|
||||||
|
while (j > 0 && pattern[j] != pattern[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == pattern[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
failure[i] = j;
|
||||||
|
}
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayFilterGroup(BooleanSetting setting, byte[]... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the Strings into byte arrays. Used to search for text in binary data.
|
||||||
|
*/
|
||||||
|
public ByteArrayFilterGroup(BooleanSetting setting, String... filters) {
|
||||||
|
super(setting, ByteTrieSearch.convertStringsToBytes(filters));
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void buildFailurePatterns() {
|
||||||
|
if (failurePatterns != null) return; // Thread race and another thread already initialized the search.
|
||||||
|
Logger.printDebug(() -> "Building failure array for: " + this);
|
||||||
|
int[][] failurePatterns = new int[filters.length][];
|
||||||
|
int i = 0;
|
||||||
|
for (byte[] pattern : filters) {
|
||||||
|
failurePatterns[i++] = createFailurePattern(pattern);
|
||||||
|
}
|
||||||
|
this.failurePatterns = failurePatterns; // Must set after initialization finishes.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult check(final byte[] bytes) {
|
||||||
|
int matchedLength = 0;
|
||||||
|
int matchedIndex = -1;
|
||||||
|
if (isEnabled()) {
|
||||||
|
int[][] failures = failurePatterns;
|
||||||
|
if (failures == null) {
|
||||||
|
buildFailurePatterns(); // Lazy load.
|
||||||
|
failures = failurePatterns;
|
||||||
|
}
|
||||||
|
for (int i = 0, length = filters.length; i < length; i++) {
|
||||||
|
byte[] filter = filters[i];
|
||||||
|
matchedIndex = indexOf(bytes, filter, failures[i]);
|
||||||
|
if (matchedIndex >= 0) {
|
||||||
|
matchedLength = filter.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
package app.revanced.extension.shared.patches.litho;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
|
|
||||||
public abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
||||||
|
|
||||||
private final List<T> filterGroups = new ArrayList<>();
|
private final List<T> filterGroups = new ArrayList<>();
|
||||||
private final TrieSearch<V> search = createSearchGraph();
|
private final TrieSearch<V> search = createSearchGraph();
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public final void addAll(final T... groups) {
|
protected final void addAll(final T... groups) {
|
||||||
filterGroups.addAll(Arrays.asList(groups));
|
filterGroups.addAll(Arrays.asList(groups));
|
||||||
|
|
||||||
for (T group : groups) {
|
for (T group : groups) {
|
||||||
@@ -42,7 +41,18 @@ public abstract class FilterGroupList<V, T extends FilterGroup<V>> implements It
|
|||||||
return filterGroups.iterator();
|
return filterGroups.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterGroup.FilterGroupResult check(V stack) {
|
@Override
|
||||||
|
public void forEach(@NonNull Consumer<? super T> action) {
|
||||||
|
filterGroups.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Spliterator<T> spliterator() {
|
||||||
|
return filterGroups.spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FilterGroup.FilterGroupResult check(V stack) {
|
||||||
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
|
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
|
||||||
search.matches(stack, result);
|
search.matches(stack, result);
|
||||||
return result;
|
return result;
|
||||||
@@ -50,21 +60,21 @@ public abstract class FilterGroupList<V, T extends FilterGroup<V>> implements It
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract TrieSearch<V> createSearchGraph();
|
protected abstract TrieSearch<V> createSearchGraph();
|
||||||
|
}
|
||||||
|
|
||||||
public static final class StringFilterGroupList extends FilterGroupList<String, StringFilterGroup> {
|
final class StringFilterGroupList extends FilterGroupList<String, StringFilterGroup> {
|
||||||
protected StringTrieSearch createSearchGraph() {
|
protected StringTrieSearch createSearchGraph() {
|
||||||
return new StringTrieSearch();
|
return new StringTrieSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If searching for a single byte pattern, then it is slightly better to use
|
* If searching for a single byte pattern, then it is slightly better to use
|
||||||
* {@link ByteArrayFilterGroup#check(byte[])} as it uses KMP which is faster
|
* {@link ByteArrayFilterGroup#check(byte[])} as it uses KMP which is faster
|
||||||
* than a prefix tree to search for only 1 pattern.
|
* than a prefix tree to search for only 1 pattern.
|
||||||
*/
|
*/
|
||||||
public static final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFilterGroup> {
|
final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFilterGroup> {
|
||||||
protected ByteTrieSearch createSearchGraph() {
|
protected ByteTrieSearch createSearchGraph() {
|
||||||
return new ByteTrieSearch();
|
return new ByteTrieSearch();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class HideInfoCardsFilter extends Filter {
|
public final class HideInfoCardsFilter extends Filter {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import app.revanced.extension.shared.Utils;
|
|||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
@@ -43,7 +41,7 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
|||||||
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class KeywordContentFilter extends Filter {
|
final class KeywordContentFilter extends Filter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strings found in the buffer for every videos. Full strings should be specified.
|
* Strings found in the buffer for every videos. Full strings should be specified.
|
||||||
@@ -556,7 +554,7 @@ public final class KeywordContentFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (contentIndex != 0 && matchedGroup == startsWithFilter) {
|
if (contentIndex != 0 && matchedGroup == startsWithFilter) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ import androidx.annotation.Nullable;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
|
||||||
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
@@ -345,7 +342,7 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
// This identifier is used not only in players but also in search results:
|
// This identifier is used not only in players but also in search results:
|
||||||
// https://github.com/ReVanced/revanced-patches/issues/3245
|
// https://github.com/ReVanced/revanced-patches/issues/3245
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.shared.patches.litho;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -7,11 +7,9 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class LithoFilterPatch {
|
public final class LithoFilterPatch {
|
||||||
@@ -38,7 +36,7 @@ public final class LithoFilterPatch {
|
|||||||
builder.append(identifier);
|
builder.append(identifier);
|
||||||
builder.append(" Path: ");
|
builder.append(" Path: ");
|
||||||
builder.append(path);
|
builder.append(path);
|
||||||
if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) {
|
if (Settings.DEBUG_PROTOBUFFER.get()) {
|
||||||
builder.append(" BufferStrings: ");
|
builder.append(" BufferStrings: ");
|
||||||
findAsciiStrings(builder, buffer);
|
findAsciiStrings(builder, buffer);
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@@ -38,7 +36,7 @@ public final class PlaybackSpeedMenuFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == oldPlaybackMenuGroup) {
|
if (matchedGroup == oldPlaybackMenuGroup) {
|
||||||
isOldPlaybackSpeedMenuVisible = true;
|
isOldPlaybackSpeedMenuVisible = true;
|
||||||
|
|||||||
@@ -3,16 +3,13 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class PlayerFlyoutMenuItemsFilter extends Filter {
|
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||||
|
|
||||||
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
||||||
@Override
|
@Override
|
||||||
@@ -97,7 +94,7 @@ public final class PlayerFlyoutMenuItemsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == videoQualityMenuFooter) {
|
if (matchedGroup == videoQualityMenuFooter) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ import app.revanced.extension.youtube.patches.VideoInformation;
|
|||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for video id's in the proto buffer of Shorts dislike.
|
* Searches for video id's in the proto buffer of Shorts dislike.
|
||||||
@@ -87,13 +84,13 @@ public final class ReturnYouTubeDislikeFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) {
|
if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterGroupResult result = videoIdFilterGroup.check(buffer);
|
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(buffer);
|
||||||
if (result.isFiltered()) {
|
if (result.isFiltered()) {
|
||||||
String matchedVideoId = findVideoId(buffer);
|
String matchedVideoId = findVideoId(buffer);
|
||||||
// Matched video will be null if in incognito mode.
|
// Matched video will be null if in incognito mode.
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.patches.litho.Filter;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
|
||||||
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
@@ -342,7 +339,7 @@ public final class ShortsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (contentType == FilterContentType.PATH) {
|
if (contentType == FilterContentType.PATH) {
|
||||||
if (matchedGroup == subscribeButton || matchedGroup == joinButton
|
if (matchedGroup == subscribeButton || matchedGroup == joinButton
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import android.graphics.Color;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.shared.settings.EnumSetting;
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
@@ -50,7 +49,7 @@ import app.revanced.extension.youtube.patches.MiniplayerPatch;
|
|||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
||||||
|
|
||||||
public class Settings extends YouTubeAndMusicSettings {
|
public class Settings extends BaseSettings {
|
||||||
// Video
|
// Video
|
||||||
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
|
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
|
||||||
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
||||||
@@ -275,6 +274,11 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||||||
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
||||||
new ChangeStartPageTypeAvailability());
|
new ChangeStartPageTypeAvailability());
|
||||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
||||||
|
|
||||||
|
// Custom filter
|
||||||
|
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||||
|
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||||
|
|
||||||
// Navigation buttons
|
// Navigation buttons
|
||||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||||
@@ -352,7 +356,6 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
||||||
public static final BooleanSetting LOOP_VIDEO = new BooleanSetting("revanced_loop_video", FALSE);
|
public static final BooleanSetting LOOP_VIDEO = new BooleanSetting("revanced_loop_video", FALSE);
|
||||||
public static final BooleanSetting LOOP_VIDEO_BUTTON = new BooleanSetting("revanced_loop_video_button", FALSE);
|
public static final BooleanSetting LOOP_VIDEO_BUTTON = new BooleanSetting("revanced_loop_video_button", FALSE);
|
||||||
public static final BooleanSetting PAUSE_ON_AUDIO_INTERRUPT = new BooleanSetting("revanced_pause_on_audio_interrupt", FALSE, true);
|
|
||||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||||
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_CHAPTERS = new BooleanSetting("revanced_disable_haptic_feedback_chapters", FALSE);
|
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_CHAPTERS = new BooleanSetting("revanced_disable_haptic_feedback_chapters", FALSE);
|
||||||
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_PRECISE_SEEKING = new BooleanSetting("revanced_disable_haptic_feedback_precise_seeking", FALSE);
|
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_PRECISE_SEEKING = new BooleanSetting("revanced_disable_haptic_feedback_precise_seeking", FALSE);
|
||||||
@@ -364,6 +367,8 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
||||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_AV1 = new BooleanSetting("revanced_spoof_video_streams_av1", FALSE, true,
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS_AV1 = new BooleanSetting("revanced_spoof_video_streams_av1", FALSE, true,
|
||||||
"revanced_spoof_video_streams_av1_user_dialog_message", new SpoofClientAv1Availability());
|
"revanced_spoof_video_streams_av1_user_dialog_message", new SpoofClientAv1Availability());
|
||||||
|
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
|
||||||
|
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
||||||
|
|
||||||
// Swipe controls
|
// Swipe controls
|
||||||
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
||||||
@@ -376,7 +381,7 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||||
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL, true,
|
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
@@ -405,9 +410,7 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||||||
|
|
||||||
// SponsorBlock
|
// SponsorBlock
|
||||||
public static final BooleanSetting SB_ENABLED = new BooleanSetting("sb_enabled", TRUE);
|
public static final BooleanSetting SB_ENABLED = new BooleanSetting("sb_enabled", TRUE);
|
||||||
/**
|
/** Do not use id setting directly. Instead use {@link SponsorBlockSettings}. */
|
||||||
* Do not use id setting directly. Instead use {@link SponsorBlockSettings}.
|
|
||||||
*/
|
|
||||||
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "", parent(SB_ENABLED));
|
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "", parent(SB_ENABLED));
|
||||||
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
||||||
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.50.0-dev.1
|
version = 5.48.0-dev.1
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ public final class app/revanced/patches/all/misc/network/OverrideCertificatePinn
|
|||||||
public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/all/misc/optimization/StripPlatformLibrariesPatchKt {
|
||||||
|
public static final fun getStripPlatformLibrariesPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt {
|
public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt {
|
||||||
public static field packageNameOption Lapp/revanced/patcher/patch/Option;
|
public static field packageNameOption Lapp/revanced/patcher/patch/Option;
|
||||||
public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
@@ -104,10 +108,6 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa
|
|||||||
public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V
|
public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt {
|
|
||||||
public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
|
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
|
||||||
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
|
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
|
||||||
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
|
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
|
||||||
@@ -124,10 +124,6 @@ public final class app/revanced/patches/all/misc/screencapture/RemoveScreenCaptu
|
|||||||
public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatchKt {
|
|
||||||
public static final fun getPreventScreenshotDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt {
|
public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt {
|
||||||
public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -312,10 +308,6 @@ public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt
|
|||||||
public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatchKt {
|
|
||||||
public static final fun getHideHighlightsTrayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt {
|
public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt {
|
||||||
public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -340,10 +332,6 @@ public final class app/revanced/patches/instagram/misc/links/OpenLinksExternally
|
|||||||
public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatchKt {
|
|
||||||
public static final fun getRemoveBuildExpiredPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatchKt {
|
public final class app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatchKt {
|
||||||
public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -368,10 +356,6 @@ public final class app/revanced/patches/letterboxd/ads/HideAdsPatchKt {
|
|||||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatchKt {
|
|
||||||
public static final fun getUnlockAppIconsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
||||||
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -472,10 +456,6 @@ public final class app/revanced/patches/music/layout/compactheader/HideCategoryB
|
|||||||
public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatchKt {
|
|
||||||
public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt {
|
public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt {
|
||||||
public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -501,10 +481,6 @@ public final class app/revanced/patches/music/misc/androidauto/BypassCertificate
|
|||||||
public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatchKt {
|
|
||||||
public static final fun getUnlockAndroidAutoMediaBrowserPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
|
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
|
||||||
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -529,10 +505,6 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
|
|||||||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/litho/filter/LithoFilterPatchKt {
|
|
||||||
public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/privacy/SanitizeSharingLinksPatchKt {
|
public final class app/revanced/patches/music/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -589,14 +561,6 @@ public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt {
|
|||||||
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/nothingx/misc/extension/SharedExtensionPatchKt {
|
|
||||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchsKt {
|
|
||||||
public static final fun getShowK1TokensPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/nunl/ads/HideAdsPatchKt {
|
public final class app/revanced/patches/nunl/ads/HideAdsPatchKt {
|
||||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -952,10 +916,6 @@ public final class app/revanced/patches/shared/misc/hex/Replacement {
|
|||||||
public final fun getReplacementBytesPadded ()[B
|
public final fun getReplacementBytesPadded ()[B
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/litho/filter/LithoFilterPatchKt {
|
|
||||||
public static final fun getAddLithoFilter ()Lkotlin/jvm/functions/Function1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
|
public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
|
||||||
public final fun component1 ()Ljava/lang/String;
|
public final fun component1 ()Ljava/lang/String;
|
||||||
public final fun component2 ()Ljava/lang/String;
|
public final fun component2 ()Ljava/lang/String;
|
||||||
@@ -980,10 +940,6 @@ public final class app/revanced/patches/shared/misc/pairip/license/DisableLicens
|
|||||||
public static final fun getDisableLicenseCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableLicenseCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/privacy/DisableSentryTelemetryKt {
|
|
||||||
public static final fun getDisableSentryTelemetryPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
||||||
public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V
|
public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
@@ -1236,38 +1192,6 @@ public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt {
|
|||||||
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/distractions/HideDistractionsPatchKt {
|
|
||||||
public static final fun getHideDistractionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityKt {
|
|
||||||
public static final fun getAddGiveGroupKudosButtonToGroupActivity ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/media/download/AddMediaDownloadPatchKt {
|
|
||||||
public static final fun getAddMediaDownloadPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatchKt {
|
|
||||||
public static final fun getOverwriteMediaUploadParametersPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/misc/extension/SharedExtensionPatchKt {
|
|
||||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/password/EnablePasswordLoginPatchKt {
|
|
||||||
public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatchKt {
|
|
||||||
public static final fun getBlockSnowplowTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/quickedit/DisableQuickEditPatchKt {
|
|
||||||
public static final fun getDisableQuickEditPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
||||||
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1748,10 +1672,6 @@ public final class app/revanced/patches/youtube/misc/announcements/Announcements
|
|||||||
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatchKt {
|
|
||||||
public static final fun getPauseOnAudioInterruptPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
|
public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
|
||||||
public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -2047,7 +1967,6 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)V
|
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
@@ -2057,6 +1976,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
||||||
|
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
import org.w3c.dom.*
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
import javax.xml.transform.OutputKeys
|
|
||||||
import javax.xml.transform.TransformerFactory
|
|
||||||
import javax.xml.transform.dom.DOMSource
|
|
||||||
import javax.xml.transform.stream.StreamResult
|
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
|
|
||||||
patches {
|
patches {
|
||||||
@@ -29,6 +22,25 @@ dependencies {
|
|||||||
compileOnly(project(":patches:stub"))
|
compileOnly(project(":patches:stub"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
register<JavaExec>("preprocessCrowdinStrings") {
|
||||||
|
description = "Preprocess strings for Crowdin push"
|
||||||
|
|
||||||
|
dependsOn(compileKotlin)
|
||||||
|
|
||||||
|
classpath = sourceSets["main"].runtimeClasspath
|
||||||
|
mainClass.set("app.revanced.util.CrowdinPreprocessorKt")
|
||||||
|
|
||||||
|
args = listOf(
|
||||||
|
"src/main/resources/addresources/values/strings.xml",
|
||||||
|
// Ideally this would use build/tmp/crowdin/strings.xml
|
||||||
|
// But using that does not work with Crowdin pull because
|
||||||
|
// it does not recognize the strings.xml file belongs to this project.
|
||||||
|
"src/main/resources/addresources/values/strings.xml"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs = listOf("-Xcontext-receivers")
|
freeCompilerArgs = listOf("-Xcontext-receivers")
|
||||||
@@ -38,96 +50,12 @@ kotlin {
|
|||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = "githubPackages"
|
name = "GitHubPackages"
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
|
||||||
credentials(PasswordCredentials::class)
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("processStringsForCrowdin") {
|
|
||||||
description = "Process strings file for Crowdin by commenting out non-standard tags."
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
// Comment out the non-standard tags. Otherwise, Crowdin interprets the file
|
|
||||||
// not as Android but instead a generic xml file where strings are
|
|
||||||
// identified by xml position and not key
|
|
||||||
val stringsXmlFile = project.projectDir.resolve("src/main/resources/addresources/values/strings.xml")
|
|
||||||
|
|
||||||
val builder = DocumentBuilderFactory.newInstance().apply {
|
|
||||||
isIgnoringComments = false
|
|
||||||
isCoalescing = false
|
|
||||||
isNamespaceAware = false
|
|
||||||
}.newDocumentBuilder()
|
|
||||||
|
|
||||||
val document = builder.newDocument()
|
|
||||||
val root = document.createElement("resources").also(document::appendChild)
|
|
||||||
|
|
||||||
fun walk(node: Node, appId: String? = null, patchId: String? = null, insideResources: Boolean = false) {
|
|
||||||
fun walkChildren(el: Element, appId: String?, patchId: String?, insideResources: Boolean) {
|
|
||||||
val children = el.childNodes
|
|
||||||
for (i in 0 until children.length) {
|
|
||||||
walk(children.item(i), appId, patchId, insideResources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when (node.nodeType) {
|
|
||||||
Node.COMMENT_NODE -> {
|
|
||||||
val comment = document.createComment(node.nodeValue)
|
|
||||||
if (insideResources) root.appendChild(comment) else document.insertBefore(comment, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
Node.ELEMENT_NODE -> {
|
|
||||||
val element = node as Element
|
|
||||||
|
|
||||||
when (element.tagName) {
|
|
||||||
"resources" -> walkChildren(element, appId, patchId, insideResources = true)
|
|
||||||
|
|
||||||
"app" -> {
|
|
||||||
val newAppId = element.getAttribute("id")
|
|
||||||
|
|
||||||
root.appendChild(document.createComment(" <app id=\"$newAppId\"> "))
|
|
||||||
walkChildren(element, newAppId, patchId, insideResources)
|
|
||||||
root.appendChild(document.createComment(" </app> "))
|
|
||||||
}
|
|
||||||
|
|
||||||
"patch" -> {
|
|
||||||
val newPatchId = element.getAttribute("id")
|
|
||||||
|
|
||||||
root.appendChild(document.createComment(" <patch id=\"$newPatchId\"> "))
|
|
||||||
walkChildren(element, appId, newPatchId, insideResources)
|
|
||||||
root.appendChild(document.createComment(" </patch> "))
|
|
||||||
}
|
|
||||||
|
|
||||||
"string" -> {
|
|
||||||
val name = element.getAttribute("name")
|
|
||||||
val value = element.textContent
|
|
||||||
val fullName = "$appId.$patchId.$name"
|
|
||||||
|
|
||||||
val stringElement = document.createElement("string")
|
|
||||||
stringElement.setAttribute("name", fullName)
|
|
||||||
stringElement.appendChild(document.createTextNode(value))
|
|
||||||
root.appendChild(stringElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> walkChildren(element, appId, patchId, insideResources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.parse(stringsXmlFile).let {
|
|
||||||
val topLevel = it.childNodes
|
|
||||||
for (i in 0 until topLevel.length) {
|
|
||||||
val node = topLevel.item(i)
|
|
||||||
if (node != it.documentElement) walk(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
walk(it.documentElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
TransformerFactory.newInstance().newTransformer().apply {
|
|
||||||
setOutputProperty(OutputKeys.INDENT, "yes")
|
|
||||||
setOutputProperty(OutputKeys.ENCODING, "utf-8")
|
|
||||||
}.transform(DOMSource(document), StreamResult(stringsXmlFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.PatchException
|
|||||||
import app.revanced.patcher.patch.booleanOption
|
import app.revanced.patcher.patch.booleanOption
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patcher.patch.stringsOption
|
import app.revanced.patcher.patch.stringsOption
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.getNode
|
import app.revanced.util.getNode
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.patch.rawResourcePatch
|
|||||||
import app.revanced.patcher.patch.stringsOption
|
import app.revanced.patcher.patch.stringsOption
|
||||||
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
|
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
|
||||||
import app.revanced.patches.shared.misc.hex.hexPatch
|
import app.revanced.patches.shared.misc.hex.hexPatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hexPatch = rawResourcePatch(
|
val hexPatch = rawResourcePatch(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package app.revanced.patches.all.misc.network
|
|||||||
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
|
import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package app.revanced.patches.all.misc.optimization
|
||||||
|
|
||||||
|
import android.os.Build.SUPPORTED_ABIS
|
||||||
|
import app.revanced.patcher.patch.rawResourcePatch
|
||||||
|
import app.revanced.patcher.patch.stringsOption
|
||||||
|
import app.revanced.util.isAndroid
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val stripPlatformLibrariesPatch = rawResourcePatch(
|
||||||
|
"Strip platform libraries",
|
||||||
|
"Removes unused platform-native libraries from the APK to reduce package size" +
|
||||||
|
"- if detected automatically, the device's unsupported ABIs by default."
|
||||||
|
) {
|
||||||
|
val allAbis = listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||||
|
val supportedAbis = if (isAndroid) SUPPORTED_ABIS.toList() else allAbis
|
||||||
|
|
||||||
|
val platformsToKeep by stringsOption(
|
||||||
|
key = "platformsToKeep",
|
||||||
|
title = "Platforms to keep",
|
||||||
|
description = "The platforms to keep in the APK.",
|
||||||
|
default = supportedAbis,
|
||||||
|
values = mapOf("Keep all" to allAbis) + allAbis.associate { "Only $it" to listOf(it) },
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
val platforms = platformsToKeep!!
|
||||||
|
get("libs").listFiles { it.name !in platforms }?.forEach { it.deleteRecursively() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package app.revanced.patches.all.misc.playintegrity
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
|
|
||||||
|
|
||||||
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
|
|
||||||
"Landroid/content/Context;",
|
|
||||||
"bindService",
|
|
||||||
listOf("Landroid/content/Intent;", "Landroid/content/ServiceConnection;", "I"),
|
|
||||||
"Z"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val disablePlayIntegrityPatch = bytecodePatch(
|
|
||||||
name = "Disable Play Integrity",
|
|
||||||
description = "Prevents apps from using Play Integrity by pretending it is not available.",
|
|
||||||
use = false,
|
|
||||||
) {
|
|
||||||
extendWith("extensions/all/misc/disable-play-integrity.rve")
|
|
||||||
|
|
||||||
dependsOn(
|
|
||||||
transformInstructionsPatch(
|
|
||||||
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
|
|
||||||
val reference = instruction
|
|
||||||
.getReference<MethodReference>()
|
|
||||||
?.takeIf {
|
|
||||||
MethodUtil.methodSignaturesMatch(CONTEXT_BIND_SERVICE_METHOD_REFERENCE, it)
|
|
||||||
}
|
|
||||||
?: return@filterMap null
|
|
||||||
|
|
||||||
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
|
|
||||||
},
|
|
||||||
transform = { method, entry ->
|
|
||||||
val (instruction, index, parameterTypes) = entry
|
|
||||||
val parameterString = parameterTypes.joinToString(separator = "")
|
|
||||||
val registerString = "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}"
|
|
||||||
|
|
||||||
method.replaceInstruction(
|
|
||||||
index,
|
|
||||||
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package app.revanced.patches.all.misc.screenshot
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
|
||||||
|
|
||||||
private val registerScreenCaptureCallbackMethodReference = ImmutableMethodReference(
|
|
||||||
"Landroid/app/Activity;",
|
|
||||||
"registerScreenCaptureCallback",
|
|
||||||
listOf(
|
|
||||||
"Ljava/util/concurrent/Executor;",
|
|
||||||
"Landroid/app/Activity\$ScreenCaptureCallback;",
|
|
||||||
),
|
|
||||||
"V"
|
|
||||||
)
|
|
||||||
|
|
||||||
private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodReference(
|
|
||||||
"Landroid/app/Activity;",
|
|
||||||
"unregisterScreenCaptureCallback",
|
|
||||||
listOf(
|
|
||||||
"Landroid/app/Activity\$ScreenCaptureCallback;",
|
|
||||||
),
|
|
||||||
"V"
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val preventScreenshotDetectionPatch = bytecodePatch(
|
|
||||||
name = "Prevent screenshot detection",
|
|
||||||
description = "Removes the registration of all screen capture callbacks. This prevents the app from detecting screenshots.",
|
|
||||||
use = false
|
|
||||||
) {
|
|
||||||
dependsOn(transformInstructionsPatch(
|
|
||||||
filterMap = { _, _, instruction, instructionIndex ->
|
|
||||||
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null
|
|
||||||
|
|
||||||
val reference = instruction.getReference<MethodReference>() ?: return@transformInstructionsPatch null
|
|
||||||
|
|
||||||
instructionIndex.takeIf {
|
|
||||||
MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) ||
|
|
||||||
MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transform = { mutableMethod, instructionIndex ->
|
|
||||||
mutableMethod.removeInstruction(instructionIndex)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,11 @@ val skipAdsPatch = bytecodePatch(
|
|||||||
name = "Skip ads",
|
name = "Skip ads",
|
||||||
description = "Automatically skips ads.",
|
description = "Automatically skips ads.",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.disney.disneyplus")
|
compatibleWith(
|
||||||
|
"com.disney.disneyplus",
|
||||||
|
"in.startv.hotstar",
|
||||||
|
"in.startv.hotstaronly",
|
||||||
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.instagram.ghost.story
|
package app.revanced.patches.instagram.ghost.story
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -1,7 +1,29 @@
|
|||||||
package app.revanced.patches.instagram.hide.explore
|
package app.revanced.patches.instagram.hide.explore
|
||||||
|
|
||||||
|
import app.revanced.patcher.Fingerprint
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
context(BytecodePatchContext)
|
||||||
|
internal fun Fingerprint.replaceJsonFieldWithBogus(
|
||||||
|
key: String,
|
||||||
|
) {
|
||||||
|
val targetStringIndex = stringMatches!!.first { match -> match.string == key }.index
|
||||||
|
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacing the JSON key we want to skip with a random string that is not a valid JSON key.
|
||||||
|
* This way the feeds array will never be populated.
|
||||||
|
* Received JSON keys that are not handled are simply ignored, so there are no side effects.
|
||||||
|
*/
|
||||||
|
method.replaceInstruction(
|
||||||
|
targetStringIndex,
|
||||||
|
"const-string v$targetStringRegister, \"BOGUS\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hideExploreFeedPatch = bytecodePatch(
|
val hideExploreFeedPatch = bytecodePatch(
|
||||||
@@ -12,6 +34,6 @@ val hideExploreFeedPatch = bytecodePatch(
|
|||||||
compatibleWith("com.instagram.android")
|
compatibleWith("com.instagram.android")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
exploreResponseJsonParserFingerprint.replaceStringWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
|
exploreResponseJsonParserFingerprint.replaceJsonFieldWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.instagram.hide.highlightsTray
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal const val TARGET_STRING = "highlights_tray"
|
|
||||||
|
|
||||||
internal val highlightsUrlBuilderFingerprint = fingerprint {
|
|
||||||
strings(TARGET_STRING,"X-IG-Accept-Hint")
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.patches.instagram.hide.highlightsTray
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val hideHighlightsTrayPatch = bytecodePatch(
|
|
||||||
name = "Hide highlights tray",
|
|
||||||
description = "Hides the highlights tray in profile section.",
|
|
||||||
use = false
|
|
||||||
) {
|
|
||||||
compatibleWith("com.instagram.android")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
highlightsUrlBuilderFingerprint.replaceStringWithBogus(TARGET_STRING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,13 +28,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
|
|
||||||
dependsOn(sharedExtensionPatch)
|
dependsOn(sharedExtensionPatch)
|
||||||
|
|
||||||
val hideHome by booleanOption(
|
|
||||||
key = "hideHome",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Home",
|
|
||||||
description = "Permanently hides the Home button. App starts at next available tab." // On the "homecoming" / current instagram layout.
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideReels by booleanOption(
|
val hideReels by booleanOption(
|
||||||
key = "hideReels",
|
key = "hideReels",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -42,27 +35,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
description = "Permanently hides the Reels button."
|
description = "Permanently hides the Reels button."
|
||||||
)
|
)
|
||||||
|
|
||||||
val hideDirect by booleanOption(
|
|
||||||
key = "hideDirect",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Direct",
|
|
||||||
description = "Permanently hides the Direct button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideSearch by booleanOption(
|
|
||||||
key = "hideSearch",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Search",
|
|
||||||
description = "Permanently hides the Search button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideProfile by booleanOption(
|
|
||||||
key = "hideProfile",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Profile",
|
|
||||||
description = "Permanently hides the Profile button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideCreate by booleanOption(
|
val hideCreate by booleanOption(
|
||||||
key = "hideCreate",
|
key = "hideCreate",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -71,7 +43,7 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
if (!hideHome!! &&!hideReels!! && !hideDirect!! && !hideSearch!! && !hideProfile!! && !hideCreate!!) {
|
if (!hideReels!! && !hideCreate!!) {
|
||||||
return@execute Logger.getLogger(this::class.java.name).warning(
|
return@execute Logger.getLogger(this::class.java.name).warning(
|
||||||
"No hide navigation buttons options are enabled. No changes made."
|
"No hide navigation buttons options are enabled. No changes made."
|
||||||
)
|
)
|
||||||
@@ -104,13 +76,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideHome!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_feed")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hideReels!!) {
|
if (hideReels!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
@@ -118,33 +83,12 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideDirect!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_direct_tab")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (hideSearch!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_search")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hideCreate!!) {
|
if (hideCreate!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
instructionsRemoveButtonByName("fragment_share")
|
instructionsRemoveButtonByName("fragment_share")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideProfile!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_profile")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.instagram.hide.suggestions
|
package app.revanced.patches.instagram.hide.suggestions
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
import app.revanced.patches.instagram.hide.explore.replaceJsonFieldWithBogus
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hideSuggestedContent = bytecodePatch(
|
val hideSuggestedContent = bytecodePatch(
|
||||||
@@ -13,7 +13,7 @@ val hideSuggestedContent = bytecodePatch(
|
|||||||
|
|
||||||
execute {
|
execute {
|
||||||
FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key ->
|
FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key ->
|
||||||
feedItemParseFromJsonFingerprint.replaceStringWithBogus(key)
|
feedItemParseFromJsonFingerprint.replaceJsonFieldWithBogus(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.instagram.misc.devmenu
|
package app.revanced.patches.instagram.misc.devmenu
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
package app.revanced.patches.instagram.misc.removeBuildExpiredPopup
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
import app.revanced.util.literal
|
|
||||||
|
|
||||||
internal const val MILLISECOND_IN_A_DAY_LITERAL = 0x5265c00L
|
|
||||||
|
|
||||||
internal val appUpdateLockoutBuilderFingerprint = fingerprint {
|
|
||||||
strings("android.hardware.sensor.hinge_angle")
|
|
||||||
literal { MILLISECOND_IN_A_DAY_LITERAL }
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package app.revanced.patches.instagram.misc.removeBuildExpiredPopup
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val removeBuildExpiredPopupPatch = bytecodePatch(
|
|
||||||
name = "Remove build expired popup",
|
|
||||||
description = "Removes the popup that appears after a while, when the app version ages.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.instagram.android")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
appUpdateLockoutBuilderFingerprint.method.apply {
|
|
||||||
val longToIntIndex = instructions.first { it.opcode == Opcode.LONG_TO_INT }.location.index
|
|
||||||
val appAgeRegister = getInstruction<TwoRegisterInstruction>(longToIntIndex).registerA
|
|
||||||
|
|
||||||
// Set app age to 0 days old such that the build expired popup doesn't appear.
|
|
||||||
addInstruction(longToIntIndex + 1, "const v$appAgeRegister, 0x0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ internal val storyUrlResponseJsonParserFingerprint = fingerprint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val profileUrlResponseJsonParserFingerprint = fingerprint {
|
internal val profileUrlResponseJsonParserFingerprint = fingerprint {
|
||||||
strings("profile_to_share_url")
|
strings("profile_to_share_url", "ProfileUrlResponse")
|
||||||
custom { method, _ -> method.name == "parseFromJson" }
|
custom { method, _ -> method.name == "parseFromJson" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ val disableReelsScrollingPatch = bytecodePatch(
|
|||||||
name = "Disable Reels scrolling",
|
name = "Disable Reels scrolling",
|
||||||
description = "Disables the endless scrolling behavior in Instagram Reels, preventing swiping to the next Reel. " +
|
description = "Disables the endless scrolling behavior in Instagram Reels, preventing swiping to the next Reel. " +
|
||||||
"Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.",
|
"Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.",
|
||||||
use = false
|
use = true
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.instagram.android")
|
compatibleWith("com.instagram.android")
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package app.revanced.patches.instagram.shared
|
|
||||||
|
|
||||||
import app.revanced.patcher.Fingerprint
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
|
|
||||||
context(BytecodePatchContext)
|
|
||||||
internal fun Fingerprint.replaceStringWithBogus(
|
|
||||||
targetString: String,
|
|
||||||
) {
|
|
||||||
val targetStringIndex = stringMatches!!.first { match -> match.string == targetString }.index
|
|
||||||
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the 'target string' with 'BOGUS'.
|
|
||||||
* This is usually done when we need to override a JSON key or url,
|
|
||||||
* to skip with a random string that is not a valid JSON key.
|
|
||||||
*/
|
|
||||||
method.replaceInstruction(
|
|
||||||
targetStringIndex,
|
|
||||||
"const-string v$targetStringRegister, \"BOGUS\"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val getCanChangeAppIconFingerprint = fingerprint {
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "getCanChangeAppIcon" && classDef.type.endsWith("SettingsAppIconFragment;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val unlockAppIconsPatch = bytecodePatch(
|
|
||||||
name = "Unlock app icons",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.letterboxd.letterboxd")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
getCanChangeAppIconFingerprint.method.returnEarly(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.patches.music.layout.hide.general
|
|
||||||
|
|
||||||
import app.revanced.patches.music.misc.litho.filter.lithoFilterPatch
|
|
||||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
|
||||||
import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch
|
|
||||||
|
|
||||||
val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|
||||||
lithoFilterPatch = lithoFilterPatch,
|
|
||||||
settingsPatch = settingsPatch,
|
|
||||||
filterClasses = setOf("Lapp/revanced/extension/shared/patches/components/CustomFilter;"),
|
|
||||||
compatibleWithPackages = arrayOf("com.google.android.apps.youtube.music" to setOf("7.29.52", "8.10.52"))
|
|
||||||
)
|
|
||||||
@@ -5,11 +5,24 @@ import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch"))
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val bypassCertificateChecksPatch = bytecodePatch(
|
val bypassCertificateChecksPatch = bytecodePatch(
|
||||||
|
name = "Bypass certificate checks",
|
||||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||||
) {
|
) {
|
||||||
dependsOn(unlockAndroidAutoMediaBrowserPatch)
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
settingsPatch
|
||||||
|
)
|
||||||
|
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.apps.youtube.music"(
|
||||||
|
"7.29.52",
|
||||||
|
"8.10.52"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
checkCertificateFingerprint.method.returnEarly(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.revanced.patches.music.misc.androidauto
|
package app.revanced.patches.music.misc.androidauto
|
||||||
|
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
internal val checkCertificateFingerprint = fingerprint {
|
internal val checkCertificateFingerprint = fingerprint {
|
||||||
@@ -10,12 +11,3 @@ internal val checkCertificateFingerprint = fingerprint {
|
|||||||
"Failed to get certificate" // Partial String match.
|
"Failed to get certificate" // Partial String match.
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val searchMediaItemsConstructorFingerprint = fingerprint {
|
|
||||||
returns("V")
|
|
||||||
strings("ytm_media_browser/search_media_items")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val searchMediaItemsExecuteFingerprint = fingerprint {
|
|
||||||
parameters()
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user