You've already forked revanced-integrations
mirror of
https://github.com/revanced/revanced-integrations
synced 2025-11-19 03:23:27 +01:00
Compare commits
133 Commits
v0.119.2-d
...
v1.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7758a7ac4 | ||
|
|
fb433da6ad | ||
|
|
aa6f591141 | ||
|
|
0bb86694e2 | ||
|
|
d484f35127 | ||
|
|
f4e2d56b18 | ||
|
|
9394c512ba | ||
|
|
5d4c8b0a1b | ||
|
|
3a56431a28 | ||
|
|
fba7181e70 | ||
|
|
3b4f0206ad | ||
|
|
3d660e1b5e | ||
|
|
dd045ad985 | ||
|
|
0a15245f41 | ||
|
|
a18c134aaa | ||
|
|
0ea4e720ed | ||
|
|
c6a1b9fc59 | ||
|
|
d6ed0c061c | ||
|
|
3ba3d5108d | ||
|
|
c2112e066a | ||
|
|
6520499a27 | ||
|
|
b2d9dcd6c0 | ||
|
|
11abdf162f | ||
|
|
b514bdd7d0 | ||
|
|
eede028c96 | ||
|
|
9eb0471989 | ||
|
|
3dbfbf9e6f | ||
|
|
373820eb99 | ||
|
|
f936f240a7 | ||
|
|
b9d50fd3d8 | ||
|
|
11e7c04b70 | ||
|
|
2613e102cc | ||
|
|
2a582f9424 | ||
|
|
bc56aa5f07 | ||
|
|
1ebf8e04a6 | ||
|
|
c13504a0b3 | ||
|
|
0124f41cd6 | ||
|
|
31777e027e | ||
|
|
dedd2f658a | ||
|
|
7f0c2c507c | ||
|
|
17e78824b7 | ||
|
|
36fe5718cf | ||
|
|
355acc7023 | ||
|
|
dcca2e5e8d | ||
|
|
c4d01b2334 | ||
|
|
ce2ad04f60 | ||
|
|
1230d77f94 | ||
|
|
2735f99c0a | ||
|
|
64463dea5b | ||
|
|
4932a71088 | ||
|
|
f6e936812e | ||
|
|
c8510dbb4d | ||
|
|
d3021bcf0b | ||
|
|
ffcee71f79 | ||
|
|
8cbe2b5a92 | ||
|
|
abfbb0d7d5 | ||
|
|
ce0bedc5d3 | ||
|
|
5cf5d86913 | ||
|
|
7dd826ad71 | ||
|
|
58afcec641 | ||
|
|
781cdf88df | ||
|
|
ba15aba063 | ||
|
|
411b0f2ccc | ||
|
|
b078f0ca37 | ||
|
|
46bfeea1a3 | ||
|
|
d0c659ce11 | ||
|
|
25dc754ff5 | ||
|
|
726a2510a5 | ||
|
|
0978e64655 | ||
|
|
a5245b85a8 | ||
|
|
9a6ec6be8c | ||
|
|
8b5d2d1871 | ||
|
|
130f629f6b | ||
|
|
828ff6f31e | ||
|
|
847cce43f6 | ||
|
|
5f30100fd5 | ||
|
|
b1ce7a75eb | ||
|
|
e84b7b328e | ||
|
|
db0fb46a39 | ||
|
|
11f97ac354 | ||
|
|
831041b432 | ||
|
|
50933dc42d | ||
|
|
37b3b6a837 | ||
|
|
6e414ec6c2 | ||
|
|
4f57d56042 | ||
|
|
e94de61eb0 | ||
|
|
6b00f90fb7 | ||
|
|
85705141a8 | ||
|
|
3344375fb9 | ||
|
|
8d534f05d5 | ||
|
|
c048527dc0 | ||
|
|
1b29b7e11d | ||
|
|
f2037316d3 | ||
|
|
55fe1f0592 | ||
|
|
2453d30970 | ||
|
|
75297a52c1 | ||
|
|
df278222e8 | ||
|
|
21bccf6a99 | ||
|
|
40cfa1e9af | ||
|
|
0fbf7a3434 | ||
|
|
6bd5aae977 | ||
|
|
bf5071107b | ||
|
|
8cbb50b8ed | ||
|
|
98c91af130 | ||
|
|
959ae4f3be | ||
|
|
04608d32e8 | ||
|
|
3e08847dce | ||
|
|
debd0a2e11 | ||
|
|
e68646d4f7 | ||
|
|
c1c7e3b596 | ||
|
|
e68f558e9c | ||
|
|
f19f122fee | ||
|
|
6d5a5c8281 | ||
|
|
650a5e06ee | ||
|
|
bb9b35554f | ||
|
|
2bcd3b4ae5 | ||
|
|
59687f1a39 | ||
|
|
cd6ba256a5 | ||
|
|
16f1163a34 | ||
|
|
c47fcde242 | ||
|
|
f5add51fa7 | ||
|
|
bd307e475f | ||
|
|
d3eba27c90 | ||
|
|
12e663c548 | ||
|
|
7d02774ea1 | ||
|
|
cc3c9e78b2 | ||
|
|
fd09e46d01 | ||
|
|
d97b390866 | ||
|
|
6f79746d78 | ||
|
|
4f50ac6c49 | ||
|
|
9670bd305b | ||
|
|
f7e99832fd | ||
|
|
f4b6d33245 |
59
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
59
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a very clearly broken issue.
|
||||
title: 'bug: <title>'
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced bug report
|
||||
|
||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-integrations/labels/bug).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: How did you find the bug? Any additional details that might help?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Add the steps to reproduce this bug including your environment.
|
||||
placeholder: Step 1. Download some files. Step 2. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots or videos
|
||||
description: Add screenshots or videos that show the bug here.
|
||||
placeholder: Drag and drop the screenshots/videos into this box.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solution
|
||||
description: If applicable, add a possible solution.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
109
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
109
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a bug or an issue.
|
||||
title: 'bug: '
|
||||
labels: ['Bug report']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source
|
||||
width="256px"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
>
|
||||
<img
|
||||
width="256px"
|
||||
src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
>
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://revanced.app/">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://github.com/ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="http://revanced.app/discord">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/revancedapp">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://t.me/app_revanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://x.com/revancedapp">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/@ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
Continuing the legacy of Vanced
|
||||
</p>
|
||||
|
||||
# ReVanced Integrations bug report
|
||||
|
||||
Before creating a new bug report, please keep the following in mind:
|
||||
|
||||
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-integrations/labels/Bug%20report).
|
||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: |
|
||||
- Describe your bug in detail
|
||||
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
||||
- Add images and videos if possible
|
||||
- List used patches if applicable
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Error logs
|
||||
description: Exceptions can be captured by running `logcat | grep AndroidRuntime` in a shell.
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solution
|
||||
description: If applicable, add a possible solution to the bug.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your bug report will be closed if you don't follow the checklist below.
|
||||
options:
|
||||
- label: This issue is not a duplicate of an existing bug report.
|
||||
required: true
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
- label: All requested information has been provided properly.
|
||||
required: true
|
||||
46
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
46
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@@ -1,46 +0,0 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed feature request.
|
||||
title: 'feat: <title>'
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced feature request
|
||||
|
||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-integrations/labels/feature-request).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Functionality
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue
|
||||
description: What is the current problem. Why does it require a feature request?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe your feature in detail. How does it solve the issue?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: Why should your feature should be considered?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
105
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
105
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed request for a new feature.
|
||||
title: 'feat: '
|
||||
labels: ['Feature request']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source
|
||||
width="256px"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
>
|
||||
<img
|
||||
width="256px"
|
||||
src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
>
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://revanced.app/">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://github.com/ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="http://revanced.app/discord">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/revancedapp">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://t.me/app_revanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://x.com/revancedapp">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/@ReVanced">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
Continuing the legacy of Vanced
|
||||
</p>
|
||||
|
||||
# ReVanced Integrations feature request
|
||||
|
||||
Before creating a new feature request, please keep the following in mind:
|
||||
|
||||
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-integrations/labels/Feature%20request).
|
||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature description
|
||||
description: |
|
||||
- Describe your feature in detail
|
||||
- Add images, videos, links, examples, references, etc. if possible
|
||||
- Add the target application name in case you request a new patch
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
A strong motivation is necessary for a feature request to be considered.
|
||||
|
||||
- Why should this feature be implemented?
|
||||
- What is the explicit use case?
|
||||
- What are the benefits?
|
||||
- What makes this feature important?
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your feature request will be closed if you don't follow the checklist below.
|
||||
options:
|
||||
- label: This issue is not a duplicate of an existing feature request.
|
||||
required: true
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
- label: All requested information has been provided properly.
|
||||
required: true
|
||||
2
.github/config.yml
vendored
2
.github/config.yml
vendored
@@ -1,2 +1,2 @@
|
||||
firstPRMergeComment: >
|
||||
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
||||
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.
|
||||
|
||||
1
.github/workflows/pull_request.yml
vendored
1
.github/workflows/pull_request.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Open pull request
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
|
||||
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -23,23 +23,28 @@ jobs:
|
||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Cache
|
||||
|
||||
- name: Cache Node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
${{ runner.home }}/.gradle/caches
|
||||
${{ runner.home }}/.gradle/wrapper
|
||||
.gradle
|
||||
node_modules
|
||||
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
|
||||
- name: Setup Java
|
||||
run: echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew build clean
|
||||
|
||||
- name: Setup semantic-release
|
||||
run: npm install
|
||||
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@
|
||||
/.idea
|
||||
/.vscode
|
||||
/*.log
|
||||
node_modules
|
||||
474
CHANGELOG.md
474
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(mapOf("path" to ":dummy")))
|
||||
compileOnly("androidx.annotation:annotation:1.6.0")
|
||||
compileOnly("androidx.annotation:annotation:1.7.0")
|
||||
compileOnly("androidx.appcompat:appcompat:1.7.0-alpha03")
|
||||
compileOnly("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")
|
||||
compileOnly("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
|
||||
@@ -5,6 +5,10 @@ import android.view.WindowManager;
|
||||
|
||||
public class RemoveScreenshotRestrictionPatch {
|
||||
|
||||
public static void addFlags(Window window, int flags) {
|
||||
window.addFlags(flags & ~WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
public static void setFlags(Window window, int flags, int mask) {
|
||||
window.setFlags(flags & ~WindowManager.LayoutParams.FLAG_SECURE, mask & ~WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public class BrandingWaterMarkPatch {
|
||||
|
||||
// Used by: app.revanced.patches.youtube.layout.watermark.patch.HideWatermarkPatch
|
||||
public static boolean isBrandingWatermarkShown() {
|
||||
return SettingsEnum.HIDE_VIDEO_WATERMARK.getBoolean() == false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public final class DisableFullscreenAmbientModePatch {
|
||||
public static boolean enableFullScreenAmbientMode() {
|
||||
return !SettingsEnum.DISABLE_FULLSCREEN_AMBIENT_MODE.getBoolean();
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,14 @@ import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class DisableFineScrubbingGesturePatch {
|
||||
public final class DisablePreciseSeekingGesturePatch {
|
||||
/**
|
||||
* Disables the fine scrubbing gesture.
|
||||
* Disables the gesture that is used to seek precisely.
|
||||
* @param tracker The velocity tracker that is used to determine the gesture.
|
||||
* @param event The motion event that is used to determine the gesture.
|
||||
*/
|
||||
public static void disableGesture(VelocityTracker tracker, MotionEvent event) {
|
||||
if (SettingsEnum.DISABLE_FINE_SCRUBBING_GESTURE.getBoolean()) return;
|
||||
if (SettingsEnum.DISABLE_PRECISE_SEEKING_GESTURE.getBoolean()) return;
|
||||
|
||||
tracker.addMovement(event);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public class DisableResumingStartupShortsPlayerPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean disableResumingStartupShortsPlayer() {
|
||||
return SettingsEnum.DISABLE_RESUMING_SHORTS_PLAYER.getBoolean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public class DisableRollingNumberAnimationsPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean disableRollingNumberAnimations() {
|
||||
return SettingsEnum.DISABLE_ROLLING_NUMBER_ANIMATIONS.getBoolean();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public class DisableStartupShortsPlayerPatch {
|
||||
//Used by app.revanced.patches.youtube.layout.startupshortsreset.patch.DisableShortsOnStartupPatch
|
||||
public static boolean disableStartupShortsPlayer() {
|
||||
return SettingsEnum.DISABLE_RESUMING_SHORTS_PLAYER.getBoolean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public final class DisableSuggestedVideoEndScreenPatch {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static View lastView;
|
||||
|
||||
public static void closeEndScreen(final ImageView imageView) {
|
||||
if (!SettingsEnum.DISABLE_SUGGESTED_VIDEO_END_SCREEN.getBoolean()) return;
|
||||
|
||||
// Get a parent view which can be listened to for layout changes.
|
||||
final var parent = imageView.getParent().getParent();
|
||||
|
||||
// Prevent adding the listener multiple times.
|
||||
if (lastView == parent) return;
|
||||
|
||||
lastView = (ViewGroup)parent;
|
||||
lastView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
// Disable sound effects to prevent the click sound.
|
||||
imageView.setSoundEffectsEnabled(false);
|
||||
imageView.performClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static app.revanced.integrations.utils.StringRef.str;
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public class GmsCoreSupport {
|
||||
private static final String GMS_CORE_PACKAGE_NAME
|
||||
= getGmsCoreVendor() + ".android.gms";
|
||||
private static final String DONT_KILL_MY_APP_LINK
|
||||
= "https://dontkillmyapp.com";
|
||||
private static final Uri GMS_CORE_PROVIDER
|
||||
= Uri.parse("content://" + getGmsCoreVendor() + ".android.gsf.gservices/prefix");
|
||||
|
||||
private static void search(Context context, String uriString, String message) {
|
||||
ReVancedUtils.showToastLong(message);
|
||||
|
||||
var intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(SearchManager.QUERY, uriString);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static void checkAvailability() {
|
||||
var context = Objects.requireNonNull(ReVancedUtils.getContext());
|
||||
|
||||
try {
|
||||
context.getPackageManager().getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
LogHelper.printInfo(() -> "GmsCore was not found", exception);
|
||||
search(context, getGmsCoreDownloadLink(), str("gms_core_not_installed_warning"));
|
||||
|
||||
// Gracefully exit the app, so it does not crash.
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
||||
if (client != null) return;
|
||||
LogHelper.printInfo(() -> "GmsCore is not running in the background");
|
||||
search(context, DONT_KILL_MY_APP_LINK, str("gms_core_not_running_warning"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getGmsCoreDownloadLink() {
|
||||
final var vendor = getGmsCoreVendor();
|
||||
switch (vendor) {
|
||||
case "com.mgoogle":
|
||||
return "https://github.com/TeamVanced/VancedMicroG/releases/latest";
|
||||
case "app.revanced":
|
||||
return "https://github.com/revanced/gmscore/releases/latest";
|
||||
default:
|
||||
return vendor + ".android.gms";
|
||||
}
|
||||
}
|
||||
|
||||
// Modified by a patch. Do not touch.
|
||||
private static String getGmsCoreVendor() {
|
||||
return "app.revanced";
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,27 @@ package app.revanced.integrations.patches;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
public class HideBreakingNewsPatch {
|
||||
|
||||
/**
|
||||
* When spoofing to app versions older than 17.30.35, the watch history preview bar uses
|
||||
* When spoofing to app versions 17.31.00 and older, the watch history preview bar uses
|
||||
* the same layout components as the breaking news shelf.
|
||||
*
|
||||
* Breaking news does not appear to be present in these older versions anyways.
|
||||
*/
|
||||
private static boolean isSpoofingOldVersionWithHorizontalCardListWatchHistory() {
|
||||
return SettingsEnum.SPOOF_APP_VERSION.getBoolean()
|
||||
&& SettingsEnum.SPOOF_APP_VERSION_TARGET.getString().compareTo("17.30.35") < 0;
|
||||
}
|
||||
private static final boolean isSpoofingOldVersionWithHorizontalCardListWatchHistory =
|
||||
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("17.31.00");
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideBreakingNews(View view) {
|
||||
if (!SettingsEnum.HIDE_BREAKING_NEWS.getBoolean()
|
||||
|| isSpoofingOldVersionWithHorizontalCardListWatchHistory()) return;
|
||||
|| isSpoofingOldVersionWithHorizontalCardListWatchHistory) return;
|
||||
ReVancedUtils.hideViewByLayoutParams(view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import static app.revanced.integrations.utils.StringRef.str;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
public class MicroGSupport {
|
||||
private static final String MICROG_VENDOR = "com.mgoogle";
|
||||
private static final String MICROG_PACKAGE_NAME = MICROG_VENDOR + ".android.gms";
|
||||
private static final String VANCED_MICROG_DOWNLOAD_LINK = "https://github.com/TeamVanced/VancedMicroG/releases/latest";
|
||||
private static final String DONT_KILL_MY_APP_LINK = "https://dontkillmyapp.com";
|
||||
private static final Uri VANCED_MICROG_PROVIDER = Uri.parse("content://" + MICROG_VENDOR + ".android.gsf.gservices/prefix");
|
||||
|
||||
private static void startIntent(Context context, String uriString, String message) {
|
||||
ReVancedUtils.showToastLong(message);
|
||||
|
||||
var intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setData(Uri.parse(uriString));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
public static void checkAvailability() {
|
||||
var context = Objects.requireNonNull(ReVancedUtils.getContext());
|
||||
|
||||
try {
|
||||
context.getPackageManager().getPackageInfo(MICROG_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
LogHelper.printInfo(() -> "Vanced MicroG was not found", exception);
|
||||
startIntent(context, VANCED_MICROG_DOWNLOAD_LINK, str("microg_not_installed_warning"));
|
||||
|
||||
// Gracefully exit the app, so it does not crash.
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
||||
try (var client = context.getContentResolver().acquireContentProviderClient(VANCED_MICROG_PROVIDER)) {
|
||||
if (client != null) return;
|
||||
LogHelper.printInfo(() -> "Vanced MicroG is not running in the background");
|
||||
startIntent(context, DONT_KILL_MY_APP_LINK, str("microg_not_running_warning"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,39 @@ package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class MinimizedPlaybackPatch {
|
||||
|
||||
public static boolean isPlaybackNotShort() {
|
||||
return !PlayerType.getCurrent().isNoneOrHidden();
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean playbackIsNotShort() {
|
||||
// Steps to verify most edge cases:
|
||||
// 1. Open a regular video
|
||||
// 2. Minimize app (PIP should appear)
|
||||
// 3. Reopen app
|
||||
// 4. Open a Short (without closing the regular video)
|
||||
// (try opening both Shorts in the video player suggestions AND Shorts from the home feed)
|
||||
// 5. Minimize the app (PIP should not appear)
|
||||
// 6. Reopen app
|
||||
// 7. Close the Short
|
||||
// 8. Resume playing the regular video
|
||||
// 9. Minimize the app (PIP should appear)
|
||||
|
||||
if (!VideoInformation.lastVideoIdIsShort()) {
|
||||
return true; // Definitely is not a Short.
|
||||
}
|
||||
|
||||
// Might be a Short, or might be a prior regular video on screen again after a Short was closed.
|
||||
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Short,
|
||||
// But there's no way around this unless an additional hook is added to definitively detect
|
||||
// the Shorts player is on screen. This use case is unusual anyways so it's not a huge concern.
|
||||
return !PlayerType.getCurrent().isNoneHiddenOrMinimized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean overrideMinimizedPlaybackAvailable() {
|
||||
// This could be done entirely in the patch,
|
||||
// but having a unique method to search for makes manually inspecting the patched apk much easier.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class RemoveTrackingQueryParameterPatch {
|
||||
private static final String NEW_TRACKING_PARAMETER_REGEX = ".si=.+";
|
||||
private static final String OLD_TRACKING_PARAMETER_REGEX = ".feature=.+";
|
||||
|
||||
public static String sanitize(String url) {
|
||||
if (!SettingsEnum.REMOVE_TRACKING_QUERY_PARAMETER.getBoolean()) return url;
|
||||
|
||||
return url
|
||||
.replaceAll(NEW_TRACKING_PARAMETER_REGEX, "")
|
||||
.replaceAll(OLD_TRACKING_PARAMETER_REGEX, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class RestoreOldSeekbarThumbnailsPatch {
|
||||
public static boolean useFullscreenSeekbarThumbnails() {
|
||||
return !SettingsEnum.RESTORE_OLD_SEEKBAR_THUMBNAILS.getBoolean();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class SlideToSeekPatch {
|
||||
public static boolean isSlideToSeekDisabled() {
|
||||
return !SettingsEnum.SLIDE_TO_SEEK.getBoolean();
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,15 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Hooking class for the current playing video.
|
||||
* @noinspection unused
|
||||
*/
|
||||
public final class VideoInformation {
|
||||
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
|
||||
private static final String SEEK_METHOD_NAME = "seekTo";
|
||||
/**
|
||||
* Prefix present in all Short player parameters signature.
|
||||
*/
|
||||
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
|
||||
|
||||
private static WeakReference<Object> playerControllerRef;
|
||||
private static Method seekMethod;
|
||||
@@ -27,6 +32,7 @@ public final class VideoInformation {
|
||||
|
||||
@NonNull
|
||||
private static volatile String playerResponseVideoId = "";
|
||||
private static volatile boolean videoIdIsShort;
|
||||
|
||||
/**
|
||||
* The current playback speed
|
||||
@@ -64,12 +70,33 @@ public final class VideoInformation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the player parameters are for a Short.
|
||||
*/
|
||||
public static boolean playerParametersAreShort(@NonNull String parameters) {
|
||||
return parameters.startsWith(SHORTS_PLAYER_PARAMETERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String newPlayerResponseSignature(@NonNull String signature, boolean isShortAndOpeningOrPlaying) {
|
||||
final boolean isShort = playerParametersAreShort(signature);
|
||||
if (!isShort || isShortAndOpeningOrPlaying) {
|
||||
if (videoIdIsShort != isShort) {
|
||||
videoIdIsShort = isShort;
|
||||
LogHelper.printDebug(() -> "videoIdIsShort: " + isShort);
|
||||
}
|
||||
}
|
||||
return signature; // Return the original value since we are observing and not modifying.
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Called off the main thread.
|
||||
*
|
||||
* @param videoId The id of the last video loaded.
|
||||
*/
|
||||
public static void setPlayerResponseVideoId(@NonNull String videoId) {
|
||||
public static void setPlayerResponseVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
if (!playerResponseVideoId.equals(videoId)) {
|
||||
LogHelper.printDebug(() -> "New player response video id: " + videoId);
|
||||
playerResponseVideoId = videoId;
|
||||
@@ -89,7 +116,7 @@ public final class VideoInformation {
|
||||
|
||||
/**
|
||||
* Overrides the current playback speed.
|
||||
*
|
||||
* <p>
|
||||
* <b> Used exclusively by {@link RememberPlaybackSpeedPatch} </b>
|
||||
*/
|
||||
public static void overridePlaybackSpeed(float speedOverride) {
|
||||
@@ -124,32 +151,39 @@ public final class VideoInformation {
|
||||
/**
|
||||
* Seek on the current video.
|
||||
* Does not function for playback of Shorts.
|
||||
*
|
||||
* <p>
|
||||
* Caution: If called from a videoTimeHook() callback,
|
||||
* this will cause a recursive call into the same videoTimeHook() callback.
|
||||
*
|
||||
* @param millisecond The millisecond to seek the video to.
|
||||
* @return if the seek was successful
|
||||
* @return true if the seek was successful.
|
||||
*/
|
||||
public static boolean seekTo(final long millisecond) {
|
||||
final long videoLength = getVideoLength();
|
||||
|
||||
// Prevent issues such as play/ pause button or autoplay not working.
|
||||
final long seekToMilliseconds = Math.min(millisecond, VideoInformation.getVideoLength() - 250);
|
||||
|
||||
ReVancedUtils.verifyOnMainThread();
|
||||
try {
|
||||
LogHelper.printDebug(() -> "Seeking to " + millisecond);
|
||||
return (Boolean) seekMethod.invoke(playerControllerRef.get(), millisecond);
|
||||
LogHelper.printDebug(() -> "Seeking to " + seekToMilliseconds);
|
||||
//noinspection DataFlowIssue
|
||||
return (Boolean) seekMethod.invoke(playerControllerRef.get(), seekToMilliseconds);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "Failed to seek", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @noinspection UnusedReturnValue*/
|
||||
public static boolean seekToRelative(long millisecondsRelative) {
|
||||
return seekTo(videoTime + millisecondsRelative);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of the current video playing. Includes Shorts.
|
||||
* Id of the last video opened. Includes Shorts.
|
||||
*
|
||||
* @return The id of the video. Empty string if not set yet.
|
||||
* @return The id of the video, or an empty string if no videos have been opened yet.
|
||||
*/
|
||||
@NonNull
|
||||
public static String getVideoId() {
|
||||
@@ -158,20 +192,30 @@ public final class VideoInformation {
|
||||
|
||||
/**
|
||||
* Differs from {@link #videoId} as this is the video id for the
|
||||
* last player response received, which may not be the current video playing.
|
||||
*
|
||||
* last player response received, which may not be the last video opened.
|
||||
* <p>
|
||||
* If Shorts are loading the background, this commonly will be
|
||||
* different from the Short that is currently on screen.
|
||||
*
|
||||
* <p>
|
||||
* For most use cases, you should instead use {@link #getVideoId()}.
|
||||
*
|
||||
* @return The id of the last video loaded. Empty string if not set yet.
|
||||
* @return The id of the last video loaded, or an empty string if no videos have been loaded yet.
|
||||
*/
|
||||
@NonNull
|
||||
public static String getPlayerResponseVideoId() {
|
||||
return playerResponseVideoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the last player response video id _that was opened_ was a Short.
|
||||
* <p>
|
||||
* Note: This value returned may not match the status of {@link #getPlayerResponseVideoId()}
|
||||
* since that includes player responses for videos not opened.
|
||||
*/
|
||||
public static boolean lastVideoIdIsShort() {
|
||||
return videoIdIsShort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current playback speed.
|
||||
*/
|
||||
@@ -192,9 +236,9 @@ public final class VideoInformation {
|
||||
|
||||
/**
|
||||
* Playback time of the current video playing. Includes Shorts.
|
||||
*
|
||||
* <p>
|
||||
* Value will lag behind the actual playback time by a variable amount based on the playback speed.
|
||||
*
|
||||
* <p>
|
||||
* If playback speed is 2.0x, this value may be up to 2000ms behind the actual playback time.
|
||||
* If playback speed is 1.0x, this value may be up to 1000ms behind the actual playback time.
|
||||
* If playback speed is 0.5x, this value may be up to 500ms behind the actual playback time.
|
||||
@@ -208,12 +252,12 @@ public final class VideoInformation {
|
||||
|
||||
/**
|
||||
* @return If the playback is at the end of the video.
|
||||
*
|
||||
* <p>
|
||||
* If video is playing in the background with no video visible,
|
||||
* this always returns false (even if the video is actually at the end).
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to checking for {@link VideoState#ENDED},
|
||||
* but can give a more up to date result for code calling from some hooks.
|
||||
* but can give a more up-to-date result for code calling from some hooks.
|
||||
*
|
||||
* @see VideoState
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package app.revanced.integrations.patches.announcements;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import app.revanced.integrations.patches.announcements.requests.AnnouncementsRoutes;
|
||||
import app.revanced.integrations.requests.Requester;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.UUID;
|
||||
|
||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||
import static app.revanced.integrations.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENT;
|
||||
|
||||
public final class AnnouncementsPatch {
|
||||
private final static String CONSUMER = getOrSetConsumer();
|
||||
|
||||
private AnnouncementsPatch() {
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void showAnnouncement(final Activity context) {
|
||||
if (!SettingsEnum.ANNOUNCEMENTS.getBoolean()) return;
|
||||
|
||||
ReVancedUtils.runOnBackgroundThread(() -> {
|
||||
try {
|
||||
HttpURLConnection connection = AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT, CONSUMER);
|
||||
|
||||
LogHelper.printDebug(() -> "Get latest announcement route connection url: " + connection.getURL().toString());
|
||||
|
||||
try {
|
||||
// Do not show the announcement if the request failed.
|
||||
if (connection.getResponseCode() != 200) {
|
||||
if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return;
|
||||
|
||||
SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue("");
|
||||
ReVancedUtils.showToastLong("Failed to get announcement");
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
final var message = "Failed connecting to announcements provider";
|
||||
|
||||
LogHelper.printException(() -> message, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonString = Requester.parseInputStreamAndClose(connection.getInputStream(), false);
|
||||
|
||||
// Do not show the announcement if it is older or the same as the last one.
|
||||
final byte[] hashBytes = MessageDigest.getInstance("SHA-256").digest(jsonString.getBytes(StandardCharsets.UTF_8));
|
||||
final var hash = java.util.Base64.getEncoder().encodeToString(hashBytes);
|
||||
if (hash.equals(SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString())) return;
|
||||
|
||||
// Parse the announcement. Fall-back to raw string if it fails.
|
||||
String title;
|
||||
String message;
|
||||
Level level = Level.INFO;
|
||||
try {
|
||||
final var announcement = new JSONObject(jsonString);
|
||||
|
||||
title = announcement.getString("title");
|
||||
message = announcement.getJSONObject("content").getString("message");
|
||||
|
||||
if (!announcement.isNull("level")) level = Level.fromInt(announcement.getInt("level"));
|
||||
} catch (Throwable ex) {
|
||||
LogHelper.printException(() -> "Failed to parse announcement. Fall-backing to raw string", ex);
|
||||
|
||||
title = "Announcement";
|
||||
message = jsonString;
|
||||
}
|
||||
|
||||
final var finalTitle = title;
|
||||
final var finalMessage = Html.fromHtml(message, FROM_HTML_MODE_COMPACT);
|
||||
final Level finalLevel = level;
|
||||
|
||||
ReVancedUtils.runOnMainThread(() -> {
|
||||
// Show the announcement.
|
||||
var alertDialog = new android.app.AlertDialog.Builder(context)
|
||||
.setTitle(finalTitle)
|
||||
.setMessage(finalMessage)
|
||||
.setIcon(finalLevel.icon)
|
||||
.setPositiveButton("Ok", (dialog, which) -> {
|
||||
SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue(hash);
|
||||
dialog.dismiss();
|
||||
}).setNegativeButton("Dismiss", (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
|
||||
// Make links clickable.
|
||||
((TextView)alertDialog.findViewById(android.R.id.message))
|
||||
.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
});
|
||||
} catch (Exception e) {
|
||||
final var message = "Failed to get announcement";
|
||||
|
||||
LogHelper.printException(() -> message, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the last announcement hash if it is not empty.
|
||||
*
|
||||
* @return true if the last announcement hash was empty.
|
||||
*/
|
||||
private static boolean emptyLastAnnouncementHash() {
|
||||
if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return true;
|
||||
SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue("");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getOrSetConsumer() {
|
||||
final var consumer = SettingsEnum.ANNOUNCEMENT_CONSUMER.getString();
|
||||
if (!consumer.isEmpty()) return consumer;
|
||||
|
||||
final var uuid = UUID.randomUUID().toString();
|
||||
SettingsEnum.ANNOUNCEMENT_CONSUMER.saveValue(uuid);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
// TODO: Use better icons.
|
||||
private enum Level {
|
||||
INFO(android.R.drawable.ic_dialog_info),
|
||||
WARNING(android.R.drawable.ic_dialog_alert),
|
||||
SEVERE(android.R.drawable.ic_dialog_alert);
|
||||
|
||||
public final int icon;
|
||||
|
||||
Level(int icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public static Level fromInt(int value) {
|
||||
return values()[Math.min(value, values().length - 1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.integrations.patches.announcements.requests;
|
||||
|
||||
import app.revanced.integrations.requests.Requester;
|
||||
import app.revanced.integrations.requests.Route;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import static app.revanced.integrations.requests.Route.Method.GET;
|
||||
|
||||
public class AnnouncementsRoutes {
|
||||
private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v2";
|
||||
|
||||
|
||||
public static final Route GET_LATEST_ANNOUNCEMENT = new Route(GET, "/announcements/youtube/latest?consumer={consumer}");
|
||||
|
||||
private AnnouncementsRoutes() {
|
||||
}
|
||||
|
||||
public static HttpURLConnection getAnnouncementsConnectionFromRoute(Route route, String... params) throws IOException {
|
||||
return Requester.getConnectionFromRoute(ANNOUNCEMENTS_PROVIDER, route, params);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
public final class AdsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup shoppingLinks;
|
||||
|
||||
public AdsFilter() {
|
||||
exceptions.addPatterns(
|
||||
@@ -71,6 +72,11 @@ public final class AdsFilter extends Filter {
|
||||
"products_in_video"
|
||||
);
|
||||
|
||||
shoppingLinks = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_SHOPPING_LINKS,
|
||||
"expandable_list"
|
||||
);
|
||||
|
||||
final var webLinkPanel = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_WEB_SEARCH_RESULTS,
|
||||
"web_link_panel"
|
||||
@@ -93,6 +99,7 @@ public final class AdsFilter extends Filter {
|
||||
viewProducts,
|
||||
selfSponsor,
|
||||
webLinkPanel,
|
||||
shoppingLinks,
|
||||
movieAds
|
||||
);
|
||||
this.identifierFilterGroupList.addAll(carouselAd);
|
||||
@@ -104,6 +111,10 @@ public final class AdsFilter extends Filter {
|
||||
if (exceptions.matches(path))
|
||||
return false;
|
||||
|
||||
// Check for the index because of likelihood of false positives.
|
||||
if (matchedGroup == shoppingLinks && matchedIndex != 0)
|
||||
return false;
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package app.revanced.integrations.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
final class DescriptionComponentsFilter extends Filter {
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
|
||||
public DescriptionComponentsFilter() {
|
||||
exceptions.addPatterns(
|
||||
"compact_channel",
|
||||
"description",
|
||||
"grid_video",
|
||||
"inline_expander",
|
||||
"metadata"
|
||||
);
|
||||
|
||||
final StringFilterGroup chapterSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHAPTERS,
|
||||
"macro_markers_carousel"
|
||||
);
|
||||
|
||||
final StringFilterGroup infoCardsSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_INFO_CARDS_SECTION,
|
||||
"infocards_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup gameSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GAME_SECTION,
|
||||
"gaming_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup musicSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_MUSIC_SECTION,
|
||||
"music_section",
|
||||
"video_attributes_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup podcastSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_PODCAST_SECTION,
|
||||
"playlist_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup transcriptSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_TRANSCIPT_SECTION,
|
||||
"transcript_section"
|
||||
);
|
||||
|
||||
pathFilterGroupList.addAll(
|
||||
chapterSection,
|
||||
infoCardsSection,
|
||||
gameSection,
|
||||
musicSection,
|
||||
podcastSection,
|
||||
transcriptSection
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
if (exceptions.matches(path)) return false;
|
||||
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,8 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
@@ -13,6 +11,11 @@ import app.revanced.integrations.utils.StringTrieSearch;
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
|
||||
null,
|
||||
"cell_description_body"
|
||||
);
|
||||
private final CustomFilterGroup custom;
|
||||
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
|
||||
@@ -22,6 +25,14 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringFilterGroup searchResultShelfHeader;
|
||||
private final StringFilterGroup inFeedSurvey;
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
|
||||
static {
|
||||
mixPlaylistsExceptions.addPatterns(
|
||||
"V.ED", // Playlist browse id.
|
||||
"java.lang.ref.WeakReference"
|
||||
);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public LayoutComponentsFilter() {
|
||||
@@ -114,14 +125,14 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"official_card"
|
||||
);
|
||||
|
||||
final var expandableMetadata = new StringFilterGroup(
|
||||
expandableMetadata = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_EXPANDABLE_CHIP,
|
||||
"inline_expander"
|
||||
);
|
||||
|
||||
final var chapters = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHAPTERS,
|
||||
"macro_markers_carousel"
|
||||
final var videoQualityMenuFooter = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_VIDEO_QUALITY_MENU_FOOTER,
|
||||
"quality_sheet_footer"
|
||||
);
|
||||
|
||||
final var channelBar = new StringFilterGroup(
|
||||
@@ -175,11 +186,22 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"chips_shelf"
|
||||
);
|
||||
|
||||
final var channelWatermark = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_VIDEO_CHANNEL_WATERMARK,
|
||||
"featured_channel_watermark_overlay"
|
||||
);
|
||||
|
||||
final var forYouShelf = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_FOR_YOU_SHELF,
|
||||
"mixed_content_shelf"
|
||||
);
|
||||
|
||||
this.pathFilterGroupList.addAll(
|
||||
channelBar,
|
||||
communityPosts,
|
||||
paidContent,
|
||||
latestPosts,
|
||||
channelWatermark,
|
||||
communityGuidelines,
|
||||
quickActions,
|
||||
expandableMetadata,
|
||||
@@ -189,6 +211,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
joinMembership,
|
||||
medicalPanel,
|
||||
notifyMe,
|
||||
videoQualityMenuFooter,
|
||||
infoPanel,
|
||||
subscribersCommunityGuidelines,
|
||||
channelGuidelines,
|
||||
@@ -197,13 +220,13 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
timedReactions,
|
||||
imageShelf,
|
||||
channelMemberShelf,
|
||||
forYouShelf,
|
||||
custom
|
||||
);
|
||||
|
||||
this.identifierFilterGroupList.addAll(
|
||||
graySeparator,
|
||||
chipsShelf,
|
||||
chapters
|
||||
chipsShelf
|
||||
);
|
||||
}
|
||||
|
||||
@@ -211,7 +234,10 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey) return true;
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
|
||||
if (matchedGroup != custom && exceptions.matches(path))
|
||||
return false; // Exceptions are not filtered.
|
||||
@@ -222,18 +248,32 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* Called from a different place then the other filters.
|
||||
*/
|
||||
public static boolean filterMixPlaylists(final byte[] bytes) {
|
||||
final boolean isMixPlaylistFiltered = mixPlaylists.check(bytes).isFiltered();
|
||||
public static boolean filterMixPlaylists(final Object conversionContext, @Nullable final byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
LogHelper.printDebug(() -> "bytes is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isMixPlaylistFiltered)
|
||||
LogHelper.printDebug(() -> "Filtered mix playlist");
|
||||
// Prevent playlist items being hidden, if a mix playlist is present in it.
|
||||
if (mixPlaylistsExceptions.matches(conversionContext.toString()))
|
||||
return false;
|
||||
|
||||
return isMixPlaylistFiltered;
|
||||
if (!mixPlaylists.check(bytes).isFiltered())
|
||||
return false;
|
||||
|
||||
// Prevent hiding the description of some videos accidentally.
|
||||
if (mixPlaylistsExceptions2.check(bytes).isFiltered())
|
||||
return false;
|
||||
|
||||
LogHelper.printDebug(() -> "Filtered mix playlist");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean showWatermark() {
|
||||
return !SettingsEnum.HIDE_VIDEO_CHANNEL_WATERMARK.getBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user