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
59 Commits
v0.122.2-d
...
v1.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c635e5f91 | ||
|
|
f9102fa83b | ||
|
|
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 |
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
|
||||
181
CHANGELOG.md
181
CHANGELOG.md
@@ -1,3 +1,184 @@
|
||||
# [1.0.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.5...v1.0.0-dev.6) (2023-12-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Client spoof:** Do not break clips ([f9102fa](https://github.com/ReVanced/revanced-integrations/commit/f9102fa83bdb2b147543882cb8ebb80b5985ad3e))
|
||||
|
||||
# [1.0.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.4...v1.0.0-dev.5) (2023-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#533](https://github.com/ReVanced/revanced-integrations/issues/533)) ([fb433da](https://github.com/ReVanced/revanced-integrations/commit/fb433da6ad652aee48fc92794de82bb914ab80ca))
|
||||
|
||||
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2023-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#532](https://github.com/ReVanced/revanced-integrations/issues/532)) ([0bb8669](https://github.com/ReVanced/revanced-integrations/commit/0bb86694e24a6a41edee62f5ef1bb80fe7bc3f19))
|
||||
|
||||
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2023-12-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Prevent autoplay from stopping to work ([f4e2d56](https://github.com/ReVanced/revanced-integrations/commit/f4e2d56b181fee4d693dea1dfe81974237e4eff7))
|
||||
|
||||
# [1.0.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.1...v1.0.0-dev.2) (2023-12-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Fix dislikes sometimes not showing for non English language ([5d4c8b0](https://github.com/ReVanced/revanced-integrations/commit/5d4c8b0a1b77e97c7c0c02288927e92f3c9765ce))
|
||||
|
||||
# [1.0.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.125.1-dev.1...v1.0.0-dev.1) (2023-12-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Allow choosing the vendor of GmsCore via patch options ([#529](https://github.com/ReVanced/revanced-integrations/issues/529)) ([fba7181](https://github.com/ReVanced/revanced-integrations/commit/fba7181e70d695d7fb13c530754dc1db99b87216))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* The class `MicroGSupport` has been renamed to `GmsCoreSupport`
|
||||
|
||||
## [0.125.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.125.0...v0.125.1-dev.1) (2023-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Allow autoplay when skipping to the end of the video ([3d660e1](https://github.com/ReVanced/revanced-integrations/commit/3d660e1b5eeab9771f96bd2d26a222b835e2485c))
|
||||
|
||||
# [0.125.0](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1...v0.125.0) (2023-12-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Modernize restart logic ([#527](https://github.com/ReVanced/revanced-integrations/issues/527)) ([0ea4e72](https://github.com/ReVanced/revanced-integrations/commit/0ea4e720edab250aac02b32a8014c24b1127d02f))
|
||||
|
||||
# [0.125.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1...v0.125.0-dev.1) (2023-11-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Modernize restart logic ([#527](https://github.com/ReVanced/revanced-integrations/issues/527)) ([0ea4e72](https://github.com/ReVanced/revanced-integrations/commit/0ea4e720edab250aac02b32a8014c24b1127d02f))
|
||||
|
||||
## [0.124.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.0...v0.124.1) (2023-11-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Prevent app from crashing on startup ([c2112e0](https://github.com/ReVanced/revanced-integrations/commit/c2112e066af31890c5d441870d24f3582e9b1dd1))
|
||||
* **YouTube - Return YouTube dislike:** Fix layout of right to left languages ([11abdf1](https://github.com/ReVanced/revanced-integrations/commit/11abdf162f1a4c82a2330b16ca42800d8599c215))
|
||||
* **YouTube - Spoof signature:** fix error toast when viewing very old YouTube videos ([#525](https://github.com/ReVanced/revanced-integrations/issues/525)) ([eede028](https://github.com/ReVanced/revanced-integrations/commit/eede028c96042e6abc18377bf16a75e4806aba4a))
|
||||
|
||||
## [0.124.1-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1-dev.2...v0.124.1-dev.3) (2023-11-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Prevent app from crashing on startup ([c2112e0](https://github.com/ReVanced/revanced-integrations/commit/c2112e066af31890c5d441870d24f3582e9b1dd1))
|
||||
|
||||
## [0.124.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1-dev.1...v0.124.1-dev.2) (2023-11-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube dislike:** Fix layout of right to left languages ([11abdf1](https://github.com/ReVanced/revanced-integrations/commit/11abdf162f1a4c82a2330b16ca42800d8599c215))
|
||||
|
||||
## [0.124.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.0...v0.124.1-dev.1) (2023-11-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof signature:** fix error toast when viewing very old YouTube videos ([#525](https://github.com/ReVanced/revanced-integrations/issues/525)) ([eede028](https://github.com/ReVanced/revanced-integrations/commit/eede028c96042e6abc18377bf16a75e4806aba4a))
|
||||
|
||||
# [0.124.0](https://github.com/ReVanced/revanced-integrations/compare/v0.123.0...v0.124.0) (2023-11-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom playback speed:** Fix toast error on opening playback speed menu ([#522](https://github.com/ReVanced/revanced-integrations/issues/522)) ([0124f41](https://github.com/ReVanced/revanced-integrations/commit/0124f41cd699467e1c973ea42774c89b751f576f))
|
||||
* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](https://github.com/ReVanced/revanced-integrations/commit/bc56aa5f07d8fa5c241343ebf43887f360c79e74))
|
||||
* **YouTube - SponsorBlock:** Do not disable autoplay when skipping to the end of the video ([f936f24](https://github.com/ReVanced/revanced-integrations/commit/f936f240a7ad14bbf6130ef6960c0fa5542f78c5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](https://github.com/ReVanced/revanced-integrations/commit/11e7c04b707d86901809db3b4f427a0155787177))
|
||||
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](https://github.com/ReVanced/revanced-integrations/commit/2613e102cc446c910c4a2f750cc034e12995dd39))
|
||||
|
||||
# [0.124.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.124.0-dev.1) (2023-11-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](https://github.com/ReVanced/revanced-integrations/commit/bc56aa5f07d8fa5c241343ebf43887f360c79e74))
|
||||
* **YouTube - SponsorBlock:** Do not disable autoplay when skipping to the end of the video ([f936f24](https://github.com/ReVanced/revanced-integrations/commit/f936f240a7ad14bbf6130ef6960c0fa5542f78c5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](https://github.com/ReVanced/revanced-integrations/commit/11e7c04b707d86901809db3b4f427a0155787177))
|
||||
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](https://github.com/ReVanced/revanced-integrations/commit/2613e102cc446c910c4a2f750cc034e12995dd39))
|
||||
|
||||
# [0.124.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.124.0-dev.1) (2023-11-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](https://github.com/ReVanced/revanced-integrations/commit/bc56aa5f07d8fa5c241343ebf43887f360c79e74))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](https://github.com/ReVanced/revanced-integrations/commit/11e7c04b707d86901809db3b4f427a0155787177))
|
||||
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](https://github.com/ReVanced/revanced-integrations/commit/2613e102cc446c910c4a2f750cc034e12995dd39))
|
||||
|
||||
## [0.123.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.123.1-dev.2) (2023-11-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable tablet layout:** Respect the original device layout ([9e3e747](https://github.com/ReVanced/revanced-integrations/commit/9e3e747ce66d58b9ee74e812a68e74d1a88288e0))
|
||||
|
||||
## [0.123.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.0...v0.123.1-dev.1) (2023-11-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom playback speed:** Fix toast error on opening playback speed menu ([#522](https://github.com/ReVanced/revanced-integrations/issues/522)) ([0124f41](https://github.com/ReVanced/revanced-integrations/commit/0124f41cd699467e1c973ea42774c89b751f576f))
|
||||
|
||||
# [0.123.0](https://github.com/ReVanced/revanced-integrations/compare/v0.122.1...v0.123.0) (2023-11-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Check if `bytes` parameter of `LayoutComponentsFilter#filterMixPlaylists` for null ([abfbb0d](https://github.com/ReVanced/revanced-integrations/commit/abfbb0d7d58defee77424996af52fd82d6c4ef73))
|
||||
* **YouTube - Spoof client:** Fix frozen video on playback start ([#520](https://github.com/ReVanced/revanced-integrations/issues/520)) ([ffcee71](https://github.com/ReVanced/revanced-integrations/commit/ffcee71f798470577ade6d59cc0408c59c878f7e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add `Disable rolling number animations` patch ([#519](https://github.com/ReVanced/revanced-integrations/issues/519)) ([ce0bedc](https://github.com/ReVanced/revanced-integrations/commit/ce0bedc5d3b332760ee4d816a57fe64dc754ea0f))
|
||||
|
||||
## [0.122.2-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.122.2-dev.1...v0.122.2-dev.2) (2023-11-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof client:** Fix frozen video on playback start ([#520](https://github.com/ReVanced/revanced-integrations/issues/520)) ([9139954](https://github.com/ReVanced/revanced-integrations/commit/91399540ba55c80c437652bf919d5af7b080bdfb))
|
||||
|
||||
## [0.122.2-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.122.1...v0.122.2-dev.1) (2023-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Check if `bytes` parameter of `LayoutComponentsFilter#filterMixPlaylists` for null ([3eb07c1](https://github.com/ReVanced/revanced-integrations/commit/3eb07c12dda889f6ed50fb3b6900b2d789bb0c10))
|
||||
|
||||
## [0.122.1](https://github.com/ReVanced/revanced-integrations/compare/v0.122.0...v0.122.1) (2023-11-19)
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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().isNoneHiddenOrSlidingMinimized();
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.patches.components.ReturnYouTubeDislikeFilterPatch;
|
||||
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
@@ -27,19 +28,25 @@ import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislik
|
||||
* Handles all interaction of UI patch components.
|
||||
*
|
||||
* Known limitation:
|
||||
* Litho based Shorts player can experience temporarily frozen video playback if the RYD fetch takes too long.
|
||||
* The implementation of Shorts litho requires blocking the loading the first Short until RYD has completed.
|
||||
* This is because it modifies the dislikes text synchronously, and if the RYD fetch has
|
||||
* not completed yet then the UI will be temporarily frozen.
|
||||
*
|
||||
* Temporary work around:
|
||||
* Enable app spoofing to version 18.33.40 or older, as that uses a non litho Shorts player.
|
||||
*
|
||||
* Permanent fix (yet to be implemented), either of:
|
||||
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes asynchronously.
|
||||
* - Find a way to force Litho to rebuild it's component tree
|
||||
* (and use that hook to force the shorts dislikes to update after the fetch is completed).
|
||||
* A (yet to be implemented) solution that fixes this problem. Any one of:
|
||||
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes text asynchronously.
|
||||
* - Find a way to force Litho to rebuild it's component tree,
|
||||
* and use that hook to force the shorts dislikes to update after the fetch is completed.
|
||||
* - Hook into the dislikes button image view, and replace the dislikes thumb down image with a
|
||||
* generated image of the number of dislikes, then update the image asynchronously. This Could
|
||||
* also be used for the regular video player to give a better UI layout and completely remove
|
||||
* the need for the Rolling Number patches.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ReturnYouTubeDislikePatch {
|
||||
|
||||
public static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
|
||||
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
|
||||
|
||||
/**
|
||||
* RYD data for the current video on screen.
|
||||
*/
|
||||
@@ -318,8 +325,12 @@ public class ReturnYouTubeDislikePatch {
|
||||
try {
|
||||
if (SettingsEnum.RYD_ENABLED.getBoolean() && !SettingsEnum.RYD_COMPACT_LAYOUT.getBoolean()) {
|
||||
if (ReturnYouTubeDislike.isPreviouslyCreatedSegmentedSpan(text)) {
|
||||
// +1 pixel is needed for some foreign languages that measure
|
||||
// the text different from what is used for layout (Greek in particular).
|
||||
// Probably a bug in Android, but who knows.
|
||||
// Single line mode is also used as an additional fix for this issue.
|
||||
return measuredTextWidth + ReturnYouTubeDislike.leftSeparatorBounds.right
|
||||
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels;
|
||||
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels + 1;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -335,9 +346,17 @@ public class ReturnYouTubeDislikePatch {
|
||||
// YouTube Rolling Numbers do not use compound drawables or drawable padding.
|
||||
if (view.getCompoundDrawablePadding() == 0) {
|
||||
LogHelper.printDebug(() -> "Adding rolling number TextView changes");
|
||||
ShapeDrawable leftSeparator = ReturnYouTubeDislike.getLeftSeparatorDrawable();
|
||||
view.setCompoundDrawables(leftSeparator, null, null, null);
|
||||
view.setCompoundDrawablePadding(ReturnYouTubeDislike.leftSeparatorShapePaddingPixels);
|
||||
ShapeDrawable separator = ReturnYouTubeDislike.getLeftSeparatorDrawable();
|
||||
if (ReVancedUtils.isRightToLeftTextLayout()) {
|
||||
view.setCompoundDrawables(null, null, separator, null);
|
||||
} else {
|
||||
view.setCompoundDrawables(separator, null, null, null);
|
||||
}
|
||||
// Single line mode does not clip words if the span is larger than the view bounds.
|
||||
// The styled span applied to the view should always have the same bounds,
|
||||
// but use this feature just in case the measurements are somehow off by a few pixels.
|
||||
view.setSingleLine(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +369,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
LogHelper.printDebug(() -> "Removing rolling number TextView changes");
|
||||
view.setCompoundDrawablePadding(0);
|
||||
view.setCompoundDrawables(null, null, null, null);
|
||||
view.setSingleLine(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,26 +556,46 @@ public class ReturnYouTubeDislikePatch {
|
||||
// Video Id and voting hooks (all players).
|
||||
//
|
||||
|
||||
private static volatile boolean lastPlayerResponseWasShort;
|
||||
|
||||
/**
|
||||
* Injection point. Uses 'playback response' video id hook to preload RYD.
|
||||
*/
|
||||
public static void preloadVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
|
||||
public static void preloadVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
try {
|
||||
// Shorts shelf in home and subscription feed causes player response hook to be called,
|
||||
// and the 'is opening/playing' parameter will be false.
|
||||
// This hook will be called again when the Short is actually opened.
|
||||
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (!SettingsEnum.RYD_SHORTS.getBoolean() && PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized()) {
|
||||
if (!SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (videoId.equals(lastPrefetchedVideoId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean videoIdIsShort = VideoInformation.lastVideoIdIsShort();
|
||||
// Shorts shelf in home and subscription feed causes player response hook to be called,
|
||||
// and the 'is opening/playing' parameter will be false.
|
||||
// This hook will be called again when the Short is actually opened.
|
||||
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean())) {
|
||||
return;
|
||||
}
|
||||
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
&& videoIdIsShort && !lastPlayerResponseWasShort;
|
||||
lastPlayerResponseWasShort = videoIdIsShort;
|
||||
lastPrefetchedVideoId = videoId;
|
||||
|
||||
LogHelper.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
||||
ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
if (waitForFetchToComplete && !fetch.fetchCompleted()) {
|
||||
// This call is off the main thread, so wait until the RYD fetch completely finishes,
|
||||
// otherwise if this returns before the fetch completes then the UI can
|
||||
// become frozen when the main thread tries to modify the litho Shorts dislikes and
|
||||
// it must wait for the fetch.
|
||||
// Only need to do this for the first Short opened, as the next Short to swipe to
|
||||
// are preloaded in the background.
|
||||
//
|
||||
// If an asynchronous litho Shorts solution is found, then this blocking call should be removed.
|
||||
LogHelper.printDebug(() -> "Waiting for prefetch to complete: " + videoId);
|
||||
fetch.getFetchData(10000); // Use any arbitrarily large max wait time.
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "preloadVideoId failure", ex);
|
||||
}
|
||||
|
||||
@@ -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, boolean videoIsOpeningOrPlaying) {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import app.revanced.integrations.utils.StringTrieSearch;
|
||||
public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
|
||||
private static ByteArrayAsStringFilterGroup mixPlaylistsExceptions2;
|
||||
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
|
||||
null,
|
||||
"cell_description_body"
|
||||
);
|
||||
private final CustomFilterGroup custom;
|
||||
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
|
||||
@@ -25,6 +27,13 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
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() {
|
||||
exceptions.addPatterns(
|
||||
@@ -35,16 +44,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"library_recent_shelf"
|
||||
);
|
||||
|
||||
mixPlaylistsExceptions.addPatterns(
|
||||
"V.ED", // Playlist browse id.
|
||||
"java.lang.ref.WeakReference"
|
||||
);
|
||||
|
||||
mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
|
||||
null,
|
||||
"cell_description_body"
|
||||
);
|
||||
|
||||
custom = new CustomFilterGroup(
|
||||
SettingsEnum.CUSTOM_FILTER,
|
||||
SettingsEnum.CUSTOM_FILTER_STRINGS
|
||||
@@ -192,6 +191,11 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"featured_channel_watermark_overlay"
|
||||
);
|
||||
|
||||
final var forYouShelf = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_FOR_YOU_SHELF,
|
||||
"mixed_content_shelf"
|
||||
);
|
||||
|
||||
this.pathFilterGroupList.addAll(
|
||||
channelBar,
|
||||
communityPosts,
|
||||
@@ -216,6 +220,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
timedReactions,
|
||||
imageShelf,
|
||||
channelMemberShelf,
|
||||
forYouShelf,
|
||||
custom
|
||||
);
|
||||
|
||||
@@ -247,19 +252,25 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
* Injection point.
|
||||
* Called from a different place then the other filters.
|
||||
*/
|
||||
public static boolean filterMixPlaylists(final Object conversionContext, final byte[] bytes) {
|
||||
public static boolean filterMixPlaylists(final Object conversionContext, @Nullable final byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
LogHelper.printDebug(() -> "bytes is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent playlist items being hidden, if a mix playlist is present in it.
|
||||
if (mixPlaylistsExceptions.matches(conversionContext.toString()))
|
||||
return false;
|
||||
|
||||
if (!mixPlaylists.check(bytes).isFiltered()) return false;
|
||||
if (!mixPlaylists.check(bytes).isFiltered())
|
||||
return false;
|
||||
|
||||
// Prevent hiding the description of some videos accidentally.
|
||||
if (mixPlaylistsExceptions2.check(bytes).isFiltered()) return false;
|
||||
if (mixPlaylistsExceptions2.check(bytes).isFiltered())
|
||||
return false;
|
||||
|
||||
LogHelper.printDebug(() -> "Filtered mix playlist");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static boolean showWatermark() {
|
||||
|
||||
@@ -53,14 +53,14 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void newPlayerResponseVideoId(String videoId, boolean videoIsOpeningOrPlaying) {
|
||||
public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
try {
|
||||
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
|
||||
if (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
synchronized (lastVideoIds) {
|
||||
if (lastVideoIds.put(videoId, Boolean.TRUE) == null) {
|
||||
LogHelper.printDebug(() -> "New video id: " + videoId);
|
||||
LogHelper.printDebug(() -> "New Short video id: " + videoId);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -15,15 +15,23 @@ import java.util.Arrays;
|
||||
public class CustomPlaybackSpeedPatch {
|
||||
/**
|
||||
* Maximum playback speed, exclusive value. Custom speeds must be less than this value.
|
||||
* Limit is required otherwise double digit speeds show up out of order in the UI selector.
|
||||
*
|
||||
* Going over 8x does not increase the actual playback speed any higher,
|
||||
* and the UI selector starts flickering and acting weird.
|
||||
* Over 10x and the speeds show up out of order in the UI selector.
|
||||
*/
|
||||
public static final float MAXIMUM_PLAYBACK_SPEED = 10;
|
||||
public static final float MAXIMUM_PLAYBACK_SPEED = 8;
|
||||
|
||||
/**
|
||||
* Custom playback speeds.
|
||||
*/
|
||||
public static float[] customPlaybackSpeeds;
|
||||
|
||||
/**
|
||||
* The last time the old playback menu was forcefully called.
|
||||
*/
|
||||
private static long lastTimeOldPlaybackMenuInvoked;
|
||||
|
||||
/**
|
||||
* PreferenceList entries and values, of all available playback speeds.
|
||||
*/
|
||||
@@ -101,36 +109,48 @@ public class CustomPlaybackSpeedPatch {
|
||||
// For some reason, the custom playback speed flyout panel is activated when the user opens the share panel. (A/B tests)
|
||||
// Check the child count of playback speed flyout panel to prevent this issue.
|
||||
// Child count of playback speed flyout panel is always 8.
|
||||
if (PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible
|
||||
&& ((ViewGroup) recyclerView.getChildAt(0)).getChildCount() == 8) {
|
||||
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
|
||||
ViewGroup parentView3rd = (ViewGroup) recyclerView.getParent().getParent().getParent();
|
||||
ViewGroup parentView4th = (ViewGroup) parentView3rd.getParent();
|
||||
|
||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||
// This only shows in phone layout.
|
||||
|
||||
final var touchInsidedView = parentView4th.getChildAt(0);
|
||||
touchInsidedView.setSoundEffectsEnabled(false);
|
||||
touchInsidedView.performClick();
|
||||
|
||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||
parentView3rd.setVisibility(View.GONE);
|
||||
parentView4th.setVisibility(View.GONE);
|
||||
|
||||
// This works without issues for both tablet and phone layouts,
|
||||
// So no code is needed to check whether the current device is a tablet or phone.
|
||||
|
||||
// Close the new Playback speed menu and show the old one.
|
||||
showOldPlaybackSpeedMenu();
|
||||
if (!PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible || recyclerView.getChildCount() == 0) {
|
||||
return;
|
||||
}
|
||||
ViewGroup PlaybackSpeedParentView = (ViewGroup) recyclerView.getChildAt(0);
|
||||
if (PlaybackSpeedParentView == null || PlaybackSpeedParentView.getChildCount() != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
|
||||
ViewGroup parentView3rd = (ViewGroup) recyclerView.getParent().getParent().getParent();
|
||||
ViewGroup parentView4th = (ViewGroup) parentView3rd.getParent();
|
||||
|
||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||
// This only shows in phone layout.
|
||||
final var touchInsidedView = parentView4th.getChildAt(0);
|
||||
touchInsidedView.setSoundEffectsEnabled(false);
|
||||
touchInsidedView.performClick();
|
||||
|
||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||
parentView3rd.setVisibility(View.GONE);
|
||||
parentView4th.setVisibility(View.GONE);
|
||||
|
||||
// This works without issues for both tablet and phone layouts,
|
||||
// So no code is needed to check whether the current device is a tablet or phone.
|
||||
|
||||
// Close the new Playback speed menu and show the old one.
|
||||
showOldPlaybackSpeedMenu();
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "onFlyoutMenuCreate failure", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void showOldPlaybackSpeedMenu() {
|
||||
private static void showOldPlaybackSpeedMenu() {
|
||||
// This method is sometimes used multiple times.
|
||||
// To prevent this, ignore method reuse within 1 second.
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now - lastTimeOldPlaybackMenuInvoked < 1000) {
|
||||
LogHelper.printDebug(() -> "Ignoring call to showOldPlaybackSpeedMenu");
|
||||
return;
|
||||
}
|
||||
lastTimeOldPlaybackMenuInvoked = now;
|
||||
LogHelper.printDebug(() -> "Old video quality menu shown");
|
||||
|
||||
// Rest of the implementation added by patch.
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
package app.revanced.integrations.patches.spoof;
|
||||
|
||||
import static app.revanced.integrations.patches.spoof.requests.StoryboardRendererRequester.getStoryboardRenderer;
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import app.revanced.integrations.patches.VideoInformation;
|
||||
import app.revanced.integrations.patches.spoof.requests.StoryboardRendererRequester;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public class SpoofSignaturePatch {
|
||||
@@ -29,6 +21,11 @@ public class SpoofSignaturePatch {
|
||||
*/
|
||||
private static final String INCOGNITO_PARAMETERS = "CgIQBg==";
|
||||
|
||||
/**
|
||||
* Parameters used when playing clips.
|
||||
*/
|
||||
private static final String CLIPS_PARAMETERS = "kAIB";
|
||||
|
||||
/**
|
||||
* Parameters causing playback issues.
|
||||
*/
|
||||
@@ -43,37 +40,19 @@ public class SpoofSignaturePatch {
|
||||
*/
|
||||
private static final String SCRIM_PARAMETER = "SAFgAXgB";
|
||||
|
||||
/**
|
||||
* Parameters used in YouTube Shorts.
|
||||
*/
|
||||
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
|
||||
|
||||
/**
|
||||
* Last video id loaded. Used to prevent reloading the same spec multiple times.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile String lastPlayerResponseVideoId;
|
||||
|
||||
private static volatile Future<StoryboardRenderer> rendererFuture;
|
||||
@Nullable
|
||||
private static volatile StoryboardRenderer videoRenderer;
|
||||
|
||||
private static volatile boolean useOriginalStoryboardRenderer;
|
||||
|
||||
private static volatile boolean isPlayingShorts;
|
||||
|
||||
@Nullable
|
||||
private static StoryboardRenderer getRenderer() {
|
||||
if (rendererFuture != null) {
|
||||
try {
|
||||
return rendererFuture.get(2000, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException ex) {
|
||||
LogHelper.printDebug(() -> "Could not get renderer (get timed out)");
|
||||
} catch (ExecutionException | InterruptedException ex) {
|
||||
// Should never happen.
|
||||
LogHelper.printException(() -> "Could not get renderer", ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
@@ -81,63 +60,72 @@ public class SpoofSignaturePatch {
|
||||
*
|
||||
* @param parameters Original protobuf parameter value.
|
||||
*/
|
||||
public static String spoofParameter(String parameters) {
|
||||
LogHelper.printDebug(() -> "Original protobuf parameter value: " + parameters);
|
||||
public static String spoofParameter(String parameters, boolean isShortAndOpeningOrPlaying) {
|
||||
try {
|
||||
LogHelper.printDebug(() -> "Original protobuf parameter value: " + parameters);
|
||||
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) return parameters;
|
||||
|
||||
// Clip's player parameters contain a lot of information (e.g. video start and end time or whether it loops)
|
||||
// For this reason, the player parameters of a clip are usually very long (150~300 characters).
|
||||
// Clips are 60 seconds or less in length, so no spoofing.
|
||||
if (useOriginalStoryboardRenderer = parameters.length() > 150) return parameters;
|
||||
|
||||
// Shorts do not need to be spoofed.
|
||||
if (useOriginalStoryboardRenderer = parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) {
|
||||
isPlayingShorts = true;
|
||||
return parameters;
|
||||
}
|
||||
isPlayingShorts = false;
|
||||
|
||||
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL
|
||||
&& containsAny(parameters, AUTOPLAY_PARAMETERS);
|
||||
if (isPlayingFeed) {
|
||||
if (useOriginalStoryboardRenderer = !SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean()) {
|
||||
// Don't spoof the feed video playback. This will cause video playback issues,
|
||||
// but only if user continues watching for more than 1 minute.
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
|
||||
return parameters;
|
||||
}
|
||||
// Spoof the feed video. Video will show up in watch history and video subtitles are missing.
|
||||
fetchStoryboardRenderer();
|
||||
return SCRIM_PARAMETER + INCOGNITO_PARAMETERS;
|
||||
}
|
||||
|
||||
fetchStoryboardRenderer();
|
||||
// Clip's player parameters contain a lot of information (e.g. video start and end time or whether it loops)
|
||||
// For this reason, the player parameters of a clip are usually very long (150~300 characters).
|
||||
// Clips are 60 seconds or less in length, so no spoofing.
|
||||
//noinspection AssignmentUsedAsCondition
|
||||
if (useOriginalStoryboardRenderer = parameters.length() > 150 || containsAny(parameters, CLIPS_PARAMETERS)) {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// Shorts do not need to be spoofed.
|
||||
//noinspection AssignmentUsedAsCondition
|
||||
if (useOriginalStoryboardRenderer = VideoInformation.playerParametersAreShort(parameters)) {
|
||||
isPlayingShorts = true;
|
||||
return parameters;
|
||||
}
|
||||
isPlayingShorts = false;
|
||||
|
||||
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL
|
||||
&& containsAny(parameters, AUTOPLAY_PARAMETERS);
|
||||
if (isPlayingFeed) {
|
||||
//noinspection AssignmentUsedAsCondition
|
||||
if (useOriginalStoryboardRenderer = !SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean()) {
|
||||
// Don't spoof the feed video playback. This will cause video playback issues,
|
||||
// but only if user continues watching for more than 1 minute.
|
||||
return parameters;
|
||||
}
|
||||
// Spoof the feed video. Video will show up in watch history and video subtitles are missing.
|
||||
fetchStoryboardRenderer();
|
||||
return SCRIM_PARAMETER + INCOGNITO_PARAMETERS;
|
||||
}
|
||||
|
||||
fetchStoryboardRenderer();
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "spoofParameter failure", ex);
|
||||
}
|
||||
return INCOGNITO_PARAMETERS;
|
||||
}
|
||||
|
||||
private static void fetchStoryboardRenderer() {
|
||||
if (!SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
|
||||
lastPlayerResponseVideoId = null;
|
||||
rendererFuture = null;
|
||||
videoRenderer = null;
|
||||
return;
|
||||
}
|
||||
String videoId = VideoInformation.getPlayerResponseVideoId();
|
||||
if (!videoId.equals(lastPlayerResponseVideoId)) {
|
||||
rendererFuture = ReVancedUtils.submitOnBackgroundThread(() -> getStoryboardRenderer(videoId));
|
||||
lastPlayerResponseVideoId = videoId;
|
||||
// This will block starting video playback until the fetch completes.
|
||||
// This is desired because if this returns without finishing the fetch,
|
||||
// then video will start playback but the image will be frozen
|
||||
// while the main thread call for the renderer waits for the fetch to complete.
|
||||
videoRenderer = StoryboardRendererRequester.getStoryboardRenderer(videoId);
|
||||
}
|
||||
// Block until the fetch is completed. Without this, occasionally when a new video is opened
|
||||
// the video will be frozen a few seconds while the audio plays.
|
||||
// This is because the main thread is calling to get the storyboard but the fetch is not completed.
|
||||
// To prevent this, call get() here and block until the fetch is completed.
|
||||
// So later when the main thread calls to get the renderer it will never block as the future is done.
|
||||
getRenderer();
|
||||
}
|
||||
|
||||
private static String getStoryboardRendererSpec(String originalStoryboardRendererSpec,
|
||||
boolean returnNullIfLiveStream) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer();
|
||||
StoryboardRenderer renderer = videoRenderer;
|
||||
if (renderer != null) {
|
||||
if (returnNullIfLiveStream && renderer.isLiveStream()) return null;
|
||||
return renderer.getSpec();
|
||||
@@ -171,7 +159,7 @@ public class SpoofSignaturePatch {
|
||||
*/
|
||||
public static int getRecommendedLevel(int originalLevel) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer();
|
||||
StoryboardRenderer renderer = videoRenderer;
|
||||
if (renderer != null) {
|
||||
Integer recommendedLevel = renderer.getRecommendedLevel();
|
||||
if (recommendedLevel != null) return recommendedLevel;
|
||||
@@ -186,7 +174,17 @@ public class SpoofSignaturePatch {
|
||||
* if {@link SettingsEnum#SPOOF_STORYBOARD_RENDERER} is not enabled.
|
||||
*/
|
||||
public static boolean getSeekbarThumbnailOverrideValue() {
|
||||
return SettingsEnum.SPOOF_SIGNATURE.getBoolean();
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
|
||||
return false;
|
||||
}
|
||||
StoryboardRenderer renderer = videoRenderer;
|
||||
if (renderer == null) {
|
||||
// Spoof storyboard renderer is turned off,
|
||||
// video is paid, or the storyboard fetch timed out.
|
||||
// Show empty thumbnails so the seek time and chapters still show up.
|
||||
return true;
|
||||
}
|
||||
return renderer.getSpec() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,15 +193,19 @@ public class SpoofSignaturePatch {
|
||||
* @param view seekbar thumbnail view. Includes both shorts and regular videos.
|
||||
*/
|
||||
public static void seekbarImageViewCreated(ImageView view) {
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()
|
||||
|| SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (isPlayingShorts) return;
|
||||
try {
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()
|
||||
|| SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (isPlayingShorts) return;
|
||||
|
||||
view.setVisibility(View.GONE);
|
||||
// Also hide the border around the thumbnail (otherwise a 1 pixel wide bordered frame is visible).
|
||||
ViewGroup parentLayout = (ViewGroup) view.getParent();
|
||||
parentLayout.setPadding(0, 0, 0, 0);
|
||||
view.setVisibility(View.GONE);
|
||||
// Also hide the border around the thumbnail (otherwise a 1 pixel wide bordered frame is visible).
|
||||
ViewGroup parentLayout = (ViewGroup) view.getParent();
|
||||
parentLayout.setPadding(0, 0, 0, 0);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "seekbarImageViewCreated failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
package app.revanced.integrations.patches.spoof;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class StoryboardRenderer {
|
||||
@Nullable
|
||||
private final String spec;
|
||||
private final boolean isLiveStream;
|
||||
@Nullable
|
||||
private final Integer recommendedLevel;
|
||||
|
||||
public StoryboardRenderer(String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
|
||||
public StoryboardRenderer(@Nullable String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
|
||||
this.spec = spec;
|
||||
this.isLiveStream = isLiveStream;
|
||||
this.recommendedLevel = recommendedLevel;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Nullable
|
||||
public String getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ final class PlayerRoutes {
|
||||
static final String ANDROID_INNER_TUBE_BODY;
|
||||
static final String TV_EMBED_INNER_TUBE_BODY;
|
||||
|
||||
/**
|
||||
* TCP connection and HTTP read timeout
|
||||
*/
|
||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds.
|
||||
|
||||
static {
|
||||
JSONObject innerTubeBody = new JSONObject();
|
||||
|
||||
@@ -88,8 +93,8 @@ final class PlayerRoutes {
|
||||
connection.setUseCaches(false);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLISECONDS);
|
||||
connection.setReadTimeout(CONNECTION_TIMEOUT_MILLISECONDS);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,56 @@
|
||||
package app.revanced.integrations.patches.spoof.requests;
|
||||
|
||||
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.ANDROID_INNER_TUBE_BODY;
|
||||
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.GET_STORYBOARD_SPEC_RENDERER;
|
||||
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.TV_EMBED_INNER_TUBE_BODY;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.patches.spoof.StoryboardRenderer;
|
||||
import app.revanced.integrations.requests.Requester;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.*;
|
||||
import app.revanced.integrations.patches.spoof.StoryboardRenderer;
|
||||
import app.revanced.integrations.requests.Requester;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
public class StoryboardRendererRequester {
|
||||
|
||||
/**
|
||||
* For videos that have no storyboard.
|
||||
* Usually for low resolution videos as old as YouTube itself.
|
||||
* Does not include paid videos where the renderer fetch fails.
|
||||
*/
|
||||
private static final StoryboardRenderer emptyStoryboard
|
||||
= new StoryboardRenderer(null, false, null);
|
||||
|
||||
private StoryboardRendererRequester() {
|
||||
}
|
||||
|
||||
private static void randomlyWaitIfLocallyDebugging() {
|
||||
final boolean randomlyWait = false; // Enable to simulate slow connection responses.
|
||||
if (randomlyWait) {
|
||||
final long maximumTimeToRandomlyWait = 10000;
|
||||
ReVancedUtils.doNothingForDuration(maximumTimeToRandomlyWait);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleConnectionError(@NonNull String toastMessage, @Nullable Exception ex,
|
||||
boolean showToastOnIOException) {
|
||||
if (showToastOnIOException) ReVancedUtils.showToastShort(toastMessage);
|
||||
LogHelper.printInfo(() -> toastMessage, ex);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JSONObject fetchPlayerResponse(@NonNull String requestBody) {
|
||||
private static JSONObject fetchPlayerResponse(@NonNull String requestBody, boolean showToastOnIOException) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
ReVancedUtils.verifyOffMainThread();
|
||||
@@ -33,14 +62,21 @@ public class StoryboardRendererRequester {
|
||||
connection.getOutputStream().write(innerTubeBody, 0, innerTubeBody.length);
|
||||
|
||||
final int responseCode = connection.getResponseCode();
|
||||
randomlyWaitIfLocallyDebugging();
|
||||
if (responseCode == 200) return Requester.parseJSONObject(connection);
|
||||
|
||||
LogHelper.printException(() -> "API not available: " + responseCode);
|
||||
// Always show a toast for this, as a non 200 response means something is broken.
|
||||
handleConnectionError("Spoof storyboard not available: " + responseCode,
|
||||
null, showToastOnIOException || SettingsEnum.DEBUG_TOAST_ON_ERROR.getBoolean());
|
||||
connection.disconnect();
|
||||
} catch (SocketTimeoutException ex) {
|
||||
LogHelper.printException(() -> "API timed out", ex);
|
||||
handleConnectionError("Spoof storyboard temporarily not available (API timed out)",
|
||||
ex, showToastOnIOException);
|
||||
} catch (IOException ex) {
|
||||
handleConnectionError("Spoof storyboard temporarily not available: " + ex.getMessage(),
|
||||
ex, showToastOnIOException);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
|
||||
LogHelper.printException(() -> "Spoof storyboard fetch failed", ex); // Should never happen.
|
||||
} finally {
|
||||
LogHelper.printDebug(() -> "Request took: " + (System.currentTimeMillis() - startTime) + "ms");
|
||||
}
|
||||
@@ -64,8 +100,9 @@ public class StoryboardRendererRequester {
|
||||
* @return StoryboardRenderer or null if playabilityStatus is not OK.
|
||||
*/
|
||||
@Nullable
|
||||
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody) {
|
||||
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody);
|
||||
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody,
|
||||
boolean showToastOnIOException) {
|
||||
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody, showToastOnIOException);
|
||||
if (playerResponse != null && isPlayabilityStatusOk(playerResponse))
|
||||
return getStoryboardRendererUsingResponse(playerResponse);
|
||||
|
||||
@@ -76,6 +113,10 @@ public class StoryboardRendererRequester {
|
||||
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) {
|
||||
try {
|
||||
LogHelper.printDebug(() -> "Parsing response: " + playerResponse);
|
||||
if (!playerResponse.has("storyboards")) {
|
||||
LogHelper.printDebug(() -> "Using empty storyboard");
|
||||
return emptyStoryboard;
|
||||
}
|
||||
final JSONObject storyboards = playerResponse.getJSONObject("storyboards");
|
||||
final boolean isLiveStream = storyboards.has("playerLiveStoryboardSpecRenderer");
|
||||
final String storyboardsRendererTag = isLiveStream
|
||||
@@ -103,23 +144,19 @@ public class StoryboardRendererRequester {
|
||||
|
||||
@Nullable
|
||||
public static StoryboardRenderer getStoryboardRenderer(@NonNull String videoId) {
|
||||
try {
|
||||
Objects.requireNonNull(videoId);
|
||||
Objects.requireNonNull(videoId);
|
||||
|
||||
var renderer = getStoryboardRendererUsingBody(String.format(ANDROID_INNER_TUBE_BODY, videoId));
|
||||
var renderer = getStoryboardRendererUsingBody(
|
||||
String.format(ANDROID_INNER_TUBE_BODY, videoId), false);
|
||||
if (renderer == null) {
|
||||
LogHelper.printDebug(() -> videoId + " not available using Android client");
|
||||
renderer = getStoryboardRendererUsingBody(
|
||||
String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId), true);
|
||||
if (renderer == null) {
|
||||
LogHelper.printDebug(() -> videoId + " not available using Android client");
|
||||
renderer = getStoryboardRendererUsingBody(String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId));
|
||||
if (renderer == null) {
|
||||
LogHelper.printDebug(() -> videoId + " not available using TV embedded client");
|
||||
}
|
||||
LogHelper.printDebug(() -> videoId + " not available using TV embedded client");
|
||||
}
|
||||
|
||||
return renderer;
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
@@ -62,12 +62,12 @@ public class ReturnYouTubeDislikeApi {
|
||||
* How long to wait until API calls are resumed, if the API requested a back off.
|
||||
* No clear guideline of how long to wait until resuming.
|
||||
*/
|
||||
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 4 * 60 * 1000; // 4 Minutes.
|
||||
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 5 * 60 * 1000; // 5 Minutes.
|
||||
|
||||
/**
|
||||
* How long to wait until API calls are resumed, if any connection error occurs.
|
||||
*/
|
||||
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 60 * 1000; // 60 Seconds.
|
||||
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes.
|
||||
|
||||
/**
|
||||
* If non zero, then the system time of when API calls can resume.
|
||||
@@ -139,25 +139,13 @@ public class ReturnYouTubeDislikeApi {
|
||||
* Simulates a slow response by doing meaningless calculations.
|
||||
* Used to debug the app UI and verify UI timeout logic works
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
private static long randomlyWaitIfLocallyDebugging() {
|
||||
private static void randomlyWaitIfLocallyDebugging() {
|
||||
final boolean DEBUG_RANDOMLY_DELAY_NETWORK_CALLS = false; // set true to debug UI
|
||||
if (DEBUG_RANDOMLY_DELAY_NETWORK_CALLS) {
|
||||
final long amountOfTimeToWaste = (long) (Math.random()
|
||||
* (API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS + API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS));
|
||||
final long timeCalculationStarted = System.currentTimeMillis();
|
||||
LogHelper.printDebug(() -> "Artificially creating network delay of: " + amountOfTimeToWaste + "ms");
|
||||
|
||||
long meaninglessValue = 0;
|
||||
while (System.currentTimeMillis() - timeCalculationStarted < amountOfTimeToWaste) {
|
||||
// could do a thread sleep, but that will trigger an exception if the thread is interrupted
|
||||
meaninglessValue += Long.numberOfLeadingZeros((long)Math.exp(Math.random()));
|
||||
}
|
||||
// return the value, otherwise the compiler or VM might optimize and remove the meaningless time wasting work,
|
||||
// leaving an empty loop that hammers on the System.currentTimeMillis native call
|
||||
return meaninglessValue;
|
||||
ReVancedUtils.doNothingForDuration(amountOfTimeToWaste);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,7 @@ public enum SettingsEnum {
|
||||
HIDE_MERCHANDISE_BANNERS("revanced_hide_merchandise_banners", BOOLEAN, TRUE),
|
||||
HIDE_PAID_CONTENT("revanced_hide_paid_content_ads", BOOLEAN, TRUE),
|
||||
HIDE_PRODUCTS_BANNER("revanced_hide_products_banner", BOOLEAN, TRUE),
|
||||
HIDE_SHOPPING_LINKS("revanced_hide_shopping_links", BOOLEAN, TRUE),
|
||||
HIDE_SELF_SPONSOR("revanced_hide_self_sponsor_ads", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_ADS("revanced_hide_video_ads", BOOLEAN, TRUE, true),
|
||||
HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE),
|
||||
@@ -62,6 +63,7 @@ public enum SettingsEnum {
|
||||
CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)),
|
||||
DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true),
|
||||
DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE),
|
||||
DISABLE_ROLLING_NUMBER_ANIMATIONS("revanced_disable_rolling_number_animations", BOOLEAN, FALSE),
|
||||
DISABLE_SUGGESTED_VIDEO_END_SCREEN("revanced_disable_suggested_video_end_screen", BOOLEAN, TRUE),
|
||||
GRADIENT_LOADING_SCREEN("revanced_gradient_loading_screen", BOOLEAN, FALSE),
|
||||
HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true),
|
||||
@@ -113,6 +115,7 @@ public enum SettingsEnum {
|
||||
HIDE_TIMESTAMP("revanced_hide_timestamp", BOOLEAN, FALSE),
|
||||
@Deprecated HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_CHANNEL_WATERMARK("revanced_hide_channel_watermark", BOOLEAN, TRUE),
|
||||
HIDE_FOR_YOU_SHELF("revanced_hide_for_you_shelf", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_QUALITY_MENU_FOOTER("revanced_hide_video_quality_menu_footer", BOOLEAN, TRUE),
|
||||
PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true),
|
||||
PLAYER_POPUP_PANELS("revanced_hide_player_popup_panels", BOOLEAN, FALSE),
|
||||
|
||||
@@ -85,7 +85,7 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
||||
ReVancedSettingsFragment.settingImportInProgress = true;
|
||||
final boolean rebootNeeded = SettingsEnum.importJSON(replacementSettings);
|
||||
if (rebootNeeded) {
|
||||
ReVancedSettingsFragment.showRebootDialog(getContext());
|
||||
ReVancedSettingsFragment.showRestartDialog(getContext());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "importSettings failure", ex);
|
||||
|
||||
@@ -3,15 +3,10 @@ package app.revanced.integrations.settingsmenu;
|
||||
import static app.revanced.integrations.utils.StringRef.str;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
@@ -22,13 +17,12 @@ import android.preference.SwitchPreference;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.apps.youtube.app.application.Shell_HomeActivity;
|
||||
|
||||
import app.revanced.integrations.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.settings.SharedPrefCategory;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
import app.revanced.shared.settings.SettingsUtils;
|
||||
|
||||
public class ReVancedSettingsFragment extends PreferenceFragment {
|
||||
/**
|
||||
@@ -37,23 +31,13 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
||||
*/
|
||||
static boolean settingImportInProgress;
|
||||
|
||||
private static void reboot(@NonNull Context activity) {
|
||||
final int intentFlags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
||||
PendingIntent intent = PendingIntent.getActivity(activity, 0,
|
||||
new Intent(activity, Shell_HomeActivity.class), intentFlags);
|
||||
AlarmManager systemService = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
|
||||
systemService.setExact(AlarmManager.ELAPSED_REALTIME, 1500L, intent);
|
||||
Process.killProcess(Process.myPid());
|
||||
}
|
||||
|
||||
static void showRebootDialog(@NonNull Context activity) {
|
||||
static void showRestartDialog(@NonNull Context contxt) {
|
||||
String positiveButton = str("in_app_update_restart_button");
|
||||
String negativeButton = str("sign_in_cancel");
|
||||
new AlertDialog.Builder(activity).setMessage(str("pref_refresh_config"))
|
||||
new AlertDialog.Builder(contxt).setMessage(str("pref_refresh_config"))
|
||||
.setPositiveButton(positiveButton, (dialog, id) -> {
|
||||
reboot(activity);
|
||||
SettingsUtils.restartApp(contxt);
|
||||
})
|
||||
.setNegativeButton(negativeButton, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
@@ -110,9 +94,9 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
||||
|
||||
if (!showingUserDialogMessage) {
|
||||
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
|
||||
showSettingUserDialogConfirmation(getActivity(), (SwitchPreference) pref, setting);
|
||||
showSettingUserDialogConfirmation(getContext(), (SwitchPreference) pref, setting);
|
||||
} else if (setting.rebootApp) {
|
||||
showRebootDialog(getActivity());
|
||||
showRestartDialog(getContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,14 +172,14 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void showSettingUserDialogConfirmation(@NonNull Activity activity, SwitchPreference switchPref, SettingsEnum setting) {
|
||||
private void showSettingUserDialogConfirmation(@NonNull Context context, SwitchPreference switchPref, SettingsEnum setting) {
|
||||
showingUserDialogMessage = true;
|
||||
new AlertDialog.Builder(activity)
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(str("revanced_settings_confirm_user_dialog_title"))
|
||||
.setMessage(setting.userDialogMessage.toString())
|
||||
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
|
||||
if (setting.rebootApp) {
|
||||
showRebootDialog(activity);
|
||||
showRestartDialog(context);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, id) -> {
|
||||
|
||||
@@ -13,7 +13,6 @@ import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
|
||||
import app.revanced.integrations.patches.ReturnYouTubeDislikePatch;
|
||||
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.integrations.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
@@ -21,9 +20,6 @@ import app.revanced.integrations.settings.SharedPrefCategory;
|
||||
|
||||
public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
|
||||
|
||||
private static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
|
||||
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
|
||||
|
||||
/**
|
||||
* If dislikes are shown on Shorts.
|
||||
*/
|
||||
@@ -79,7 +75,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
|
||||
shortsPreference.setChecked(SettingsEnum.RYD_SHORTS.getBoolean());
|
||||
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
|
||||
String shortsSummary = str("revanced_ryd_shorts_summary_on",
|
||||
IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
? ""
|
||||
: "\n\n" + str("revanced_ryd_shorts_summary_disclaimer"));
|
||||
shortsPreference.setSummaryOn(shortsSummary);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package app.revanced.integrations.shared
|
||||
|
||||
import app.revanced.integrations.patches.VideoInformation
|
||||
import app.revanced.integrations.utils.Event
|
||||
import app.revanced.integrations.utils.LogHelper
|
||||
|
||||
/**
|
||||
* WatchWhile player type
|
||||
* WatchWhile player type.
|
||||
*/
|
||||
enum class PlayerType {
|
||||
/**
|
||||
@@ -83,6 +84,8 @@ enum class PlayerType {
|
||||
* Does not include the first moment after a short is opened when a regular video is minimized on screen,
|
||||
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
|
||||
* To include those situations instead use [isNoneHiddenOrMinimized].
|
||||
*
|
||||
* @see VideoInformation
|
||||
*/
|
||||
fun isNoneOrHidden(): Boolean {
|
||||
return this == NONE || this == HIDDEN
|
||||
@@ -99,6 +102,7 @@ enum class PlayerType {
|
||||
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
|
||||
*
|
||||
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
|
||||
* @see VideoInformation
|
||||
*/
|
||||
fun isNoneHiddenOrSlidingMinimized(): Boolean {
|
||||
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
|
||||
@@ -117,6 +121,7 @@ enum class PlayerType {
|
||||
*
|
||||
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
|
||||
* a regular video is minimized (and a new video is not being opened).
|
||||
* @see VideoInformation
|
||||
*/
|
||||
fun isNoneHiddenOrMinimized(): Boolean {
|
||||
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED
|
||||
|
||||
@@ -131,6 +131,7 @@ class SwipeControlsOverlayLayout(
|
||||
|
||||
override fun onEnterSwipeSession() {
|
||||
if (config.shouldEnableHapticFeedback) {
|
||||
@Suppress("DEPRECATION")
|
||||
performHapticFeedback(
|
||||
HapticFeedbackConstants.LONG_PRESS,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
|
||||
|
||||
@@ -112,6 +112,26 @@ public class ReVancedUtils {
|
||||
return backgroundThreadPool.submit(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a delay by doing meaningless calculations.
|
||||
* Used for debugging to verify UI timeout logic.
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static long doNothingForDuration(long amountOfTimeToWaste) {
|
||||
final long timeCalculationStarted = System.currentTimeMillis();
|
||||
LogHelper.printDebug(() -> "Artificially creating delay of: " + amountOfTimeToWaste + "ms");
|
||||
|
||||
long meaninglessValue = 0;
|
||||
while (System.currentTimeMillis() - timeCalculationStarted < amountOfTimeToWaste) {
|
||||
// could do a thread sleep, but that will trigger an exception if the thread is interrupted
|
||||
meaninglessValue += Long.numberOfLeadingZeros((long) Math.exp(Math.random()));
|
||||
}
|
||||
// return the value, otherwise the compiler or VM might optimize and remove the meaningless time wasting work,
|
||||
// leaving an empty loop that hammers on the System.currentTimeMillis native call
|
||||
return meaninglessValue;
|
||||
}
|
||||
|
||||
|
||||
public static boolean containsAny(@NonNull String value, @NonNull String... targets) {
|
||||
return indexOfFirstFound(value, targets) >= 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user