mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 09:53:55 +01:00
Compare commits
47 Commits
v5.11.0-de
...
v5.13.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fca2f70c0e | ||
|
|
348f7e12cb | ||
|
|
b6b7208eeb | ||
|
|
a2c79f1349 | ||
|
|
4f5bb3c915 | ||
|
|
4b77d27c77 | ||
|
|
7991c80129 | ||
|
|
6baf4ea2ac | ||
|
|
c89538c8f5 | ||
|
|
94fb367618 | ||
|
|
354835966d | ||
|
|
168f9b769e | ||
|
|
e4c4b3a73a | ||
|
|
fce98b4960 | ||
|
|
839aa81e9c | ||
|
|
905bb0ea5f | ||
|
|
a94a663859 | ||
|
|
04b37dd55a | ||
|
|
2382e9d09e | ||
|
|
97f504976a | ||
|
|
0a6c5158e0 | ||
|
|
a959d798e8 | ||
|
|
39a0b9bda6 | ||
|
|
92c38b2cb4 | ||
|
|
4732210d4b | ||
|
|
f30a49f1cb | ||
|
|
bcd157dd2b | ||
|
|
d299ea5973 | ||
|
|
a20021e290 | ||
|
|
373ca966f3 | ||
|
|
12de922afa | ||
|
|
580bb3cf6c | ||
|
|
421af92f4c | ||
|
|
4d03e1b5a1 | ||
|
|
24d68df6cd | ||
|
|
e9aee17746 | ||
|
|
7c4285e3e6 | ||
|
|
e3110271a7 | ||
|
|
0079eceb87 | ||
|
|
af2a97cb16 | ||
|
|
aeb552e8f2 | ||
|
|
6e936fea42 | ||
|
|
f63769f39f | ||
|
|
1c9ab20a63 | ||
|
|
cdeccad908 | ||
|
|
399889c6fa | ||
|
|
ec77861410 |
127
CHANGELOG.md
127
CHANGELOG.md
@@ -1,3 +1,130 @@
|
||||
# [5.13.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.3...v5.13.0-dev.4) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
|
||||
|
||||
# [5.13.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.2...v5.13.0-dev.3) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
|
||||
|
||||
# [5.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.1...v5.13.0-dev.2) (2025-02-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([29b265d](https://github.com/ReVanced/revanced-patches/commit/29b265d8fdaa48502650be9623bfc518a57a0bb1))
|
||||
|
||||
# [5.13.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0-dev.1) (2025-02-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([198e4d2](https://github.com/ReVanced/revanced-patches/commit/198e4d2a2315c24a09eb9ecfefbd131a75384d2c))
|
||||
|
||||
# [5.12.0](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0) (2025-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([1bd7986](https://github.com/ReVanced/revanced-patches/commit/1bd7986823e774a929c8a9102a7cc96e245d5274))
|
||||
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([83d116e](https://github.com/ReVanced/revanced-patches/commit/83d116e8fd3935ee431cfdf0b8e095d04ee77259))
|
||||
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([0412c79](https://github.com/ReVanced/revanced-patches/commit/0412c7901dc8599b6079d9c3ba26452f88af642b))
|
||||
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([9a88b42](https://github.com/ReVanced/revanced-patches/commit/9a88b4239fd63d5f91105fec8e7d59d318a5d09a))
|
||||
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
|
||||
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([b31fed9](https://github.com/ReVanced/revanced-patches/commit/b31fed98901fcda1bce6f05eb0de63280c689fa0))
|
||||
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
|
||||
|
||||
# [5.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.6...v5.12.0-dev.7) (2025-02-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([0412c79](https://github.com/ReVanced/revanced-patches/commit/0412c7901dc8599b6079d9c3ba26452f88af642b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
|
||||
|
||||
# [5.12.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.5...v5.12.0-dev.6) (2025-02-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([1bd7986](https://github.com/ReVanced/revanced-patches/commit/1bd7986823e774a929c8a9102a7cc96e245d5274))
|
||||
|
||||
# [5.12.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.4...v5.12.0-dev.5) (2025-02-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
|
||||
|
||||
# [5.12.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.3...v5.12.0-dev.4) (2025-02-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([b31fed9](https://github.com/ReVanced/revanced-patches/commit/b31fed98901fcda1bce6f05eb0de63280c689fa0))
|
||||
|
||||
# [5.12.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.2...v5.12.0-dev.3) (2025-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([83d116e](https://github.com/ReVanced/revanced-patches/commit/83d116e8fd3935ee431cfdf0b8e095d04ee77259))
|
||||
|
||||
# [5.12.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.1...v5.12.0-dev.2) (2025-02-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([9a88b42](https://github.com/ReVanced/revanced-patches/commit/9a88b4239fd63d5f91105fec8e7d59d318a5d09a))
|
||||
|
||||
# [5.12.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0-dev.1) (2025-02-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
|
||||
|
||||
# [5.11.0](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.11.0) (2025-02-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
|
||||
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
|
||||
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
|
||||
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b434182](https://github.com/ReVanced/revanced-patches/commit/b434182df69313c4eb5f0dfd98101cb80e46ead2))
|
||||
|
||||
# [5.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.11.0-dev.1...v5.11.0-dev.2) (2025-02-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
|
||||
|
||||
# [5.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.3...v5.11.0-dev.1) (2025-02-05)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
|
||||
@@ -12,7 +12,7 @@ import android.os.Handler;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/** @noinspection deprecation, unused */
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class SpoofWifiPatch {
|
||||
|
||||
// Used to check what the (real or fake) active network is (take a look at `hasTransport`).
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.extension.all.misc.directory.documentsprovider;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
@@ -23,6 +24,7 @@ import java.util.Objects;
|
||||
/**
|
||||
* A DocumentsProvider that allows access to the app's internal data directory.
|
||||
*/
|
||||
@SuppressLint("LongLogTag")
|
||||
public class InternalDataDocumentsProvider extends DocumentsProvider {
|
||||
private static final String[] rootColumns =
|
||||
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
|
||||
@@ -5,7 +5,8 @@ import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
public final class RemoveScreencaptureRestrictionPatch {
|
||||
@SuppressWarnings("unused")
|
||||
public final class RemoveScreenCaptureRestrictionPatch {
|
||||
// Member of AudioAttributes.Builder
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) {
|
||||
@@ -1 +1,16 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.extension.all.screenshot.removerestriction;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RemoveScreenshotRestrictionPatch {
|
||||
|
||||
public static void addFlags(Window window, int flags) {
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
// Do not remove. Necessary for the extension plugin to be applied to the project.
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -29,6 +29,6 @@ public class BaseSettings {
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
||||
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
|
||||
// Client type must be last spoof setting due to cyclic references.
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_UNPLUGGED, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ public class BooleanSetting extends Setting<Boolean> {
|
||||
*/
|
||||
public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) {
|
||||
setting.value = Objects.requireNonNull(newValue);
|
||||
|
||||
if (setting.isSetToDefault()) {
|
||||
setting.removeFromPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +69,8 @@ public class BooleanSetting extends Setting<Boolean> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Boolean newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveBoolean(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveBoolean(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -89,10 +89,8 @@ public class EnumSetting<T extends Enum<?>> extends Setting<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull T newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveEnumAsString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveEnumAsString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class FloatSetting extends Setting<Float> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Float newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveFloatString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveFloatString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class IntegerSetting extends Setting<Integer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Integer newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveIntegerString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveIntegerString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class LongSetting extends Setting<Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Long newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveLongString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveLongString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.*;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Setting<T> {
|
||||
|
||||
/**
|
||||
@@ -288,6 +287,13 @@ public abstract class Setting<T> {
|
||||
*/
|
||||
public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) {
|
||||
setting.setValueFromString(newValue);
|
||||
|
||||
// Clear the preference value since default is used, to allow changing
|
||||
// the changing the default for a future release. Without this after upgrading
|
||||
// the saved value will be whatever was the default when the app was first installed.
|
||||
if (setting.isSetToDefault()) {
|
||||
setting.removeFromPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +309,33 @@ public abstract class Setting<T> {
|
||||
/**
|
||||
* Persistently saves the value.
|
||||
*/
|
||||
public abstract void save(@NonNull T newValue);
|
||||
public final void save(@NonNull T newValue) {
|
||||
if (value.equals(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
|
||||
if (defaultValue.equals(newValue)) {
|
||||
removeFromPreferences();
|
||||
} else {
|
||||
saveToPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save {@link #value} to {@link #preferences}.
|
||||
*/
|
||||
protected abstract void saveToPreferences();
|
||||
|
||||
/**
|
||||
* Remove {@link #value} from {@link #preferences}.
|
||||
*/
|
||||
protected final void removeFromPreferences() {
|
||||
Logger.printDebug(() -> "Clearing stored preference value (reset to default): " + key);
|
||||
preferences.removeKey(key);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract T get();
|
||||
|
||||
@@ -55,10 +55,8 @@ public class StringSetting extends Setting<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull String newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -66,22 +66,6 @@ public enum ClientType {
|
||||
true,
|
||||
"Android Creator"
|
||||
),
|
||||
ANDROID_VR(
|
||||
ANDROID_VR_NO_AUTH.id,
|
||||
ANDROID_VR_NO_AUTH.clientName,
|
||||
ANDROID_VR_NO_AUTH.packageName,
|
||||
ANDROID_VR_NO_AUTH.deviceMake,
|
||||
ANDROID_VR_NO_AUTH.deviceModel,
|
||||
ANDROID_VR_NO_AUTH.osName,
|
||||
ANDROID_VR_NO_AUTH.osVersion,
|
||||
ANDROID_VR_NO_AUTH.androidSdkVersion,
|
||||
ANDROID_VR_NO_AUTH.buildId,
|
||||
ANDROID_VR_NO_AUTH.cronetVersion,
|
||||
ANDROID_VR_NO_AUTH.clientVersion,
|
||||
ANDROID_VR_NO_AUTH.requiresAuth,
|
||||
true,
|
||||
"Android VR"
|
||||
),
|
||||
IOS_UNPLUGGED(
|
||||
33,
|
||||
"IOS_UNPLUGGED",
|
||||
@@ -112,6 +96,22 @@ public enum ClientType {
|
||||
forceAVC()
|
||||
? "iOS TV Force AVC"
|
||||
: "iOS TV"
|
||||
),
|
||||
ANDROID_VR_AUTH(
|
||||
ANDROID_VR_NO_AUTH.id,
|
||||
ANDROID_VR_NO_AUTH.clientName,
|
||||
ANDROID_VR_NO_AUTH.packageName,
|
||||
ANDROID_VR_NO_AUTH.deviceMake,
|
||||
ANDROID_VR_NO_AUTH.deviceModel,
|
||||
ANDROID_VR_NO_AUTH.osName,
|
||||
ANDROID_VR_NO_AUTH.osVersion,
|
||||
ANDROID_VR_NO_AUTH.androidSdkVersion,
|
||||
ANDROID_VR_NO_AUTH.buildId,
|
||||
ANDROID_VR_NO_AUTH.cronetVersion,
|
||||
ANDROID_VR_NO_AUTH.clientVersion,
|
||||
ANDROID_VR_NO_AUTH.requiresAuth,
|
||||
true,
|
||||
"Android VR"
|
||||
);
|
||||
|
||||
private static boolean forceAVC() {
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -3,3 +3,14 @@ dependencies {
|
||||
compileOnly(project(":extensions:tiktok:stub"))
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 22
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
protected void syncSettingWithPreference(@NonNull Preference pref,
|
||||
@NonNull Setting<?> setting,
|
||||
boolean applySettingToPreference) {
|
||||
if (pref instanceof RangeValuePreference rangeValuePref) {
|
||||
if (pref instanceof RangeValuePreference) {
|
||||
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
|
||||
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
||||
} else if (pref instanceof DownloadPathPreference downloadPathPref) {
|
||||
} else if (pref instanceof DownloadPathPreference) {
|
||||
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
|
||||
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
||||
} else {
|
||||
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
||||
@@ -32,7 +34,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
final var context = getContext();
|
||||
final var context = getActivity();
|
||||
|
||||
// Currently no resources can be compiled for TikTok (fails with aapt error).
|
||||
// So all TikTok Strings are hard coded in the extension.
|
||||
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 22
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:tumblr:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,3 +6,14 @@ dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.appcompat)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 21
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//noinspection GradleDependency
|
||||
android.compileSdk = 33
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:youtube:stub"))
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeD
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
@@ -366,9 +365,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();
|
||||
|
||||
private static void clearRemovedShortsTextViews() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // YouTube requires Android N or greater
|
||||
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
|
||||
}
|
||||
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
@@ -58,7 +55,6 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* @return If the app is currently in background PiP mode.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
private static boolean isAppInBackgroundPiPMode() {
|
||||
Activity activity = mainActivityRef.get();
|
||||
return activity != null && activity.isInPictureInPictureMode();
|
||||
@@ -80,7 +76,6 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
||||
try {
|
||||
final boolean autoplay;
|
||||
|
||||
@@ -7,13 +7,10 @@ import static app.revanced.extension.youtube.patches.announcements.requests.Anno
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -31,7 +28,6 @@ public final class AnnouncementsPatch {
|
||||
private AnnouncementsPatch() {
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private static boolean isLatestAlready() throws IOException {
|
||||
HttpURLConnection connection =
|
||||
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
|
||||
@@ -70,7 +66,6 @@ public final class AnnouncementsPatch {
|
||||
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void showAnnouncement(final Activity context) {
|
||||
if (!Settings.ANNOUNCEMENTS.get()) return;
|
||||
|
||||
|
||||
@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final class ButtonsFilter extends Filter {
|
||||
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
|
||||
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
|
||||
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
|
||||
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
|
||||
|
||||
private final StringFilterGroup likeSubscribeGlow;
|
||||
private final StringFilterGroup actionBarGroup;
|
||||
private final StringFilterGroup bufferFilterPathGroup;
|
||||
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
|
||||
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
|
||||
addIdentifierCallbacks(actionBarGroup);
|
||||
|
||||
|
||||
likeSubscribeGlow = new StringFilterGroup(
|
||||
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
||||
"animated_button_border.eml"
|
||||
);
|
||||
|
||||
bufferFilterPathGroup = new StringFilterGroup(
|
||||
null,
|
||||
"|ContainerType|button.eml|"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
likeSubscribeGlow,
|
||||
bufferFilterPathGroup,
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_LIKE_DISLIKE_BUTTON,
|
||||
"|segmented_like_dislike_button"
|
||||
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"|clip_button.eml|"
|
||||
),
|
||||
bufferFilterPathGroup
|
||||
)
|
||||
);
|
||||
|
||||
bufferButtonsGroupList.addAll(
|
||||
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (matchedGroup == likeSubscribeGlow) {
|
||||
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
||||
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the current matched group is the action bar group,
|
||||
// in case every filter group is enabled, hide the action bar.
|
||||
if (matchedGroup == actionBarGroup) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
||||
return filterGroups.iterator();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void forEach(@NonNull Consumer<? super T> action) {
|
||||
filterGroups.forEach(action);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@NonNull
|
||||
@Override
|
||||
public Spliterator<T> spliterator() {
|
||||
|
||||
@@ -4,11 +4,8 @@ import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
import static java.lang.Character.UnicodeBlock.*;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -44,7 +41,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
||||
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
final class KeywordContentFilter extends Filter {
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.patches.components;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class LayoutComponentsFilter extends Filter {
|
||||
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
|
||||
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
|
||||
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
|
||||
|
||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
|
||||
"V.ED", // Playlist browse id.
|
||||
"java.lang.ref.WeakReference"
|
||||
@@ -38,16 +32,15 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup inFeedSurvey;
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup singleItemInformationPanel;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
private final ByteArrayFilterGroup searchResultRecommendations;
|
||||
private final StringFilterGroup searchResultVideo;
|
||||
private final StringFilterGroup compactChannelBarInner;
|
||||
private final StringFilterGroup compactChannelBarInnerButton;
|
||||
private final ByteArrayFilterGroup joinMembershipButton;
|
||||
private final StringFilterGroup likeSubscribeGlow;
|
||||
private final StringFilterGroup horizontalShelves;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public LayoutComponentsFilter() {
|
||||
exceptions.addPatterns(
|
||||
"home_video_with_context",
|
||||
@@ -123,8 +116,12 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
final var infoPanel = new StringFilterGroup(
|
||||
Settings.HIDE_HIDE_INFO_PANELS,
|
||||
"publisher_transparency_panel",
|
||||
Settings.HIDE_INFO_PANELS,
|
||||
"publisher_transparency_panel"
|
||||
);
|
||||
|
||||
singleItemInformationPanel = new StringFilterGroup(
|
||||
Settings.HIDE_INFO_PANELS,
|
||||
"single_item_information_panel"
|
||||
);
|
||||
|
||||
@@ -217,10 +214,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"sponsorships"
|
||||
);
|
||||
|
||||
likeSubscribeGlow = new StringFilterGroup(
|
||||
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
||||
"animated_button_border.eml"
|
||||
);
|
||||
|
||||
final var channelWatermark = new StringFilterGroup(
|
||||
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
|
||||
@@ -254,7 +247,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
expandableMetadata,
|
||||
inFeedSurvey,
|
||||
notifyMe,
|
||||
likeSubscribeGlow,
|
||||
compactChannelBar,
|
||||
communityPosts,
|
||||
paidPromotion,
|
||||
@@ -269,6 +261,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
compactChannelBarInner,
|
||||
medicalPanel,
|
||||
infoPanel,
|
||||
singleItemInformationPanel,
|
||||
emergencyBox,
|
||||
subscribersCommunityGuidelines,
|
||||
channelGuidelines,
|
||||
@@ -285,6 +278,19 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
// This identifier is used not only in players but also in search results:
|
||||
// https://github.com/ReVanced/revanced-patches/issues/3245
|
||||
// Until 2024, medical information panels such as Covid 19 also used this identifier and were shown in the search results.
|
||||
// From 2025, the medical information panel is no longer shown in the search results.
|
||||
// Therefore, this identifier does not filter when the search bar is activated.
|
||||
if (matchedGroup == singleItemInformationPanel) {
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen() || !NavigationBar.isSearchBarActive()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == searchResultVideo) {
|
||||
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
@@ -292,15 +298,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == likeSubscribeGlow) {
|
||||
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
||||
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
@@ -16,7 +13,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
private final ByteArrayFilterGroup exception;
|
||||
private final StringFilterGroup videoQualityMenuFooter;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public PlayerFlyoutMenuItemsFilter() {
|
||||
exception = new ByteArrayFilterGroup(
|
||||
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled
|
||||
|
||||
@@ -234,6 +234,12 @@ public class ReturnYouTubeDislike {
|
||||
// example video: https://www.youtube.com/watch?v=UnrU5vxCHxw
|
||||
// RYD data: https://returnyoutubedislikeapi.com/votes?videoId=UnrU5vxCHxw
|
||||
//
|
||||
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
|
||||
// Change the "Likes" string to show that likes and dislikes are hidden.
|
||||
String hiddenMessageString = str("revanced_ryd_video_likes_hidden_by_video_owner");
|
||||
return newSpanUsingStylingOfAnotherSpan(oldSpannable, hiddenMessageString);
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Using estimated likes");
|
||||
oldLikes = formatDislikeCount(voteData.getLikeCount());
|
||||
}
|
||||
@@ -346,56 +352,49 @@ public class ReturnYouTubeDislike {
|
||||
}
|
||||
|
||||
private static String formatDislikeCount(long dislikeCount) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
||||
if (dislikeCountFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
|
||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
||||
if (dislikeCountFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
|
||||
|
||||
// YouTube disregards locale specific number characters
|
||||
// and instead shows english number characters everywhere.
|
||||
// To use the same behavior, override the digit characters to use English
|
||||
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
// YouTube disregards locale specific number characters
|
||||
// and instead shows english number characters everywhere.
|
||||
// To use the same behavior, override the digit characters to use English
|
||||
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
return dislikeCountFormatter.format(dislikeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
|
||||
return String.valueOf(dislikeCount);
|
||||
return dislikeCountFormatter.format(dislikeCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatDislikePercentage(float dislikePercentage) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
||||
if (dislikePercentageFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
||||
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
|
||||
if (dislikePercentageFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
||||
|
||||
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
&& dislikePercentageFormatter instanceof DecimalFormat) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
&& dislikePercentageFormatter instanceof DecimalFormat) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
if (dislikePercentage >= 0.01) { // at least 1%
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
|
||||
} else {
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
|
||||
}
|
||||
return dislikePercentageFormatter.format(dislikePercentage);
|
||||
}
|
||||
}
|
||||
|
||||
// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
|
||||
return String.valueOf((int) (dislikePercentage * 100));
|
||||
if (dislikePercentage >= 0.01) { // at least 1%
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
|
||||
} else {
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
|
||||
}
|
||||
|
||||
return dislikePercentageFormatter.format(dislikePercentage);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -403,15 +402,13 @@ public class ReturnYouTubeDislike {
|
||||
Objects.requireNonNull(videoId);
|
||||
synchronized (fetchCache) {
|
||||
// Remove any expired entries.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
final long now = System.currentTimeMillis();
|
||||
fetchCache.values().removeIf(value -> {
|
||||
final boolean expired = value.isExpired(now);
|
||||
if (expired)
|
||||
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
|
||||
return expired;
|
||||
});
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
fetchCache.values().removeIf(value -> {
|
||||
final boolean expired = value.isExpired(now);
|
||||
if (expired)
|
||||
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
|
||||
return expired;
|
||||
});
|
||||
|
||||
ReturnYouTubeDislike fetch = fetchCache.get(videoId);
|
||||
if (fetch == null) {
|
||||
@@ -551,6 +548,15 @@ public class ReturnYouTubeDislike {
|
||||
}
|
||||
|
||||
if (spanIsForLikes) {
|
||||
if (!Utils.containsNumber(original)) {
|
||||
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
|
||||
Logger.printDebug(() -> "Likes are hidden");
|
||||
return original;
|
||||
} else {
|
||||
Logger.printDebug(() -> "Using estimated likes");
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolling Shorts does not cause the Spans to be reloaded,
|
||||
// so there is no need to cache the likes for this situations.
|
||||
Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId);
|
||||
|
||||
@@ -5,15 +5,12 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@@ -79,7 +76,6 @@ public class LicenseActivityHook {
|
||||
* <p>
|
||||
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static void initialize(Activity licenseActivity) {
|
||||
try {
|
||||
ThemeHelper.setActivityTheme(licenseActivity);
|
||||
@@ -119,7 +115,6 @@ public class LicenseActivityHook {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressLint("UseCompatLoadingForDrawables")
|
||||
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
|
||||
// Replace dummy placeholder toolbar.
|
||||
|
||||
@@ -124,7 +124,6 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
|
||||
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
||||
@@ -137,7 +136,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
|
||||
public static final BooleanSetting HIDE_HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
||||
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
||||
@@ -185,6 +184,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
||||
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||
// Action buttons
|
||||
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
||||
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
||||
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
||||
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
||||
@@ -306,21 +306,21 @@ public class Settings extends BaseSettings {
|
||||
|
||||
// Swipe controls
|
||||
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
||||
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE);
|
||||
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE);
|
||||
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE, true);
|
||||
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE, true);
|
||||
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 50, true,
|
||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
||||
|
||||
// Debugging
|
||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||
@@ -333,6 +333,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting RYD_SHORTS = new BooleanSetting("ryd_shorts", TRUE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_DISLIKE_PERCENTAGE = new BooleanSetting("ryd_dislike_percentage", FALSE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_COMPACT_LAYOUT = new BooleanSetting("ryd_compact_layout", FALSE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_ESTIMATED_LIKE = new BooleanSetting("ryd_estimated_like", TRUE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("ryd_toast_on_connection_error", TRUE, parent(RYD_ENABLED));
|
||||
|
||||
// SponsorBlock
|
||||
@@ -344,13 +345,14 @@ public class Settings extends BaseSettings {
|
||||
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_SQUARE_LAYOUT = new BooleanSetting("sb_square_layout", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_COMPACT_SKIP_BUTTON = new BooleanSetting("sb_compact_skip_button", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_AUTO_HIDE_SKIP_BUTTON = new BooleanSetting("sb_auto_hide_skip_button", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TOAST_ON_SKIP = new BooleanSetting("sb_toast_on_skip", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("sb_toast_on_connection_error", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED));
|
||||
public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", FALSE, parent(SB_ENABLED));
|
||||
public static final StringSetting SB_API_URL = new StringSetting("sb_api_url", "https://sponsor.ajay.app");
|
||||
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
|
||||
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
|
||||
|
||||
@@ -3,18 +3,14 @@ package app.revanced.extension.youtube.settings.preference;
|
||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.Preference;
|
||||
import android.text.Html;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Allows using basic html for the summary text.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "deprecation"})
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class HtmlPreference extends Preference {
|
||||
{
|
||||
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));
|
||||
|
||||
@@ -17,8 +17,6 @@ import android.view.WindowInsets;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -98,7 +96,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
listPreference.setEntryValues(sortedEntryValues);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
@@ -153,13 +150,10 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
toolbar.setTitle(childScreen.getTitle());
|
||||
toolbar.setNavigationIcon(getBackButtonDrawable());
|
||||
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
final int margin = (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
||||
);
|
||||
toolbar.setTitleMargin(margin, 0, margin, 0);
|
||||
}
|
||||
final int margin = (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
||||
);
|
||||
toolbar.setTitleMargin(margin, 0, margin, 0);
|
||||
|
||||
TextView toolbarTextView = Utils.getChildView(toolbar,
|
||||
true, TextView.class::isInstance);
|
||||
|
||||
@@ -39,6 +39,11 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
*/
|
||||
private SwitchPreference compactLayoutPreference;
|
||||
|
||||
/**
|
||||
* If hidden likes are replaced with an estimated value.
|
||||
*/
|
||||
private SwitchPreference estimatedLikesPreference;
|
||||
|
||||
/**
|
||||
* If segmented like/dislike button uses smaller compact layout.
|
||||
*/
|
||||
@@ -48,6 +53,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable());
|
||||
percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable());
|
||||
compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable());
|
||||
estimatedLikesPreference.setEnabled(Settings.RYD_ESTIMATED_LIKE.isAvailable());
|
||||
toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable());
|
||||
}
|
||||
|
||||
@@ -117,6 +123,19 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
});
|
||||
preferenceScreen.addPreference(compactLayoutPreference);
|
||||
|
||||
estimatedLikesPreference = new SwitchPreference(context);
|
||||
estimatedLikesPreference.setChecked(Settings.RYD_ESTIMATED_LIKE.get());
|
||||
estimatedLikesPreference.setTitle(str("revanced_ryd_estimated_like_title"));
|
||||
estimatedLikesPreference.setSummaryOn(str("revanced_ryd_estimated_like_summary_on"));
|
||||
estimatedLikesPreference.setSummaryOff(str("revanced_ryd_estimated_like_summary_off"));
|
||||
estimatedLikesPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
Settings.RYD_ESTIMATED_LIKE.save((Boolean) newValue);
|
||||
ReturnYouTubeDislike.clearAllUICaches();
|
||||
updateUIState();
|
||||
return true;
|
||||
});
|
||||
preferenceScreen.addPreference(estimatedLikesPreference);
|
||||
|
||||
toastOnRYDNotAvailable = new SwitchPreference(context);
|
||||
toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get());
|
||||
toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title"));
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.*;
|
||||
import android.text.Html;
|
||||
@@ -37,8 +36,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
private SwitchPreference sbEnabled;
|
||||
private SwitchPreference addNewSegment;
|
||||
private SwitchPreference votingEnabled;
|
||||
private SwitchPreference compactSkipButton;
|
||||
private SwitchPreference autoHideSkipSegmentButton;
|
||||
private SwitchPreference compactSkipButton;
|
||||
private SwitchPreference squareLayout;
|
||||
private SwitchPreference showSkipToast;
|
||||
private SwitchPreference trackSkips;
|
||||
private SwitchPreference showTimeWithoutSegments;
|
||||
@@ -62,7 +62,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
} else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) {
|
||||
SponsorBlockViewController.hideNewSegmentLayout();
|
||||
}
|
||||
// Voting and add new segment buttons automatically shows/hide themselves.
|
||||
// Voting and add new segment buttons automatically show/hide themselves.
|
||||
|
||||
SponsorBlockViewController.updateLayout();
|
||||
|
||||
sbEnabled.setChecked(enabled);
|
||||
|
||||
@@ -72,11 +74,14 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get());
|
||||
votingEnabled.setEnabled(enabled);
|
||||
|
||||
autoHideSkipSegmentButton.setEnabled(enabled);
|
||||
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
|
||||
|
||||
compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get());
|
||||
compactSkipButton.setEnabled(enabled);
|
||||
|
||||
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
|
||||
autoHideSkipSegmentButton.setEnabled(enabled);
|
||||
squareLayout.setChecked(Settings.SB_SQUARE_LAYOUT.get());
|
||||
squareLayout.setEnabled(enabled);
|
||||
|
||||
showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get());
|
||||
showSkipToast.setEnabled(enabled);
|
||||
@@ -176,6 +181,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
autoHideSkipSegmentButton = new SwitchPreference(context);
|
||||
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button"));
|
||||
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on"));
|
||||
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off"));
|
||||
category.addPreference(autoHideSkipSegmentButton);
|
||||
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue);
|
||||
updateUI();
|
||||
return true;
|
||||
});
|
||||
|
||||
compactSkipButton = new SwitchPreference(context);
|
||||
compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button"));
|
||||
compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on"));
|
||||
@@ -187,13 +203,13 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
autoHideSkipSegmentButton = new SwitchPreference(context);
|
||||
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button"));
|
||||
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on"));
|
||||
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off"));
|
||||
category.addPreference(autoHideSkipSegmentButton);
|
||||
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue);
|
||||
squareLayout = new SwitchPreference(context);
|
||||
squareLayout.setTitle(str("revanced_sb_square_layout"));
|
||||
squareLayout.setSummaryOn(str("revanced_sb_square_layout_sum_on"));
|
||||
squareLayout.setSummaryOff(str("revanced_sb_square_layout_sum_off"));
|
||||
category.addPreference(squareLayout);
|
||||
squareLayout.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_SQUARE_LAYOUT.save((Boolean) newValue);
|
||||
updateUI();
|
||||
return true;
|
||||
});
|
||||
@@ -393,9 +409,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
|
||||
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
importExport.getEditText().setAutofillHints((String) null);
|
||||
}
|
||||
importExport.getEditText().setAutofillHints((String) null);
|
||||
importExport.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
|
||||
importExport.setOnPreferenceClickListener(preference1 -> {
|
||||
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
|
||||
|
||||
@@ -85,7 +85,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
String summary = str(key + "_summary");
|
||||
|
||||
// Android VR supports AV1 but all other clients do not.
|
||||
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
|
||||
if (clientType != ClientType.ANDROID_VR_AUTH && clientType != ClientType.ANDROID_VR_NO_AUTH) {
|
||||
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.shared;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
@@ -257,7 +255,6 @@ public final class NavigationBar {
|
||||
* Injection point.
|
||||
* Fixes missing drawable.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
|
||||
if (fillBellCairoBlack != 0) {
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package app.revanced.extension.youtube.sponsorblock.ui;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
|
||||
|
||||
// Edit: This should be a subclass of PlayerControlButton
|
||||
public class CreateSegmentButtonController {
|
||||
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
|
||||
private static boolean isShowing;
|
||||
public class CreateSegmentButtonController extends PlayerControlTopButton {
|
||||
@Nullable
|
||||
private static CreateSegmentButtonController instance;
|
||||
|
||||
public static void hideControls() {
|
||||
if (instance != null) instance.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
@@ -27,10 +29,7 @@ public class CreateSegmentButtonController {
|
||||
Logger.printDebug(() -> "initializing new segment button");
|
||||
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
|
||||
youtubeControlsLayout, "revanced_sb_create_segment_button"));
|
||||
imageView.setVisibility(View.GONE);
|
||||
imageView.setOnClickListener(v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
|
||||
|
||||
buttonReference = new WeakReference<>(imageView);
|
||||
instance = new CreateSegmentButtonController(imageView);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initialize failure", ex);
|
||||
}
|
||||
@@ -40,72 +39,22 @@ public class CreateSegmentButtonController {
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
if (visible) {
|
||||
// Fix button flickering, by pushing this call to the back of
|
||||
// the main thread and letting other layout code run first.
|
||||
Utils.runOnMainThread(() -> setVisibility(true, false));
|
||||
} else {
|
||||
setVisibility(false, false);
|
||||
}
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibility(boolean visible, boolean animated) {
|
||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
||||
if (visible && !animated) return;
|
||||
|
||||
setVisibility(visible, animated);
|
||||
if (instance != null) instance.setVisibility(visible, animated);
|
||||
}
|
||||
|
||||
private static void setVisibility(boolean visible, boolean animated) {
|
||||
try {
|
||||
if (isShowing == visible) return;
|
||||
isShowing = visible;
|
||||
|
||||
ImageView iView = buttonReference.get();
|
||||
if (iView == null) return;
|
||||
|
||||
if (visible) {
|
||||
iView.clearAnimation();
|
||||
if (!shouldBeShown()) {
|
||||
return;
|
||||
}
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
||||
}
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iView.getVisibility() == View.VISIBLE) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
||||
}
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "changeVisibility failure", ex);
|
||||
}
|
||||
private CreateSegmentButtonController(ImageView imageView) {
|
||||
super(imageView, v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
|
||||
}
|
||||
|
||||
private static boolean shouldBeShown() {
|
||||
protected boolean shouldBeShown() {
|
||||
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
|
||||
&& !VideoInformation.isAtEndOfVideo();
|
||||
}
|
||||
|
||||
public static void hide() {
|
||||
if (!isShowing) {
|
||||
return;
|
||||
}
|
||||
Utils.verifyOnMainThread();
|
||||
View v = buttonReference.get();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
isShowing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ package app.revanced.extension.youtube.sponsorblock.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
@@ -14,15 +15,15 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.getResourceColor;
|
||||
import static app.revanced.extension.shared.Utils.getResourceDimensionPixelSize;
|
||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
|
||||
public final class NewSegmentLayout extends FrameLayout {
|
||||
private static final ColorStateList rippleColorStateList = new ColorStateList(
|
||||
new int[][]{new int[]{android.R.attr.state_enabled}},
|
||||
new int[]{0x33ffffff} // sets the ripple color to white
|
||||
new int[]{0x33ffffff} // Ripple effect color (semi-transparent white)
|
||||
);
|
||||
private final int rippleEffectId;
|
||||
|
||||
final int defaultBottomMargin;
|
||||
final int ctaBottomMargin;
|
||||
@@ -47,10 +48,6 @@ public final class NewSegmentLayout extends FrameLayout {
|
||||
getResourceIdentifier(context, "revanced_sb_new_segment", "layout"), this, true
|
||||
);
|
||||
|
||||
TypedValue rippleEffect = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, rippleEffect, true);
|
||||
rippleEffectId = rippleEffect.resourceId;
|
||||
|
||||
initializeButton(
|
||||
context,
|
||||
"revanced_sb_new_segment_rewind",
|
||||
@@ -120,6 +117,28 @@ public final class NewSegmentLayout extends FrameLayout {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the layout of this UI control.
|
||||
*/
|
||||
public void updateLayout() {
|
||||
final boolean squareLayout = Settings.SB_SQUARE_LAYOUT.get();
|
||||
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams();
|
||||
final int margin = squareLayout
|
||||
? 0
|
||||
: SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
|
||||
params.setMarginStart(margin);
|
||||
setLayoutParams(params);
|
||||
|
||||
GradientDrawable backgroundDrawable = new GradientDrawable();
|
||||
backgroundDrawable.setColor(getResourceColor("skip_ad_button_background_color"));
|
||||
final float cornerRadius = squareLayout
|
||||
? 0
|
||||
: 16 * getResources().getDisplayMetrics().density;
|
||||
backgroundDrawable.setCornerRadius(cornerRadius);
|
||||
setBackground(backgroundDrawable);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ButtonOnClickHandlerFunction {
|
||||
void apply();
|
||||
|
||||
@@ -8,6 +8,7 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -19,11 +20,19 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||
|
||||
public class SkipSponsorButton extends FrameLayout {
|
||||
private static final boolean highContrast = true;
|
||||
/**
|
||||
* Adds a high contrast border around the skip button.
|
||||
*
|
||||
* This feature is not currently used.
|
||||
* If this is added, it needs an additional button width change because
|
||||
* as-is the skip button text is clipped when this is on.
|
||||
*/
|
||||
private static final boolean highContrast = false;
|
||||
private final LinearLayout skipSponsorBtnContainer;
|
||||
private final TextView skipSponsorTextView;
|
||||
private final Paint background;
|
||||
@@ -49,18 +58,23 @@ public class SkipSponsorButton extends FrameLayout {
|
||||
|
||||
LayoutInflater.from(context).inflate(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button
|
||||
setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height
|
||||
skipSponsorBtnContainer = Objects.requireNonNull((LinearLayout) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
|
||||
skipSponsorBtnContainer = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
|
||||
|
||||
background = new Paint();
|
||||
background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
|
||||
background.setStyle(Paint.Style.FILL);
|
||||
|
||||
border = new Paint();
|
||||
border.setColor(getResourceColor("skip_ad_button_border_color")); // color:skip_ad_button_border_color);
|
||||
border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width);
|
||||
border.setStyle(Paint.Style.STROKE);
|
||||
skipSponsorTextView = Objects.requireNonNull((TextView) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
|
||||
|
||||
skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
|
||||
defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin
|
||||
ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin
|
||||
|
||||
updateLayout();
|
||||
|
||||
skipSponsorBtnContainer.setOnClickListener(v -> {
|
||||
// The view controller handles hiding this button, but hide it here as well just in case something goofs.
|
||||
setVisibility(View.GONE);
|
||||
@@ -72,30 +86,56 @@ public class SkipSponsorButton extends FrameLayout {
|
||||
protected final void dispatchDraw(Canvas canvas) {
|
||||
final int left = skipSponsorBtnContainer.getLeft();
|
||||
final int top = skipSponsorBtnContainer.getTop();
|
||||
final int leftPlusWidth = (left + skipSponsorBtnContainer.getWidth());
|
||||
final int topPlusHeight = (top + skipSponsorBtnContainer.getHeight());
|
||||
canvas.drawRect(left, top, leftPlusWidth, topPlusHeight, background);
|
||||
if (!highContrast) {
|
||||
canvas.drawLines(new float[]{
|
||||
leftPlusWidth, top, left, top,
|
||||
left, top, left, topPlusHeight,
|
||||
left, topPlusHeight, leftPlusWidth, topPlusHeight},
|
||||
border);
|
||||
final int right = left + skipSponsorBtnContainer.getWidth();
|
||||
final int bottom = top + skipSponsorBtnContainer.getHeight();
|
||||
|
||||
// Determine corner radius for rounded button
|
||||
float cornerRadius = skipSponsorBtnContainer.getHeight() / 2f;
|
||||
|
||||
if (Settings.SB_SQUARE_LAYOUT.get()) {
|
||||
// Square button.
|
||||
canvas.drawRect(left, top, right, bottom, background);
|
||||
if (highContrast) {
|
||||
canvas.drawLines(new float[]{
|
||||
right, top, left, top,
|
||||
left, top, left, bottom,
|
||||
left, bottom, right, bottom},
|
||||
border); // Draw square border.
|
||||
}
|
||||
} else {
|
||||
// Rounded button.
|
||||
RectF rect = new RectF(left, top, right, bottom);
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, background); // Draw rounded background.
|
||||
if (highContrast) {
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, border); // Draw rounded border.
|
||||
}
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, if this button state was changed
|
||||
* Update the layout of this button.
|
||||
*/
|
||||
public boolean updateSkipButtonText(@NonNull SponsorSegment segment) {
|
||||
public void updateLayout() {
|
||||
if (Settings.SB_SQUARE_LAYOUT.get()) {
|
||||
// No padding for square corners.
|
||||
setPadding(0, 0, 0, 0);
|
||||
} else {
|
||||
// Apply padding for rounded corners.
|
||||
final int padding = SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
|
||||
setPadding(padding, 0, padding, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSkipButtonText(@NonNull SponsorSegment segment) {
|
||||
this.segment = segment;
|
||||
CharSequence newText = segment.getSkipButtonText();
|
||||
|
||||
//noinspection StringEqualsCharSequence
|
||||
if (newText.equals(skipSponsorTextView.getText())) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
skipSponsorTextView.setText(newText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
|
||||
import kotlin.Unit;
|
||||
|
||||
public class SponsorBlockViewController {
|
||||
public static final int ROUNDED_LAYOUT_MARGIN = 12;
|
||||
|
||||
private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null);
|
||||
private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null);
|
||||
private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null);
|
||||
@@ -36,7 +40,7 @@ public class SponsorBlockViewController {
|
||||
static {
|
||||
PlayerType.getOnChange().addObserver((PlayerType type) -> {
|
||||
playerTypeChanged(type);
|
||||
return null;
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,12 +84,16 @@ public class SponsorBlockViewController {
|
||||
});
|
||||
youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup);
|
||||
|
||||
skipHighlightButtonRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
|
||||
skipSponsorButtonRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
|
||||
newSegmentLayoutRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id"))));
|
||||
skipHighlightButtonRef = new WeakReference<>(Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
|
||||
|
||||
skipSponsorButtonRef = new WeakReference<>(Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
|
||||
|
||||
NewSegmentLayout newSegmentLayout = Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id")));
|
||||
newSegmentLayoutRef = new WeakReference<>(newSegmentLayout);
|
||||
newSegmentLayout.updateLayout();
|
||||
|
||||
newSegmentLayoutVisible = false;
|
||||
skipHighlight = null;
|
||||
@@ -101,6 +109,23 @@ public class SponsorBlockViewController {
|
||||
hideNewSegmentLayout();
|
||||
}
|
||||
|
||||
public static void updateLayout() {
|
||||
SkipSponsorButton button = skipSponsorButtonRef.get();
|
||||
if (button != null) {
|
||||
button.updateLayout();
|
||||
}
|
||||
|
||||
button = skipHighlightButtonRef.get();
|
||||
if (button != null) {
|
||||
button.updateLayout();
|
||||
}
|
||||
|
||||
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
|
||||
if (newSegmentLayout != null) {
|
||||
newSegmentLayout.updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public static void showSkipHighlightButton(@NonNull SponsorSegment segment) {
|
||||
skipHighlight = Objects.requireNonNull(segment);
|
||||
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
|
||||
@@ -214,8 +239,8 @@ public class SponsorBlockViewController {
|
||||
// but if buttons are showing when the end of the video is reached then they need
|
||||
// to be forcefully hidden
|
||||
if (!Settings.AUTO_REPEAT.get()) {
|
||||
CreateSegmentButtonController.hide();
|
||||
VotingButtonController.hide();
|
||||
CreateSegmentButtonController.hideControls();
|
||||
VotingButtonController.hideControls();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "endOfVideoReached failure", ex);
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
package app.revanced.extension.youtube.sponsorblock.ui;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
||||
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
|
||||
|
||||
// Edit: This should be a subclass of PlayerControlButton
|
||||
public class VotingButtonController {
|
||||
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
|
||||
private static boolean isShowing;
|
||||
public class VotingButtonController extends PlayerControlTopButton {
|
||||
@Nullable
|
||||
private static VotingButtonController instance;
|
||||
|
||||
public static void hideControls() {
|
||||
if (instance != null) instance.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
@@ -29,10 +31,7 @@ public class VotingButtonController {
|
||||
Logger.printDebug(() -> "initializing voting button");
|
||||
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
|
||||
youtubeControlsLayout, "revanced_sb_voting_button"));
|
||||
imageView.setVisibility(View.GONE);
|
||||
imageView.setOnClickListener(v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
|
||||
|
||||
buttonReference = new WeakReference<>(imageView);
|
||||
instance = new VotingButtonController(imageView);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initialize failure", ex);
|
||||
}
|
||||
@@ -42,75 +41,22 @@ public class VotingButtonController {
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
if (visible) {
|
||||
// Fix button flickering, by pushing this call to the back of
|
||||
// the main thread and letting other layout code run first.
|
||||
Utils.runOnMainThread(() -> setVisibility(true, false));
|
||||
} else {
|
||||
setVisibility(false, false);
|
||||
}
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibility(boolean visible, boolean animated) {
|
||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
||||
if (visible && !animated) return;
|
||||
|
||||
setVisibility(visible, animated);
|
||||
if (instance != null) instance.setVisibility(visible, animated);
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
private static void setVisibility(boolean visible, boolean animated) {
|
||||
try {
|
||||
if (isShowing == visible) return;
|
||||
isShowing = visible;
|
||||
|
||||
ImageView iView = buttonReference.get();
|
||||
if (iView == null) return;
|
||||
|
||||
if (visible) {
|
||||
iView.clearAnimation();
|
||||
if (!shouldBeShown()) {
|
||||
return;
|
||||
}
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
||||
}
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iView.getVisibility() == View.VISIBLE) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
||||
}
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "changeVisibility failure", ex);
|
||||
}
|
||||
private VotingButtonController(ImageView imageView) {
|
||||
super(imageView, v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
|
||||
}
|
||||
|
||||
private static boolean shouldBeShown() {
|
||||
protected boolean shouldBeShown() {
|
||||
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
|
||||
&& SegmentPlaybackController.videoHasSegments() && !VideoInformation.isAtEndOfVideo();
|
||||
}
|
||||
|
||||
public static void hide() {
|
||||
if (!isShowing) {
|
||||
return;
|
||||
}
|
||||
Utils.verifyOnMainThread();
|
||||
View v = buttonReference.get();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
isShowing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,17 @@ class SwipeControlsConfigurationProvider(
|
||||
* should swipe controls be enabled? (global setting)
|
||||
*/
|
||||
val enableSwipeControls: Boolean
|
||||
get() = isFullscreenVideo && (enableVolumeControls || enableBrightnessControl)
|
||||
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
*/
|
||||
val enableVolumeControls: Boolean
|
||||
get() = Settings.SWIPE_VOLUME.get()
|
||||
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
*/
|
||||
val enableBrightnessControl: Boolean
|
||||
get() = Settings.SWIPE_BRIGHTNESS.get()
|
||||
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
||||
|
||||
/**
|
||||
* is the video player currently in fullscreen mode?
|
||||
@@ -46,7 +44,7 @@ class SwipeControlsConfigurationProvider(
|
||||
* should volume key controls be overwritten? (global setting)
|
||||
*/
|
||||
val overwriteVolumeKeyControls: Boolean
|
||||
get() = isFullscreenVideo && enableVolumeControls
|
||||
get() = enableVolumeControls && isFullscreenVideo
|
||||
//endregion
|
||||
|
||||
//region gesture adjustments
|
||||
@@ -65,7 +63,6 @@ class SwipeControlsConfigurationProvider(
|
||||
//endregion
|
||||
|
||||
//region overlay adjustments
|
||||
|
||||
/**
|
||||
* should the overlay enable haptic feedback?
|
||||
*/
|
||||
@@ -79,15 +76,10 @@ class SwipeControlsConfigurationProvider(
|
||||
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
||||
|
||||
/**
|
||||
* text size for the overlay, in sp
|
||||
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
|
||||
* If the opacity value is out of range, it resets to the default and displays a warning message.
|
||||
*/
|
||||
val overlayTextSize: Int
|
||||
get() = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
|
||||
|
||||
/**
|
||||
* get the background color for text on the overlay, as a color int
|
||||
*/
|
||||
val overlayTextBackgroundColor: Int
|
||||
val overlayBackgroundOpacity: Int
|
||||
get() {
|
||||
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
||||
|
||||
@@ -102,11 +94,34 @@ class SwipeControlsConfigurationProvider(
|
||||
}
|
||||
|
||||
/**
|
||||
* get the foreground color for text on the overlay, as a color int
|
||||
* The color of the progress overlay.
|
||||
*/
|
||||
val overlayForegroundColor: Int
|
||||
val overlayProgressColor: Int
|
||||
get() = 0xBFFFFFFF.toInt()
|
||||
|
||||
/**
|
||||
* The color used for the background of the progress overlay fill.
|
||||
*/
|
||||
val overlayFillBackgroundPaint: Int
|
||||
get() = 0x80D3D3D3.toInt()
|
||||
|
||||
/**
|
||||
* The color used for the text and icons in the overlay.
|
||||
*/
|
||||
val overlayTextColor: Int
|
||||
get() = Color.WHITE
|
||||
|
||||
/**
|
||||
* A flag that determines if the overlay should only show the icon.
|
||||
*/
|
||||
val overlayShowOverlayMinimalStyle: Boolean
|
||||
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
|
||||
|
||||
/**
|
||||
* A flag that determines if the progress bar should be circular.
|
||||
*/
|
||||
val isCircularProgressBar: Boolean
|
||||
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
|
||||
//endregion
|
||||
|
||||
//region behaviour
|
||||
|
||||
@@ -82,11 +82,15 @@ abstract class BaseGestureController(
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
from: MotionEvent,
|
||||
from: MotionEvent?,
|
||||
to: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float,
|
||||
): Boolean {
|
||||
if (from == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
// submit to swipe detector
|
||||
submitForSwipe(from, to, distanceX, distanceY)
|
||||
|
||||
|
||||
@@ -1,138 +1,145 @@
|
||||
package app.revanced.extension.youtube.swipecontrols.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.TypedValue
|
||||
import android.util.AttributeSet
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import app.revanced.extension.shared.StringRef.str
|
||||
import app.revanced.extension.shared.Utils
|
||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
||||
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
||||
import app.revanced.extension.youtube.swipecontrols.misc.applyDimension
|
||||
import kotlin.math.min
|
||||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* main overlay layout for volume and brightness swipe controls
|
||||
*
|
||||
* @param context context to create in
|
||||
* Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars.
|
||||
*/
|
||||
class SwipeControlsOverlayLayout(
|
||||
context: Context,
|
||||
private val config: SwipeControlsConfigurationProvider,
|
||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||
/**
|
||||
* DO NOT use this, for tools only
|
||||
*/
|
||||
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
||||
|
||||
private val feedbackTextView: TextView
|
||||
private val autoBrightnessIcon: Drawable
|
||||
private val manualBrightnessIcon: Drawable
|
||||
private val mutedVolumeIcon: Drawable
|
||||
private val normalVolumeIcon: Drawable
|
||||
// Drawable icons for brightness and volume
|
||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||
private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
|
||||
private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
|
||||
private val highBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_high")
|
||||
private val fullBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_full")
|
||||
private val mutedVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_mute")
|
||||
private val lowVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_low")
|
||||
private val normalVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_normal")
|
||||
private val fullVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_high")
|
||||
|
||||
private fun getDrawable(name: String, width: Int, height: Int): Drawable {
|
||||
return resources.getDrawable(
|
||||
// Function to retrieve drawable resources by name
|
||||
private fun getDrawable(name: String): Drawable {
|
||||
val drawable = resources.getDrawable(
|
||||
Utils.getResourceIdentifier(context, name, "drawable"),
|
||||
context.theme,
|
||||
).apply {
|
||||
setTint(config.overlayForegroundColor)
|
||||
setBounds(
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
}
|
||||
)
|
||||
drawable.setTint(config.overlayTextColor)
|
||||
return drawable
|
||||
}
|
||||
|
||||
// Initialize progress bars
|
||||
private val circularProgressView: CircularProgressView
|
||||
private val horizontalProgressView: HorizontalProgressView
|
||||
|
||||
init {
|
||||
// init views
|
||||
val feedbackTextViewPadding = 2.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
||||
val compoundIconPadding = 4.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
||||
feedbackTextView = TextView(context).apply {
|
||||
layoutParams = LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
).apply {
|
||||
// Initialize circular progress bar
|
||||
circularProgressView = CircularProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayShowOverlayMinimalStyle,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor
|
||||
).apply {
|
||||
layoutParams = LayoutParams(300, 300).apply {
|
||||
addRule(CENTER_IN_PARENT, TRUE)
|
||||
setPadding(
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
)
|
||||
}
|
||||
background = GradientDrawable().apply {
|
||||
cornerRadius = 8f
|
||||
setColor(config.overlayTextBackgroundColor)
|
||||
}
|
||||
setTextColor(config.overlayForegroundColor)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, config.overlayTextSize.toFloat())
|
||||
compoundDrawablePadding = compoundIconPadding
|
||||
visibility = GONE
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(feedbackTextView)
|
||||
addView(circularProgressView)
|
||||
|
||||
// get icons scaled, assuming square icons
|
||||
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
|
||||
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
|
||||
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
|
||||
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
|
||||
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
|
||||
// Initialize rectangular progress bar
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
||||
horizontalProgressView = HorizontalProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayShowOverlayMinimalStyle,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor
|
||||
).apply {
|
||||
layoutParams = LayoutParams(layoutWidth, 100).apply {
|
||||
addRule(CENTER_HORIZONTAL)
|
||||
topMargin = 40 // Top margin
|
||||
}
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(horizontalProgressView)
|
||||
}
|
||||
|
||||
// Handler and callback for hiding progress bars
|
||||
private val feedbackHideHandler = Handler(Looper.getMainLooper())
|
||||
private val feedbackHideCallback = Runnable {
|
||||
feedbackTextView.visibility = GONE
|
||||
circularProgressView.visibility = GONE
|
||||
horizontalProgressView.visibility = GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* show the feedback view for a given time
|
||||
*
|
||||
* @param message the message to show
|
||||
* @param icon the icon to use
|
||||
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
|
||||
*/
|
||||
private fun showFeedbackView(message: String, icon: Drawable) {
|
||||
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
|
||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||
feedbackTextView.apply {
|
||||
text = message
|
||||
setCompoundDrawablesRelative(
|
||||
icon,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
|
||||
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||
viewToShow.apply {
|
||||
setProgress(progress, max, value, isBrightness)
|
||||
this.icon = icon
|
||||
visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
// Handle volume change
|
||||
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
|
||||
showFeedbackView(
|
||||
"$newVolume",
|
||||
if (newVolume > 0) normalVolumeIcon else mutedVolumeIcon,
|
||||
)
|
||||
val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
|
||||
val icon = when {
|
||||
newVolume == 0 -> mutedVolumeIcon
|
||||
volumePercentage < 33 -> lowVolumeIcon
|
||||
volumePercentage < 66 -> normalVolumeIcon
|
||||
else -> fullVolumeIcon
|
||||
}
|
||||
showFeedbackView("$newVolume", newVolume, maximumVolume, icon, isBrightness = false)
|
||||
}
|
||||
|
||||
// Handle brightness change
|
||||
override fun onBrightnessChanged(brightness: Double) {
|
||||
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
||||
showFeedbackView(
|
||||
str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text"),
|
||||
autoBrightnessIcon,
|
||||
)
|
||||
} else if (brightness >= 0) {
|
||||
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
|
||||
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||
} else {
|
||||
val brightnessValue = round(brightness).toInt()
|
||||
val icon = when {
|
||||
brightnessValue < 25 -> lowBrightnessIcon
|
||||
brightnessValue < 50 -> mediumBrightnessIcon
|
||||
brightnessValue < 75 -> highBrightnessIcon
|
||||
else -> fullBrightnessIcon
|
||||
}
|
||||
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
||||
}
|
||||
}
|
||||
|
||||
// Begin swipe session
|
||||
override fun onEnterSwipeSession() {
|
||||
if (config.shouldEnableHapticFeedback) {
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -143,3 +150,233 @@ class SwipeControlsOverlayLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for progress views to reduce code duplication.
|
||||
*/
|
||||
/**
|
||||
* Abstract base class for progress views to reduce code duplication.
|
||||
*/
|
||||
abstract class AbstractProgressView(
|
||||
context: Context,
|
||||
protected val overlayBackgroundOpacity: Int,
|
||||
protected val overlayShowOverlayMinimalStyle: Boolean,
|
||||
protected val overlayProgressColor: Int,
|
||||
protected val overlayFillBackgroundPaint: Int,
|
||||
protected val overlayTextColor: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
// Combined paint creation function for both fill and stroke styles
|
||||
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
this.style = style
|
||||
this.color = color
|
||||
this.strokeCap = strokeCap
|
||||
this.strokeWidth = strokeWidth
|
||||
}
|
||||
|
||||
// Initialize paints
|
||||
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
|
||||
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = overlayTextColor
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 30f // Can adjust based on need
|
||||
}
|
||||
|
||||
|
||||
protected var progress = 0
|
||||
protected var maxProgress = 100
|
||||
protected var displayText: String = "0"
|
||||
protected var isBrightness = true
|
||||
public var icon: Drawable? = null
|
||||
|
||||
init {
|
||||
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
|
||||
}
|
||||
|
||||
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
progress = value
|
||||
maxProgress = max
|
||||
displayText = text
|
||||
isBrightness = isBrightnessMode
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
// Base class implementation can be empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom view for rendering a circular progress indicator with text and icon.
|
||||
*/
|
||||
class CircularProgressView(
|
||||
context: Context,
|
||||
overlayBackgroundOpacity: Int,
|
||||
overlayShowOverlayMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
overlayTextColor: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AbstractProgressView(
|
||||
context,
|
||||
overlayBackgroundOpacity,
|
||||
overlayShowOverlayMinimalStyle,
|
||||
overlayProgressColor,
|
||||
overlayFillBackgroundPaint,
|
||||
overlayTextColor,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
private val rectF = RectF()
|
||||
|
||||
init {
|
||||
textPaint.textSize = 40f // Override default text size for horizontal view
|
||||
progressPaint.strokeWidth = 20f
|
||||
fillBackgroundPaint.strokeWidth = 20f
|
||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.STROKE
|
||||
fillBackgroundPaint.style = Paint.Style.STROKE
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
val size = min(width, height).toFloat()
|
||||
rectF.set(20f, 20f, size - 20f, size - 20f)
|
||||
|
||||
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
||||
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
||||
|
||||
// Select the paint for drawing based on whether it's brightness or volume.
|
||||
val sweepAngle = (progress.toFloat() / maxProgress) * 360
|
||||
canvas.drawArc(rectF, -90f, sweepAngle, false, progressPaint) // Draw the progress arc.
|
||||
|
||||
// Draw the icon in the center.
|
||||
icon?.let {
|
||||
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
|
||||
val iconX = (width - iconSize) / 2
|
||||
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
|
||||
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
// If not in icon-only mode, draw the text inside the ring.
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom view for rendering a rectangular progress bar with icons and text.
|
||||
*/
|
||||
class HorizontalProgressView(
|
||||
context: Context,
|
||||
overlayBackgroundOpacity: Int,
|
||||
overlayShowOverlayMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
overlayTextColor: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AbstractProgressView(
|
||||
context,
|
||||
overlayBackgroundOpacity,
|
||||
overlayShowOverlayMinimalStyle,
|
||||
overlayProgressColor,
|
||||
overlayFillBackgroundPaint,
|
||||
overlayTextColor,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
|
||||
private val iconSize = 60f
|
||||
private val padding = 40f
|
||||
|
||||
init {
|
||||
textPaint.textSize = 30f // Override default text size for horizontal view
|
||||
progressPaint.strokeWidth = 0f
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
fillBackgroundPaint.style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
val width = width.toFloat()
|
||||
val height = height.toFloat()
|
||||
|
||||
// Radius for rounded corners
|
||||
val cornerRadius = min(width, height) / 2
|
||||
|
||||
// Calculate the total width for the elements
|
||||
val minimalElementWidth = 5 * padding + iconSize
|
||||
|
||||
// Calculate the starting point (X) to center the elements
|
||||
val minimalStartX = (width - minimalElementWidth) / 2
|
||||
|
||||
// Draw the background
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
} else {
|
||||
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
}
|
||||
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
// Draw the fill background
|
||||
val startX = 2 * padding + iconSize
|
||||
val endX = width - 4 * padding
|
||||
val fillWidth = endX - startX
|
||||
|
||||
canvas.drawRoundRect(
|
||||
startX,
|
||||
height / 2 - 5f,
|
||||
endX,
|
||||
height / 2 + 5f,
|
||||
10f, 10f,
|
||||
fillBackgroundPaint
|
||||
)
|
||||
|
||||
// Draw the progress
|
||||
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
|
||||
canvas.drawRoundRect(
|
||||
startX,
|
||||
height / 2 - 5f,
|
||||
startX + progressWidth,
|
||||
height / 2 + 5f,
|
||||
10f, 10f,
|
||||
progressPaint
|
||||
)
|
||||
}
|
||||
|
||||
// Draw the icon
|
||||
icon?.let {
|
||||
val iconX = if (!overlayShowOverlayMinimalStyle) {
|
||||
padding
|
||||
} else {
|
||||
padding + minimalStartX
|
||||
}
|
||||
val iconY = height / 2 - iconSize / 2
|
||||
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
// Draw the text on the right
|
||||
val textX = if (!overlayShowOverlayMinimalStyle) {
|
||||
width - 2 * padding
|
||||
} else {
|
||||
minimalStartX + minimalElementWidth - 2 * padding
|
||||
}
|
||||
val textY = height / 2 + textPaint.textSize / 3
|
||||
|
||||
// Draw the text
|
||||
canvas.drawText(displayText, textX, textY, textPaint)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlButton extends PlayerControlButton {
|
||||
public class CopyVideoUrlButton extends PlayerControlBottomButton {
|
||||
@Nullable
|
||||
private static CopyVideoUrlButton instance;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlTimestampButton extends PlayerControlButton {
|
||||
public class CopyVideoUrlTimestampButton extends PlayerControlBottomButton {
|
||||
@Nullable
|
||||
private static CopyVideoUrlTimestampButton instance;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ExternalDownloadButton extends PlayerControlButton {
|
||||
public class ExternalDownloadButton extends PlayerControlBottomButton {
|
||||
@Nullable
|
||||
private static ExternalDownloadButton instance;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlaybackSpeedDialogButton extends PlayerControlButton {
|
||||
public class PlaybackSpeedDialogButton extends PlayerControlBottomButton {
|
||||
@Nullable
|
||||
private static PlaybackSpeedDialogButton instance;
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import static app.revanced.extension.youtube.videoplayer.PlayerControlTopButton.fadeOutDuration;
|
||||
|
||||
import android.transition.Fade;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||
|
||||
public abstract class PlayerControlBottomButton {
|
||||
private final WeakReference<ImageView> buttonRef;
|
||||
private final BooleanSetting setting;
|
||||
private boolean isVisible;
|
||||
|
||||
protected PlayerControlBottomButton(ViewGroup bottomControlsViewGroup, String imageViewButtonId,
|
||||
BooleanSetting booleanSetting, View.OnClickListener onClickListener,
|
||||
@Nullable View.OnLongClickListener longClickListener) {
|
||||
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId);
|
||||
|
||||
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
|
||||
Utils.getResourceIdentifier(imageViewButtonId, "id")
|
||||
));
|
||||
imageView.setVisibility(View.GONE);
|
||||
|
||||
imageView.setOnClickListener(onClickListener);
|
||||
if (longClickListener != null) {
|
||||
imageView.setOnLongClickListener(longClickListener);
|
||||
}
|
||||
|
||||
setting = booleanSetting;
|
||||
buttonRef = new WeakReference<>(imageView);
|
||||
}
|
||||
|
||||
protected void setVisibilityImmediate(boolean visible) {
|
||||
private_setVisibility(visible, false);
|
||||
}
|
||||
|
||||
protected void setVisibility(boolean visible, boolean animated) {
|
||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
||||
if (visible && !animated) return;
|
||||
|
||||
private_setVisibility(visible, animated);
|
||||
}
|
||||
|
||||
private void private_setVisibility(boolean visible, boolean animated) {
|
||||
try {
|
||||
// If the visibility state hasn't changed, return early.
|
||||
if (isVisible == visible) return;
|
||||
isVisible = visible;
|
||||
|
||||
ImageView iView = buttonRef.get();
|
||||
if (iView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewGroup parent = (ViewGroup) iView.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply transition if animation is enabled.
|
||||
if (animated) {
|
||||
Fade fade = visible
|
||||
? PlayerControlTopButton.fadeInTransition
|
||||
: PlayerControlTopButton.fadeOutTransition;
|
||||
TransitionManager.beginDelayedTransition(parent, fade);
|
||||
}
|
||||
|
||||
// If the view should be visible and the setting allows it.
|
||||
if (visible && setting.get()) {
|
||||
// Set the view to VISIBLE.
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
} else if (iView.getVisibility() == View.VISIBLE) {
|
||||
// First, set visibility to INVISIBLE for animation.
|
||||
iView.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (animated) {
|
||||
// Set the view to GONE after the fade animation ends.
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
if (!isVisible) {
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
}, fadeOutDuration);
|
||||
} else {
|
||||
// If no animation, immediately set the view to GONE.
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "private_setVisibility failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||
|
||||
public abstract class PlayerControlButton {
|
||||
private static final Animation fadeIn;
|
||||
private static final Animation fadeOut;
|
||||
private static final Animation fadeOutImmediate;
|
||||
|
||||
private final WeakReference<ImageView> buttonRef;
|
||||
protected final BooleanSetting setting;
|
||||
protected boolean isVisible;
|
||||
|
||||
static {
|
||||
// TODO: check if these durations are correct.
|
||||
fadeIn = Utils.getResourceAnimation("fade_in");
|
||||
fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||
|
||||
fadeOut = Utils.getResourceAnimation("fade_out");
|
||||
fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled"));
|
||||
|
||||
fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out");
|
||||
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Animation getButtonFadeIn() {
|
||||
return fadeIn;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Animation getButtonFadeOut() {
|
||||
return fadeOut;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Animation getButtonFadeOutImmediately() {
|
||||
return fadeOutImmediate;
|
||||
}
|
||||
|
||||
public PlayerControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId,
|
||||
@NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener,
|
||||
@Nullable View.OnLongClickListener longClickListener) {
|
||||
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId);
|
||||
|
||||
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
|
||||
Utils.getResourceIdentifier(imageViewButtonId, "id")
|
||||
));
|
||||
imageView.setVisibility(View.GONE);
|
||||
|
||||
imageView.setOnClickListener(onClickListener);
|
||||
if (longClickListener != null) {
|
||||
imageView.setOnLongClickListener(longClickListener);
|
||||
}
|
||||
|
||||
setting = booleanSetting;
|
||||
buttonRef = new WeakReference<>(imageView);
|
||||
}
|
||||
|
||||
public void setVisibilityImmediate(boolean visible) {
|
||||
if (visible) {
|
||||
// Fix button flickering, by pushing this call to the back of
|
||||
// the main thread and letting other layout code run first.
|
||||
Utils.runOnMainThread(() -> private_setVisibility(true, false));
|
||||
} else {
|
||||
private_setVisibility(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisibility(boolean visible, boolean animated) {
|
||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
||||
if (visible && !animated) return;
|
||||
|
||||
private_setVisibility(visible, animated);
|
||||
}
|
||||
|
||||
private void private_setVisibility(boolean visible, boolean animated) {
|
||||
try {
|
||||
if (isVisible == visible) return;
|
||||
isVisible = visible;
|
||||
|
||||
ImageView iView = buttonRef.get();
|
||||
if (iView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible && setting.get()) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
||||
}
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
} else if (iView.getVisibility() == View.VISIBLE) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
||||
}
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setVisibility failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.transition.Fade;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
// Ideally this should be refactored into PlayerControlBottomButton,
|
||||
// but the show/hide logic is not the same so keeping this as two classes might be simpler.
|
||||
public abstract class PlayerControlTopButton {
|
||||
static final int fadeInDuration;
|
||||
static final int fadeOutDuration;
|
||||
|
||||
private static final Animation fadeInAnimation;
|
||||
private static final Animation fadeOutAnimation;
|
||||
|
||||
static final Fade fadeInTransition;
|
||||
static final Fade fadeOutTransition;
|
||||
|
||||
private final WeakReference<ImageView> buttonReference;
|
||||
private boolean isShowing;
|
||||
|
||||
static {
|
||||
fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
|
||||
fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
|
||||
|
||||
fadeInAnimation = Utils.getResourceAnimation("fade_in");
|
||||
fadeInAnimation.setDuration(fadeInDuration);
|
||||
|
||||
fadeOutAnimation = Utils.getResourceAnimation("fade_out");
|
||||
fadeOutAnimation.setDuration(fadeOutDuration);
|
||||
|
||||
fadeInTransition = new Fade();
|
||||
fadeInTransition.setDuration(fadeInDuration);
|
||||
|
||||
fadeOutTransition = new Fade();
|
||||
fadeOutTransition.setDuration(fadeOutDuration);
|
||||
}
|
||||
|
||||
protected PlayerControlTopButton(ImageView imageView, View.OnClickListener onClickListener) {
|
||||
imageView.setVisibility(View.GONE);
|
||||
imageView.setOnClickListener(onClickListener);
|
||||
|
||||
buttonReference = new WeakReference<>(imageView);
|
||||
}
|
||||
|
||||
protected void setVisibilityImmediate(boolean visible) {
|
||||
private_setVisibility(visible, false);
|
||||
}
|
||||
|
||||
protected void setVisibility(boolean visible, boolean animated) {
|
||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
||||
if (visible && !animated) return;
|
||||
|
||||
private_setVisibility(visible, animated);
|
||||
}
|
||||
|
||||
private void private_setVisibility(boolean visible, boolean animated) {
|
||||
try {
|
||||
if (isShowing == visible) return;
|
||||
isShowing = visible;
|
||||
|
||||
ImageView iView = buttonReference.get();
|
||||
if (iView == null) return;
|
||||
|
||||
if (visible) {
|
||||
iView.clearAnimation();
|
||||
if (!shouldBeShown()) {
|
||||
return;
|
||||
}
|
||||
if (animated) {
|
||||
iView.startAnimation(fadeInAnimation);
|
||||
}
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iView.getVisibility() == View.VISIBLE) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(fadeOutAnimation);
|
||||
}
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "private_setVisibility failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean shouldBeShown();
|
||||
|
||||
public void hide() {
|
||||
if (!isShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.verifyOnMainThread();
|
||||
View v = buttonReference.get();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
v.setVisibility(View.GONE);
|
||||
isShowing = false;
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.11.0-dev.1
|
||||
version = 5.13.0-dev.4
|
||||
|
||||
@@ -3,7 +3,6 @@ revanced-patcher = "21.0.0"
|
||||
# Tracking https://github.com/google/smali/issues/64.
|
||||
#noinspection GradleDependency
|
||||
smali = "3.0.5"
|
||||
gson = "2.11.0"
|
||||
# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818.
|
||||
#noinspection GradleDependency
|
||||
agp = "8.2.2"
|
||||
@@ -11,10 +10,9 @@ annotation = "1.9.1"
|
||||
appcompat = "1.7.0"
|
||||
okhttp = "5.0.0-alpha.14"
|
||||
retrofit = "2.11.0"
|
||||
guava = "33.2.1-jre"
|
||||
guava = "33.4.0-jre"
|
||||
|
||||
[libraries]
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1",
|
||||
"semantic-release": "^24.1.2"
|
||||
"semantic-release": "^24.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -6760,9 +6760,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semantic-release": {
|
||||
"version": "24.1.2",
|
||||
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.1.2.tgz",
|
||||
"integrity": "sha512-hvEJ7yI97pzJuLsDZCYzJgmRxF8kiEJvNZhf0oiZQcexw+Ycjy4wbdsn/sVMURgNCu8rwbAXJdBRyIxM4pe32g==",
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.1.tgz",
|
||||
"integrity": "sha512-z0/3cutKNkLQ4Oy0HTi3lubnjTsdjjgOqmxdPjeYWe6lhFqUPfwslZxRHv3HDZlN4MhnZitb9SLihDkZNxOXfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6782,7 +6782,7 @@
|
||||
"git-log-parser": "^1.2.0",
|
||||
"hook-std": "^3.0.0",
|
||||
"hosted-git-info": "^8.0.0",
|
||||
"import-from-esm": "^1.3.1",
|
||||
"import-from-esm": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^12.0.0",
|
||||
"marked-terminal": "^7.0.0",
|
||||
@@ -6926,6 +6926,20 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semantic-release/node_modules/import-from-esm": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
|
||||
"integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"import-meta-resolve": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20"
|
||||
}
|
||||
},
|
||||
"node_modules/semantic-release/node_modules/indent-string": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1",
|
||||
"semantic-release": "^24.1.2"
|
||||
"semantic-release": "^24.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ patches {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Used by JsonGenerator.
|
||||
implementation(libs.gson)
|
||||
// Required due to smali, or build fails. Can be removed once smali is bumped.
|
||||
implementation(libs.guava)
|
||||
// Android API stubs defined here.
|
||||
|
||||
@@ -14,7 +14,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch(
|
||||
) {
|
||||
dependsOn(
|
||||
bytecodePatch {
|
||||
extendWith("extensions/all/misc/directory/export-internal-data-documents-provider.rve")
|
||||
extendWith("extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider.rve")
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch"
|
||||
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -8,7 +8,12 @@ val hideVideoAdsPatch = bytecodePatch(
|
||||
name = "Hide music video ads",
|
||||
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
navigate(showVideoAdsParentFingerprint.originalMethod)
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
package app.revanced.patches.music.audio.exclusiveaudio
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val enableExclusiveAudioPlaybackPatch = bytecodePatch(
|
||||
name = "Enable exclusive audio playback",
|
||||
description = "Enables the option to play audio without video.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
allowExclusiveAudioPlaybackFingerprint.method.apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,15 @@ internal val allowExclusiveAudioPlaybackFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
parameters()
|
||||
opcodes(
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.GOTO,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.RETURN
|
||||
Opcode.MOVE_RESULT
|
||||
)
|
||||
}
|
||||
@@ -11,10 +11,14 @@ val permanentRepeatPatch = bytecodePatch(
|
||||
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex
|
||||
val repeatIndex = startIndex + 1
|
||||
|
||||
|
||||
@@ -11,7 +11,12 @@ val hideCategoryBar = bytecodePatch(
|
||||
description = "Hides the category bar at the top of the homepage.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
constructCategoryBarFingerprint.method.apply {
|
||||
|
||||
@@ -11,7 +11,12 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
name = "Hide 'Get Music Premium' label",
|
||||
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
hideGetPremiumFingerprint.method.apply {
|
||||
|
||||
@@ -18,7 +18,12 @@ val removeUpgradeButtonPatch = bytecodePatch(
|
||||
name = "Remove upgrade button",
|
||||
description = "Removes the upgrade tab from the pivot bar.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
pivotBarConstructorFingerprint.method.apply {
|
||||
|
||||
@@ -8,7 +8,12 @@ val bypassCertificateChecksPatch = bytecodePatch(
|
||||
name = "Bypass certificate checks",
|
||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music"("7.29.52"))
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
checkCertificateFingerprint.method.returnEarly(true)
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val checkCertificateFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
parameters("Ljava/lang/String;")
|
||||
strings("X509", "Failed to get certificate.")
|
||||
strings(
|
||||
"X509",
|
||||
"Failed to get certificate" // Partial String match.
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,12 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
name = "Remove background playback restrictions",
|
||||
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction(
|
||||
|
||||
@@ -25,7 +25,12 @@ val spoofClientPatch = bytecodePatch(
|
||||
name = "Spoof client",
|
||||
description = "Spoofs the client to fix playback.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
|
||||
@@ -56,8 +56,9 @@ val customThemePatch = resourcePatch(
|
||||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
for (i in 0 until resourcesNode.childNodes.length) {
|
||||
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||
val childNodes = resourcesNode.childNodes
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
|
||||
node.textContent =
|
||||
when (node.getAttribute("name")) {
|
||||
|
||||
@@ -2,4 +2,4 @@ package app.revanced.patches.tiktok.misc.extension
|
||||
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
|
||||
val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook)
|
||||
val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook, jatoInitHook)
|
||||
|
||||
@@ -12,3 +12,19 @@ internal val initHook = extensionHook(
|
||||
method.name == "<init>"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In some cases the extension code can be called before
|
||||
* the app main activity onCreate is called.
|
||||
*
|
||||
* This class is called from startup code titled "BPEA RunnableGuardLancet"
|
||||
*/
|
||||
internal val jatoInitHook = extensionHook(
|
||||
insertIndexResolver = { 0 },
|
||||
contextRegisterResolver = { "p1" }
|
||||
) {
|
||||
custom { method, classDef ->
|
||||
classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/JatoInitTask;" &&
|
||||
method.name == "run"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package app.revanced.patches.windyapp.misc.unlockpro
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
||||
@Deprecated("This patch no longer works and will be removed in the future.")
|
||||
@Suppress("unused")
|
||||
val unlockProPatch = bytecodePatch(
|
||||
name = "Unlock pro",
|
||||
description = "Unlocks all pro features.",
|
||||
) {
|
||||
compatibleWith("co.windyapp.android")
|
||||
|
||||
@@ -78,8 +78,6 @@ val hideAdsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -25,8 +25,6 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -23,8 +23,6 @@ val videoAdsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -53,8 +53,6 @@ val copyVideoUrlPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -24,8 +24,6 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -68,8 +68,6 @@ val downloadsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -23,8 +23,6 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -26,8 +26,6 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
// 18.38.44 patches but crashes on startup.
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -35,7 +35,6 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -19,7 +19,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
val seekbarThumbnailsPatch = bytecodePatch(
|
||||
name = "Seekbar thumbnails",
|
||||
description = "Adds an option to use high quality fullscreen seekbar thumbnails. " +
|
||||
"Patching 19.16.39 or lower adds an option to restore old seekbar thumbnails.",
|
||||
"Patching 19.16.39 adds an option to restore old seekbar thumbnails.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
@@ -29,8 +29,6 @@ val seekbarThumbnailsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -42,9 +42,10 @@ private val swipeControlsResourcePatch = resourcePatch {
|
||||
SwitchPreference("revanced_swipe_haptic_feedback"),
|
||||
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
||||
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
||||
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
|
||||
SwitchPreference("revanced_swipe_show_circular_overlay"),
|
||||
SwitchPreference("revanced_swipe_overlay_minimal_style"),
|
||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
||||
)
|
||||
|
||||
@@ -53,7 +54,12 @@ private val swipeControlsResourcePatch = resourcePatch {
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"revanced_ic_sc_brightness_auto.xml",
|
||||
"revanced_ic_sc_brightness_manual.xml",
|
||||
"revanced_ic_sc_brightness_full.xml",
|
||||
"revanced_ic_sc_brightness_high.xml",
|
||||
"revanced_ic_sc_brightness_low.xml",
|
||||
"revanced_ic_sc_brightness_medium.xml",
|
||||
"revanced_ic_sc_volume_high.xml",
|
||||
"revanced_ic_sc_volume_low.xml",
|
||||
"revanced_ic_sc_volume_mute.xml",
|
||||
"revanced_ic_sc_volume_normal.xml",
|
||||
),
|
||||
@@ -74,8 +80,6 @@ val swipeControlsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -22,8 +22,6 @@ val autoCaptionsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -43,8 +43,6 @@ val customBrandingPatch = resourcePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
|
||||
@@ -41,8 +41,6 @@ val changeHeaderPatch = resourcePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
@@ -172,14 +170,16 @@ val changeHeaderPatch = resourcePatch(
|
||||
// Instead change styles.xml to use the old drawable resources.
|
||||
if (is_19_25_or_greater) {
|
||||
document("res/values/styles.xml").use { document ->
|
||||
val documentChildNodes = document.childNodes
|
||||
|
||||
arrayOf(
|
||||
"CairoLightThemeRingo2Updates" to variants[0],
|
||||
"CairoDarkThemeRingo2Updates" to variants[1]
|
||||
).forEach { (styleName, theme) ->
|
||||
val style = document.childNodes.findElementByAttributeValueOrThrow(
|
||||
val styleNodes = documentChildNodes.findElementByAttributeValueOrThrow(
|
||||
"name",
|
||||
styleName,
|
||||
)
|
||||
).childNodes
|
||||
|
||||
val drawable = "@drawable/${HEADER_FILE_NAME}_${theme}"
|
||||
|
||||
@@ -187,7 +187,7 @@ val changeHeaderPatch = resourcePatch(
|
||||
"ytWordmarkHeader",
|
||||
"ytPremiumWordmarkHeader"
|
||||
).forEach { itemName ->
|
||||
style.childNodes.findElementByAttributeValueOrThrow(
|
||||
styleNodes.findElementByAttributeValueOrThrow(
|
||||
"name",
|
||||
itemName,
|
||||
).textContent = drawable
|
||||
|
||||
@@ -22,8 +22,6 @@ val hideButtonsPatch = resourcePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
@@ -41,6 +39,7 @@ val hideButtonsPatch = resourcePatch(
|
||||
PreferenceScreenPreference(
|
||||
"revanced_hide_buttons_screen",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_disable_like_subscribe_glow"),
|
||||
SwitchPreference("revanced_hide_like_dislike_button"),
|
||||
SwitchPreference("revanced_hide_share_button"),
|
||||
SwitchPreference("revanced_hide_report_button"),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user