Compare commits

..

No commits in common. "main" and "v5.20.0-dev.1" have entirely different histories.

205 changed files with 1621 additions and 3776 deletions

View File

@ -1,182 +1,3 @@
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)
### Bug Fixes
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
### Features
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)
### Bug Fixes
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)
### Bug Fixes
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
# [5.21.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.9...v5.21.0-dev.10) (2025-04-23)
### Features
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
# [5.21.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.8...v5.21.0-dev.9) (2025-04-21)
### Bug Fixes
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
# [5.21.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.7...v5.21.0-dev.8) (2025-04-20)
### Bug Fixes
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
# [5.21.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.6...v5.21.0-dev.7) (2025-04-20)
### Bug Fixes
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)
### Bug Fixes
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)
### Bug Fixes
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
# [5.21.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.3...v5.21.0-dev.4) (2025-04-17)
### Bug Fixes
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
# [5.21.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.2...v5.21.0-dev.3) (2025-04-16)
### Features
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
# [5.21.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.1...v5.21.0-dev.2) (2025-04-16)
### Features
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)
### Features
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)
### Bug Fixes
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)
### Bug Fixes
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)
### Bug Fixes
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
### Features
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)
### Bug Fixes
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)
### Bug Fixes
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)
### Features
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)
### Bug Fixes
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)
### Bug Fixes
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)
### Features
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)

View File

@ -1,16 +0,0 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
compileOnly(libs.annotation)
}

View File

@ -1,28 +0,0 @@
package app.revanced.extension.all.misc.hide.adb;
import android.content.ContentResolver;
import android.provider.Settings;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings("unused")
public final class HideAdbPatch {
private static final List<String> SPOOF_SETTINGS = Arrays.asList("adb_enabled", "adb_wifi_enabled", "development_settings_enabled");
public static int getInt(ContentResolver cr, String name) throws Settings.SettingNotFoundException {
if (SPOOF_SETTINGS.contains(name)) {
return 0;
}
return Settings.Global.getInt(cr, name);
}
public static int getInt(ContentResolver cr, String name, int def) {
if (SPOOF_SETTINGS.contains(name)) {
return 0;
}
return Settings.Global.getInt(cr, name, def);
}
}

View File

@ -1,4 +1,4 @@
package app.revanced.extension.all.misc.connectivity.wifi.spoof;
package app.revanced.extension.all.connectivity.wifi.spoof;
import android.app.PendingIntent;
import android.content.Context;

View File

@ -342,12 +342,9 @@ public abstract class Setting<T> {
/**
* Identical to calling {@link #save(Object)} using {@link #defaultValue}.
*
* @return The newly saved default value.
*/
public T resetToDefault() {
public void resetToDefault() {
save(defaultValue);
return defaultValue;
}
/**

View File

@ -204,7 +204,7 @@ public class StreamingDataRequest {
// but empty response body does.
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
}
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());

View File

@ -80,9 +80,6 @@ public final class UnlockPremiumPatch {
new OverrideAttribute("streaming-rules", ""),
// Enables premium UI in settings and removes the premium button in the nav-bar.
new OverrideAttribute("nft-disabled", "1"),
// Enable Spotify Connect and disable other premium related UI, like buying premium.
// It also removes the download button.
new OverrideAttribute("type", "premium"),
// Enable Spotify Car Thing hardware device.
// Device is discontinued and no longer works with the latest releases,
// but it might still work with older app targets.

View File

@ -9,7 +9,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@ -82,13 +81,6 @@ public final class ChangeStartPagePatch {
}
}
public static class ChangeStartPageTypeAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return Settings.CHANGE_START_PAGE.get() != StartPage.DEFAULT;
}
}
/**
* Intent action when YouTube is cold started from the launcher.
* <p>
@ -101,8 +93,6 @@ public final class ChangeStartPagePatch {
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
private static final boolean CHANGE_START_PAGE_ALWAYS = Settings.CHANGE_START_PAGE_ALWAYS.get();
/**
* There is an issue where the back button on the toolbar doesn't work properly.
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
@ -114,13 +104,13 @@ public final class ChangeStartPagePatch {
return original;
}
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
if (appLaunched) {
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
return original;
}
appLaunched = true;
Logger.printDebug(() -> "Changing browseId to: " + START_PAGE.id);
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id);
return START_PAGE.id;
}
@ -135,14 +125,14 @@ public final class ChangeStartPagePatch {
return;
}
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
if (appLaunched) {
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
return;
}
appLaunched = true;
String intentAction = START_PAGE.id;
Logger.printDebug(() -> "Changing intent action to: " + intentAction);
Logger.printDebug(() -> "Changing intent action to " + intentAction);
intent.setAction(intentAction);
}
}

View File

@ -17,7 +17,8 @@ public class CustomPlayerOverlayOpacityPatch {
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
opacity = Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue;
}
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;

View File

@ -1,23 +1,20 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused")
public class DisableAutoCaptionsPatch {
private static volatile boolean captionsButtonStatus;
/**
* Injection point.
* Used by injected code. Do not delete.
*/
public static boolean disableAutoCaptions() {
return Settings.DISABLE_AUTO_CAPTIONS.get() && !captionsButtonStatus;
public static boolean captionsButtonDisabled;
public static boolean autoCaptionsEnabled() {
return Settings.AUTO_CAPTIONS.get()
// Do not use auto captions for Shorts.
&& ShortsPlayerState.isOpen();
}
/**
* Injection point.
*/
public static void setCaptionsButtonStatus(boolean status) {
captionsButtonStatus = status;
}
}

View File

@ -162,7 +162,8 @@ public final class MiniplayerPatch {
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
opacity = Settings.MINIPLAYER_OPACITY.resetToDefault();
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;

View File

@ -1,48 +1,11 @@
package app.revanced.extension.youtube.patches;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.View;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class WideSearchbarPatch {
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
/**
* Injection point.
*/
public static boolean enableWideSearchbar(boolean original) {
return WIDE_SEARCHBAR_ENABLED || original;
}
/**
* Injection point.
*/
public static void setActionBar(View view) {
try {
if (!WIDE_SEARCHBAR_ENABLED) return;
View searchBarView = Utils.getChildViewByResourceName(view, "search_bar");
final int paddingLeft = searchBarView.getPaddingLeft();
final int paddingRight = searchBarView.getPaddingRight();
final int paddingTop = searchBarView.getPaddingTop();
final int paddingBottom = searchBarView.getPaddingBottom();
final int paddingStart = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
8, Resources.getSystem().getDisplayMetrics());
if (Utils.isRightToLeftTextLayout()) {
searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom);
} else {
searchBarView.setPadding(paddingStart, paddingTop, paddingRight, paddingBottom);
}
} catch (Exception ex) {
Logger.printException(() -> "setActionBar failure", ex);
}
return Settings.WIDE_SEARCHBAR.get() || original;
}
}

View File

@ -31,7 +31,7 @@ final class ButtonsFilter extends Filter {
bufferFilterPathGroup = new StringFilterGroup(
null,
"|ContainerType|button.eml"
"|ContainerType|button.eml|"
);
addPathCallbacks(
@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
),
new StringFilterGroup(
Settings.HIDE_DOWNLOAD_BUTTON,
"|download_button.eml"
"|download_button.eml|"
),
new StringFilterGroup(
Settings.HIDE_PLAYLIST_BUTTON,
@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
),
new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml"
"|clip_button.eml|"
)
);
@ -68,19 +68,15 @@ final class ButtonsFilter extends Filter {
Settings.HIDE_REMIX_BUTTON,
"yt_outline_youtube_shorts_plus"
),
new ByteArrayFilterGroup(
Settings.HIDE_THANKS_BUTTON,
"yt_outline_dollar_sign_heart"
),
new ByteArrayFilterGroup(
Settings.HIDE_ASK_BUTTON,
"yt_fill_spark"
),
// Check for clip button both here and using a path filter,
// as there's a chance the path is a generic action button and won't contain 'clip_button'
new ByteArrayFilterGroup(
Settings.HIDE_CLIP_BUTTON,
"yt_outline_scissors"
),
new ByteArrayFilterGroup(
Settings.HIDE_THANKS_BUTTON,
"yt_outline_dollar_sign_heart"
)
);
}

View File

@ -211,7 +211,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInnerButton = new StringFilterGroup(
null,
"|button.eml"
"|button.eml|"
);
joinMembershipButton = new ByteArrayFilterGroup(

View File

@ -40,7 +40,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
addPathCallbacks(
videoQualityMenuFooter,
new StringFilterGroup(null, "overflow_menu_item.eml")
new StringFilterGroup(null, "overflow_menu_item.eml|")
);
flyoutFilterGroupList.addAll(

View File

@ -54,12 +54,12 @@ public class CustomPlaybackSpeedPatch {
static {
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
TAP_AND_HOLD_SPEED = holdSpeed;
} else {
showInvalidCustomSpeedToast();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.resetToDefault();
Settings.SPEED_TAP_AND_HOLD.resetToDefault();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
}
loadCustomSpeeds();

View File

@ -7,7 +7,6 @@ import static app.revanced.extension.shared.settings.Setting.migrateOldSettingTo
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.ChangeStartPageTypeAvailability;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
@ -25,7 +24,6 @@ import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehavi
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
import android.graphics.Color;
@ -126,7 +124,6 @@ public class Settings extends BaseSettings {
// Player
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_AUTO_CAPTIONS = new BooleanSetting("revanced_disable_auto_captions", FALSE, true);
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
@ -199,7 +196,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
// Player flyout menu items
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
@ -225,8 +221,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
new ChangeStartPageTypeAvailability());
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
// Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
@ -300,6 +294,7 @@ public class Settings extends BaseSettings {
// Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
@ -324,15 +319,12 @@ public class Settings extends BaseSettings {
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_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
public static final 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));
public static final StringSetting SWIPE_OVERLAY_PROGRESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_color", "#FFFFFF", 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));
@ -410,7 +402,6 @@ public class Settings extends BaseSettings {
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
private static final BooleanSetting DEPRECATED_AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
static {
// region Migration
@ -464,11 +455,6 @@ public class Settings extends BaseSettings {
SPOOF_APP_VERSION_TARGET.resetToDefault();
}
if (!DEPRECATED_AUTO_CAPTIONS.isSetToDefault()) {
DISABLE_AUTO_CAPTIONS.save(true);
DEPRECATED_AUTO_CAPTIONS.resetToDefault();
}
// endregion
// region SB import/export callbacks

View File

@ -1,98 +1,83 @@
package app.revanced.extension.youtube.swipecontrols
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import app.revanced.extension.shared.Logger
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
/**
* Provides configuration settings for volume and brightness swipe controls in the YouTube player.
* Manages enabling/disabling gestures, overlay appearance, and behavior preferences.
* provider for configuration for volume and brightness swipe controls
*
* @param context the context to create in
*/
class SwipeControlsConfigurationProvider {
//region swipe enable
class SwipeControlsConfigurationProvider(
private val context: Context,
) {
//region swipe enable
/**
* Indicates whether swipe controls are enabled globally.
* Returns true if either volume or brightness controls are enabled and the video is in fullscreen mode.
* should swipe controls be enabled? (global setting)
*/
val enableSwipeControls: Boolean
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
/**
* Indicates whether swipe controls for adjusting volume are enabled.
* should swipe controls for volume be enabled?
*/
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
/**
* Indicates whether swipe controls for adjusting brightness are enabled.
* should swipe controls for volume be enabled?
*/
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
/**
* Checks if the video player is currently in fullscreen mode.
* is the video player currently in fullscreen mode?
*/
private val isFullscreenVideo: Boolean
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
//endregion
//endregion
//region keys enable
//region keys enable
/**
* Indicates whether volume key controls should be overridden by swipe controls.
* Returns true if volume controls are enabled and the video is in fullscreen mode.
* should volume key controls be overwritten? (global setting)
*/
val overwriteVolumeKeyControls: Boolean
get() = enableVolumeControls && isFullscreenVideo
//endregion
//endregion
//region gesture adjustments
//region gesture adjustments
/**
* Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
* should press-to-swipe be enabled?
*/
val shouldEnablePressToSwipe: Boolean
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
/**
* The threshold for detecting swipe gestures, in pixels.
* Loaded once to ensure consistent behavior during rapid scroll events.
* threshold for swipe detection
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant
*/
val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
//endregion
//region overlay adjustments
/**
* The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
* Resets to default if set to 0, as it would disable swiping.
*/
val volumeSwipeSensitivity: Int
get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) {
return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
}
return sensitivity
}
//endregion
//region overlay adjustments
/**
* Indicates whether haptic feedback should be enabled for swipe control interactions.
* should the overlay enable haptic feedback?
*/
val shouldEnableHapticFeedback: Boolean
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
/**
* The duration in milliseconds that the overlay should remain visible after a change.
* how long the overlay should be shown on changes
*/
val overlayShowTimeoutMillis: Long
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/**
* The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
* Resets to default and shows a toast if the value is out of range.
* 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 overlayBackgroundOpacity: Int
get() {
@ -100,7 +85,8 @@ class SwipeControlsConfigurationProvider {
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
}
opacity = opacity * 255 / 100
@ -108,125 +94,55 @@ class SwipeControlsConfigurationProvider {
}
/**
* The color of the progress bar in the overlay.
* Resets to default and shows a toast if the color string is invalid or empty.
* The color of the progress overlay.
*/
val overlayProgressColor: Int
get() {
try {
@SuppressLint("UseKtx")
val color = Color.parseColor(Settings.SWIPE_OVERLAY_PROGRESS_COLOR.get())
return (0xBF000000.toInt() or (color and 0xFFFFFF))
} catch (ex: IllegalArgumentException) {
Logger.printDebug({ "Could not parse color" }, ex)
Utils.showToastLong(str("revanced_swipe_overlay_progress_color_invalid_toast"))
Settings.SWIPE_OVERLAY_PROGRESS_COLOR.resetToDefault()
return overlayProgressColor // Recursively return.
}
}
get() = 0xBFFFFFFF.toInt()
/**
* The background color used for the filled portion of the progress bar in the overlay.
* The color used for the background of the progress overlay fill.
*/
val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt()
/**
* The color used for text and icons in the overlay.
* The color used for the text and icons in the overlay.
*/
val overlayTextColor: Int
get() = Color.WHITE
/**
* The text size in the overlay, in density-independent pixels (dp).
* Must be between 1 and 30 dp; resets to default and shows a toast if invalid.
* A flag that determines if the overlay should only show the icon.
*/
val overlayTextSize: Int
get() {
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
if (size < 1 || size > 30) {
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
return Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
}
return size
}
val overlayShowOverlayMinimalStyle: Boolean
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
/**
* Defines the style of the swipe controls overlay, determining its layout and appearance.
*
* @property isMinimal Indicates whether the style is minimalistic, omitting detailed progress indicators.
* @property isHorizontalMinimalCenter Indicates whether the style is a minimal horizontal bar centered vertically.
* @property isCircular Indicates whether the style uses a circular progress bar.
* @property isVertical Indicates whether the style uses a vertical progress bar.
* A flag that determines if the progress bar should be circular.
*/
@Suppress("unused")
enum class SwipeOverlayStyle(
val isMinimal: Boolean = false,
val isHorizontalMinimalCenter: Boolean = false,
val isCircular: Boolean = false,
val isVertical: Boolean = false
) {
/**
* A full horizontal progress bar with detailed indicators.
*/
HORIZONTAL,
val isCircularProgressBar: Boolean
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
//endregion
/**
* A minimal horizontal progress bar positioned at the top.
*/
HORIZONTAL_MINIMAL_TOP(isMinimal = true),
/**
* A minimal horizontal progress bar centered vertically.
*/
HORIZONTAL_MINIMAL_CENTER(isMinimal = true, isHorizontalMinimalCenter = true),
/**
* A full circular progress bar with detailed indicators.
*/
CIRCULAR(isCircular = true),
/**
* A minimal circular progress bar.
*/
CIRCULAR_MINIMAL(isMinimal = true, isCircular = true),
/**
* A full vertical progress bar with detailed indicators.
*/
VERTICAL(isVertical = true),
/**
* A minimal vertical progress bar.
*/
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
}
//region behaviour
/**
* The current style of the overlay, determining its layout and appearance.
*/
val overlayStyle: SwipeOverlayStyle
get() = Settings.SWIPE_OVERLAY_STYLE.get()
//endregion
//region behaviour
/**
* Indicates whether the brightness level should be saved and restored when entering or exiting fullscreen mode.
* should the brightness be saved and restored when exiting or entering fullscreen
*/
val shouldSaveAndRestoreBrightness: Boolean
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
/**
* Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
* should auto-brightness be enabled at the lowest value of the brightness gesture
*/
val shouldLowestValueEnableAutoBrightness: Boolean
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
/**
* The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
* variable that stores the brightness gesture value in the settings
*/
var savedScreenBrightnessValue: Float
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
//endregion
//endregion
}

View File

@ -23,7 +23,9 @@ import java.lang.ref.WeakReference
/**
* The main controller for volume and brightness swipe controls.
* note that the superclass is overwritten to the superclass of the MainActivity at patch time.
* note that the superclass is overwritten to the superclass of the MainActivity at patch time
*
* @smali Lapp/revanced/extension/swipecontrols/SwipeControlsHostActivity;
*/
class SwipeControlsHostActivity : Activity() {
/**
@ -125,7 +127,7 @@ class SwipeControlsHostActivity : Activity() {
private fun initialize() {
// create controllers
printDebug { "initializing swipe controls controllers" }
config = SwipeControlsConfigurationProvider()
config = SwipeControlsConfigurationProvider(this)
keys = VolumeKeysController(this)
audio = createAudioController()
screen = createScreenController()

View File

@ -41,7 +41,7 @@ class VolumeKeysController(
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
controller.audio?.apply {
volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
volume += if (volumeUp) 1 else -1
controller.overlay.onVolumeChanged(volume, maxVolume)
}
}

View File

@ -24,7 +24,6 @@ abstract class BaseGestureController(
controller.overlay,
10,
1,
controller.config.volumeSwipeSensitivity,
) {
/**

View File

@ -41,7 +41,6 @@ interface VolumeAndBrightnessScroller {
* @param overlayController overlay controller instance
* @param volumeDistance unit distance for volume scrolling, in dp
* @param brightnessDistance unit distance for brightness scrolling, in dp
* @param volumeSwipeSensitivity how much volume will change by single swipe
*/
class VolumeAndBrightnessScrollerImpl(
context: Context,
@ -50,7 +49,6 @@ class VolumeAndBrightnessScrollerImpl(
private val overlayController: SwipeControlsOverlay,
volumeDistance: Int = 10,
brightnessDistance: Int = 1,
private val volumeSwipeSensitivity: Int,
) : VolumeAndBrightnessScroller {
// region volume
@ -62,7 +60,7 @@ class VolumeAndBrightnessScrollerImpl(
),
) { _, _, direction ->
volumeController?.run {
volume += direction * volumeSwipeSensitivity
volume += direction
overlayController.onVolumeChanged(volume, maxVolume)
}
}

View File

@ -1,11 +1,8 @@
package app.revanced.extension.youtube.swipecontrols.views
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.os.Handler
@ -14,30 +11,21 @@ import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.RelativeLayout
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 kotlin.math.min
import kotlin.math.max
import kotlin.math.round
/**
* Convert dp to pixels based on system display density.
*/
fun Float.toDisplayPixels(): Float {
return this * Resources.getSystem().displayMetrics.density
}
/**
* Main overlay layout for displaying volume and brightness level with circular, horizontal and vertical progress bars.
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
*/
class SwipeControlsOverlayLayout(
context: Context,
private val config: SwipeControlsConfigurationProvider,
) : RelativeLayout(context), SwipeControlsOverlay {
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
// Drawable icons for brightness and volume
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
@ -63,21 +51,18 @@ class SwipeControlsOverlayLayout(
// Initialize progress bars
private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView
private val verticalBrightnessProgressView: VerticalProgressView
private val verticalVolumeProgressView: VerticalProgressView
init {
// Initialize circular progress bar
circularProgressView = CircularProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayShowOverlayMinimalStyle,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
config.overlayTextColor
).apply {
layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
layoutParams = LayoutParams(300, 300).apply {
addRule(CENTER_IN_PARENT, TRUE)
}
visibility = GONE // Initially hidden
@ -86,65 +71,22 @@ class SwipeControlsOverlayLayout(
// Initialize horizontal progress bar
val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
horizontalProgressView = HorizontalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayShowOverlayMinimalStyle,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
config.overlayTextColor
).apply {
layoutParams = LayoutParams(layoutWidth, 32f.toDisplayPixels().toInt()).apply {
layoutParams = LayoutParams(layoutWidth, 100).apply {
addRule(CENTER_HORIZONTAL)
if (config.overlayStyle.isHorizontalMinimalCenter) {
addRule(CENTER_VERTICAL)
} else {
topMargin = 20f.toDisplayPixels().toInt()
}
topMargin = 40 // Top margin
}
visibility = GONE // Initially hidden
}
addView(horizontalProgressView)
// Initialize vertical progress bar for brightness (right side)
verticalBrightnessProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
addRule(ALIGN_PARENT_RIGHT)
rightMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
}
addView(verticalBrightnessProgressView)
// Initialize vertical progress bar for volume (left side)
verticalVolumeProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
addRule(ALIGN_PARENT_LEFT)
leftMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
}
addView(verticalVolumeProgressView)
}
// Handler and callback for hiding progress bars
@ -152,8 +94,6 @@ class SwipeControlsOverlayLayout(
private val feedbackHideCallback = Runnable {
circularProgressView.visibility = GONE
horizontalProgressView.visibility = GONE
verticalBrightnessProgressView.visibility = GONE
verticalVolumeProgressView.visibility = GONE
}
/**
@ -163,11 +103,7 @@ class SwipeControlsOverlayLayout(
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
val viewToShow = when {
config.overlayStyle.isCircular -> circularProgressView
config.overlayStyle.isVertical -> if (isBrightness) verticalBrightnessProgressView else verticalVolumeProgressView
else -> horizontalProgressView
}
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
viewToShow.apply {
setProgress(progress, max, value, isBrightness)
this.icon = icon
@ -190,9 +126,7 @@ class SwipeControlsOverlayLayout(
// Handle brightness change
override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
val displayText = if (config.overlayStyle.isVertical) "А"
else str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text")
showFeedbackView(displayText, 0, 100, autoBrightnessIcon, isBrightness = true)
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
} else {
val brightnessValue = round(brightness).toInt()
val icon = when {
@ -201,8 +135,7 @@ class SwipeControlsOverlayLayout(
brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
val displayText = if (config.overlayStyle.isVertical) "$brightnessValue" else "$brightnessValue%"
showFeedbackView(displayText, brightnessValue, 100, icon, isBrightness = true)
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
}
}
@ -223,12 +156,11 @@ class SwipeControlsOverlayLayout(
*/
abstract class AbstractProgressView(
context: Context,
overlayBackgroundOpacity: Int,
protected val isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
private val overlayTextColor: Int,
protected val overlayTextSize: Int,
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) {
@ -242,25 +174,26 @@ abstract class AbstractProgressView(
}
// Initialize paints
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
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 = overlayTextSize.toFloat().toDisplayPixels()
textSize = 40f // Can adjust based on need
}
// Rect for text measurement
protected val textBounds = Rect()
protected var progress = 0
protected var maxProgress = 100
protected var displayText: String = "0"
protected var isBrightness = true
var icon: Drawable? = null
public var icon: Drawable? = null
open fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
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
@ -268,11 +201,6 @@ abstract class AbstractProgressView(
invalidate()
}
protected fun measureTextWidth(text: String, paint: Paint): Int {
paint.getTextBounds(text, 0, text.length, textBounds)
return textBounds.width()
}
override fun onDraw(canvas: Canvas) {
// Base class implementation can be empty
}
@ -281,36 +209,34 @@ abstract class AbstractProgressView(
/**
* Custom view for rendering a circular progress indicator with icons and text.
*/
@SuppressLint("ViewConstructor")
class CircularProgressView(
context: Context,
overlayBackgroundOpacity: Int,
isMinimalStyle: Boolean,
overlayShowOverlayMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
isMinimalStyle,
overlayShowOverlayMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val rectF = RectF()
init {
progressPaint.strokeWidth = 6f.toDisplayPixels()
fillBackgroundPaint.strokeWidth = 6f.toDisplayPixels()
progressPaint.strokeCap = Paint.Cap.ROUND
textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE
progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE
}
@ -318,8 +244,7 @@ class CircularProgressView(
super.onDraw(canvas)
val size = min(width, height).toFloat()
val inset = 6f.toDisplayPixels()
rectF.set(inset, inset, size - inset, size - inset)
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.
@ -330,307 +255,124 @@ class CircularProgressView(
// Draw the icon in the center.
icon?.let {
val iconSize = (if (isMinimalStyle) 36f else 24f).toDisplayPixels().toInt()
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
val iconX = (width - iconSize) / 2
val iconY = if (isMinimalStyle) {
(height - iconSize) / 2
} else {
(height / 2) - 24f.toDisplayPixels().toInt()
}
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
it.draw(canvas)
}
// If not a minimal style mode, draw the text inside the ring.
if (!isMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 20f.toDisplayPixels(), textPaint)
if (!overlayShowOverlayMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
}
}
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}
/**
* Custom view for rendering a rectangular progress bar with icons and text.
*/
@SuppressLint("ViewConstructor")
class HorizontalProgressView(
context: Context,
overlayBackgroundOpacity: Int,
isMinimalStyle: Boolean,
overlayShowOverlayMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
isMinimalStyle,
overlayShowOverlayMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val iconSize = 20f.toDisplayPixels()
private val padding = 12f.toDisplayPixels()
private var textWidth = 0f
private val progressBarHeight = 3f.toDisplayPixels()
private val progressBarWidth: Float = resources.displayMetrics.widthPixels / 4f
private val iconSize = 60f
private val padding = 40f
init {
textPaint.textSize = 36f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
/**
* Calculate required width based on content
* @return Required width to display all elements
*/
private fun calculateRequiredWidth(): Float {
textWidth = measureTextWidth(displayText, textPaint).toFloat()
return if (!isMinimalStyle) {
padding + iconSize + padding + progressBarWidth + padding + textWidth + padding
} else {
padding + iconSize + padding + textWidth + padding
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
val height = suggestedHeight
val requiredWidth = calculateRequiredWidth().toInt()
val width = min(max(100, requiredWidth), suggestedWidth)
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val viewWidth = width.toFloat()
val viewHeight = height.toFloat()
val viewHeightHalf = viewHeight / 2
val width = width.toFloat()
val height = height.toFloat()
textWidth = measureTextWidth(displayText, textPaint).toFloat()
// Radius for rounded corners
val cornerRadius = min(width, height) / 2
val cornerRadius = viewHeightHalf
// Calculate the total width for the elements
val minimalElementWidth = 5 * padding + iconSize
val startX = padding
val iconEndX = startX + iconSize
// Calculate the starting point (X) to center the elements
val minimalStartX = (width - minimalElementWidth) / 2
val textStartX = (viewWidth - 1.5f * padding - textWidth)
// 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)
}
canvas.drawRoundRect(
0f, 0f, viewWidth, viewHeight,
cornerRadius, cornerRadius, backgroundPaint
)
if (!overlayShowOverlayMinimalStyle) {
// Draw the fill background
val startX = 2 * padding + iconSize
val endX = width - 4 * padding
val fillWidth = endX - startX
icon?.let {
val iconY = viewHeightHalf - iconSize / 2
it.setBounds(
startX.toInt(),
iconY.toInt(),
(startX + iconSize).toInt(),
(iconY + iconSize).toInt()
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)
}
val textY = viewHeightHalf + textPaint.textSize / 3
textPaint.textAlign = Paint.Align.LEFT
if (isMinimalStyle) {
canvas.drawText(displayText, textStartX, textY, textPaint)
// Draw the text on the right
val textX = if (!overlayShowOverlayMinimalStyle) {
width - 2 * padding
} else {
val progressStartX = iconEndX + padding
val progressEndX = textStartX - padding
val progressWidth = progressEndX - progressStartX
if (progressWidth > 50) {
val progressBarHeightHalf = progressBarHeight / 2.0f
val viewHeightHalfMinusProgressBarHeightHalf = viewHeightHalf - progressBarHeightHalf
val viewHeightHalfPlusProgressBarHeightHalf = viewHeightHalf + progressBarHeightHalf
canvas.drawRoundRect(
progressStartX,
viewHeightHalfMinusProgressBarHeightHalf,
progressEndX,
viewHeightHalfPlusProgressBarHeightHalf,
progressBarHeightHalf,
progressBarHeightHalf,
fillBackgroundPaint
)
val progressValue = (progress.toFloat() / maxProgress) * progressWidth
canvas.drawRoundRect(
progressStartX,
viewHeightHalfMinusProgressBarHeightHalf,
progressStartX + progressValue,
viewHeightHalfPlusProgressBarHeightHalf,
progressBarHeightHalf,
progressBarHeightHalf,
progressPaint
)
}
canvas.drawText(displayText, textStartX, textY, textPaint)
minimalStartX + minimalElementWidth - 2 * padding
}
}
val textY = height / 2 + textPaint.textSize / 3
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}
/**
* Custom view for rendering a vertical progress bar with icons and text.
*/
@SuppressLint("ViewConstructor")
class VerticalProgressView(
context: Context,
overlayBackgroundOpacity: Int,
isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
isMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val iconSize = 20f.toDisplayPixels()
private val padding = 12f.toDisplayPixels()
private val progressBarWidth = 3f.toDisplayPixels()
private val progressBarHeight: Float = resources.displayMetrics.widthPixels / 3f
init {
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
/**
* Calculate required height based on content
* @return Required height to display all elements
*/
private fun calculateRequiredHeight(): Float {
return if (!isMinimalStyle) {
padding + iconSize + padding + progressBarHeight + padding + textPaint.textSize + padding
} else {
padding + iconSize + padding + textPaint.textSize + padding
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
val requiredHeight = calculateRequiredHeight().toInt()
val height = min(max(100, requiredHeight), suggestedHeight)
setMeasuredDimension(suggestedWidth, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val viewWidth = width.toFloat()
val viewHeight = height.toFloat()
val viewWidthHalf = viewWidth / 2
val cornerRadius = viewWidthHalf
val startY = padding
val iconEndY = startY + iconSize
val textStartY = viewHeight - padding - textPaint.textSize / 2
canvas.drawRoundRect(
0f, 0f, viewWidth, viewHeight,
cornerRadius, cornerRadius, backgroundPaint
)
icon?.let {
val iconX = viewWidthHalf - iconSize / 2
it.setBounds(
iconX.toInt(),
startY.toInt(),
(iconX + iconSize).toInt(),
(startY + iconSize).toInt()
)
it.draw(canvas)
}
val textX = viewWidthHalf
textPaint.textAlign = Paint.Align.CENTER
if (isMinimalStyle) {
canvas.drawText(displayText, textX, textStartY, textPaint)
} else {
val progressStartY = (iconEndY + padding).toFloat()
val progressEndY = textStartY - textPaint.textSize - padding
val progressHeight = progressEndY - progressStartY
if (progressHeight > 50) {
val progressBarWidthHalf = progressBarWidth / 2
val viewHeightHalfMinusProgressBarHeightHalf = viewWidthHalf - progressBarWidthHalf
val viewHeightHalfPlusProgressBarHeightHalf = viewWidthHalf + progressBarWidthHalf
canvas.drawRoundRect(
viewHeightHalfMinusProgressBarHeightHalf,
progressStartY,
viewHeightHalfPlusProgressBarHeightHalf,
progressEndY,
progressBarWidthHalf,
progressBarWidthHalf,
fillBackgroundPaint
)
val progressValue = (progress.toFloat() / maxProgress) * progressHeight
canvas.drawRoundRect(
viewHeightHalfMinusProgressBarHeightHalf,
progressEndY - progressValue,
viewHeightHalfPlusProgressBarHeightHalf,
progressEndY,
progressBarWidthHalf,
progressBarWidthHalf,
progressPaint
)
}
canvas.drawText(displayText, textX, textStartY, textPaint)
}
}
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
// Draw the text
canvas.drawText(displayText, textX, textY, textPaint)
}
}

View File

@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
version = 5.21.0
version = 5.20.0-dev.1

View File

@ -2,10 +2,6 @@ public final class app/revanced/patches/all/misc/activity/exportall/ExportAllAct
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/all/misc/adb/HideAdbPatchKt {
public static final fun getHideAdbStatusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
}
@ -846,10 +842,6 @@ public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@ -1420,8 +1412,6 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_20_07_or_greater ()Z
public static final fun is_20_09_or_greater ()Z
public static final fun is_20_10_or_greater ()Z
public static final fun is_20_14_or_greater ()Z
public static final fun is_20_15_or_greater ()Z
}
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
@ -1535,11 +1525,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
}
public final class app/revanced/util/BytecodeUtilsKt {
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
@ -1566,17 +1552,9 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I

View File

@ -1,75 +0,0 @@
package app.revanced.patches.all.misc.adb
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;"
private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"),
"I"
)
private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"),
"I"
)
private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean {
return anyOf.any {
MethodUtil.methodSignaturesMatch(it, this)
}
}
@Suppress("unused")
val hideAdbStatusPatch = bytecodePatch(
name = "Hide ADB status",
description = "Hides enabled development settings and/or ADB.",
use = false,
) {
extendWith("extensions/all/misc/adb/hide-adb.rve")
dependsOn(
transformInstructionsPatch(
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
val reference = instruction
.takeIf { it.opcode == Opcode.INVOKE_STATIC }
?.getReference<MethodReference>()
?.takeIf {
it.anyMethodSignatureMatches(it,
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE
)
}
?: return@filterMap null
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
},
transform = { method, entry ->
val (instruction, index, parameterTypes) = entry
val parameterString = parameterTypes.joinToString(separator = "")
val registerString = when (parameterTypes.size) {
2 -> "v${instruction.registerC}, v${instruction.registerD}"
else -> "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}"
}
method.replaceInstruction(
index,
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I"
)
}
)
)
}

View File

@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"

View File

@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
}
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused")

View File

@ -10,7 +10,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
"Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused")

View File

@ -12,8 +12,7 @@ internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
"Z", // useDebugBilling
"Z", // showManageSubscriptions
"Z", // alwaysShowSuperAds
// matches "Lcom/duolingo/debug/FamilyQuestOverride;" or "Lcom/duolingo/data/debug/monetization/FamilyQuestOverride;"
"Lcom/duolingo/",
"Lcom/duolingo/debug/FamilyQuestOverride;",
)
opcodes(Opcode.IPUT_BOOLEAN)
}

View File

@ -52,8 +52,8 @@ fun gmsCoreSupportPatch(
block: BytecodePatchBuilder.() -> Unit = {},
) = bytecodePatch(
name = "GmsCore support",
description = "Allows the app to work without root by using a different package name when patched " +
"using a GmsCore instead of Google Play Services.",
description = "Allows patched Google apps to run without root and under a different package name " +
"by using GmsCore instead of Google Play Services.",
) {
val gmsCoreVendorGroupIdOption = stringOption(
key = "gmsCoreVendorGroupId",

View File

@ -14,7 +14,7 @@ import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertLiteralOverride
import app.revanced.util.insertFeatureFlagBooleanOverride
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@ -235,7 +235,7 @@ fun spoofVideoStreamsPatch(
// region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertLiteralOverride(
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
)
@ -245,21 +245,21 @@ fun spoofVideoStreamsPatch(
// region turn off stream config replacement feature flag.
if (fixMediaFetchHotConfigChanges()) {
mediaFetchHotConfigFingerprint.method.insertLiteralOverride(
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
)
}
if (fixMediaFetchHotConfigAlternativeChanges()) {
mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride(
mediaFetchHotConfigAlternativeFingerprint.method.insertFeatureFlagBooleanOverride(
MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
)
}
if (fixParsePlaybackResponseFeatureFlag()) {
playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride(
playbackStartDescriptorFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
)

View File

@ -0,0 +1,82 @@
package app.revanced.patches.spotify.layout.theme
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
internal val customThemeByteCodePatch = bytecodePatch {
dependsOn(sharedExtensionPatch)
val backgroundColor by spotifyBackgroundColor
val backgroundColorSecondary by spotifyBackgroundColorSecondary
execute {
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
// Bytecode changes are not needed for legacy app target.
// Player background color is changed with existing resource patch.
return@execute
}
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
val index = indexOfFirstLiteralInstructionOrThrow(literal)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
const-string v$register, "$colorString"
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
move-result-wide v$register
"""
)
}
val encoreColorsClassName = with(encoreThemeFingerprint) {
// Find index of the first static get found after the string constant.
val encoreColorsFieldReferenceIndex = originalMethod.indexOfFirstInstructionOrThrow(
stringMatches!!.first().index,
Opcode.SGET_OBJECT
)
originalMethod.getInstruction(encoreColorsFieldReferenceIndex)
.getReference<FieldReference>()!!.definingClass
}
val encoreColorsConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { method, classDef ->
classDef.type == encoreColorsClassName &&
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
}
}
encoreColorsConstructorFingerprint.method.apply {
// Playlist song list background color.
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
// Share menu background color.
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
}
homeCategoryPillColorsFingerprint.method.apply {
// Home category pills background color.
addColorChangeInstructions(HOME_CATEGORY_PILL_COLOR_LITERAL, backgroundColorSecondary!!)
}
settingsHeaderColorFingerprint.method.apply {
// Settings header background color.
addColorChangeInstructions(SETTINGS_HEADER_COLOR_LITERAL, backgroundColorSecondary!!)
}
}
}

View File

@ -1,133 +1,8 @@
package app.revanced.patches.spotify.layout.theme
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import org.w3c.dom.Element
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
internal val spotifyBackgroundColor = stringOption(
key = "backgroundColor",
default = "@android:color/black",
title = "Primary background color",
description = "The background color. Can be a hex color or a resource reference.",
required = true,
)
internal val overridePlayerGradientColor = booleanOption(
key = "overridePlayerGradientColor",
default = false,
title = "Override player gradient color",
description = "Apply primary background color to the player gradient color, which changes dynamically with the song.",
required = false
)
internal val spotifyBackgroundColorSecondary = stringOption(
key = "backgroundColorSecondary",
default = "#FF121212",
title = "Secondary background color",
description =
"The secondary background color. (e.g. playlist list in home, player artist, song credits). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColor = stringOption(
key = "accentColor",
default = "#FF1ED760",
title = "Accent color",
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColorPressed = stringOption(
key = "accentColorPressed",
default = "#FF169C46",
title = "Pressed dark theme accent color",
description =
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
required = true,
)
private val customThemeBytecodePatch = bytecodePatch {
dependsOn(sharedExtensionPatch)
execute {
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
// Bytecode changes are not needed for legacy app target.
// Player background color is changed with existing resource patch.
return@execute
}
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
val index = indexOfFirstLiteralInstructionOrThrow(literal)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
const-string v$register, "$colorString"
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
move-result-wide v$register
"""
)
}
val encoreColorsClassName = with(encoreThemeFingerprint.originalMethod) {
// "Encore" colors are referenced right before the value of POSITIVE_INFINITY is returned.
// Begin the instruction find using the index of where POSITIVE_INFINITY is set into the register.
val positiveInfinityIndex = indexOfFirstLiteralInstructionOrThrow(
Float.POSITIVE_INFINITY
)
val encoreColorsFieldReferenceIndex = indexOfFirstInstructionReversedOrThrow(
positiveInfinityIndex,
Opcode.SGET_OBJECT
)
getInstruction(encoreColorsFieldReferenceIndex)
.getReference<FieldReference>()!!.definingClass
}
val encoreColorsConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { method, classDef ->
classDef.type == encoreColorsClassName &&
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
}
}
val backgroundColor by spotifyBackgroundColor
val backgroundColorSecondary by spotifyBackgroundColorSecondary
encoreColorsConstructorFingerprint.method.apply {
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
}
homeCategoryPillColorsFingerprint.method.addColorChangeInstructions(
HOME_CATEGORY_PILL_COLOR_LITERAL,
backgroundColorSecondary!!
)
settingsHeaderColorFingerprint.method.addColorChangeInstructions(
SETTINGS_HEADER_COLOR_LITERAL,
backgroundColorSecondary!!
)
}
}
@Suppress("unused")
val customThemePatch = resourcePatch(
name = "Custom theme",
@ -136,10 +11,9 @@ val customThemePatch = resourcePatch(
) {
compatibleWith("com.spotify.music")
dependsOn(customThemeBytecodePatch)
dependsOn(customThemeByteCodePatch)
val backgroundColor by spotifyBackgroundColor()
val overridePlayerGradientColor by overridePlayerGradientColor()
val backgroundColorSecondary by spotifyBackgroundColorSecondary()
val accentColor by spotifyAccentColor()
val accentColorPressed by spotifyAccentColorPressed()
@ -151,39 +25,31 @@ val customThemePatch = resourcePatch(
val childNodes = resourcesNode.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i) as? Element ?: continue
val name = node.getAttribute("name")
// Skip overriding song/player gradient start color if the option is disabled.
// Gradient end color should be themed regardless to allow the gradient to connect with
// our primary background color.
if (name == "bg_gradient_start_color" && !overridePlayerGradientColor!!) {
continue
}
node.textContent = when (name) {
// Gradient next to user photo and "All" in home page.
node.textContent = when (node.getAttribute("name")) {
// Gradient next to user photo and "All" in home page
"dark_base_background_base",
// Main background.
// Main background
"gray_7",
// Left sidebar background in tablet mode.
// Left sidebar background in tablet mode
"gray_10",
// "Add account", "Settings and privacy", "View Profile" left sidebar background.
// Add account, Settings and privacy, View Profile left sidebar background
"dark_base_background_elevated_base",
// Song/player gradient start/end color.
// Song/player background
"bg_gradient_start_color", "bg_gradient_end_color",
// Login screen background and gradient start.
"sthlm_blk", "sthlm_blk_grad_start",
// Misc.
// Login screen
"sthlm_blk", "sthlm_blk_grad_start", "stockholm_black",
// Misc
"image_placeholder_color",
-> backgroundColor
// Track credits, merch background in song player.
// Track credits, merch in song player
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
// Playlist list background in home page.
// Playlist list background in home page
"opacity_white_10",
// "About the artist" background in song player.
// About artist background in song player
"gray_15",
// "What's New" pills background.
// What's New pills background
"dark_base_background_tinted_highlight"
-> backgroundColorSecondary
@ -193,13 +59,5 @@ val customThemePatch = resourcePatch(
}
}
}
// Login screen gradient.
document("res/drawable/start_screen_gradient.xml").use { document ->
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
gradientNode.setAttribute("android:startColor", backgroundColor)
gradientNode.setAttribute("android:endColor", backgroundColor)
}
}
}

View File

@ -6,15 +6,12 @@ import com.android.tools.smali.dexlib2.AccessFlags
internal val encoreThemeFingerprint = fingerprint {
strings("Encore theme was not provided.") // Partial string match.
custom { method, _ ->
method.name == "invoke"
}
}
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
internal val homeCategoryPillColorsFingerprint = fingerprint{
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)

View File

@ -0,0 +1,36 @@
package app.revanced.patches.spotify.layout.theme
import app.revanced.patcher.patch.stringOption
internal val spotifyBackgroundColor = stringOption(
key = "backgroundColor",
default = "@android:color/black",
title = "Primary background color",
description = "The background color. Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyBackgroundColorSecondary = stringOption(
key = "backgroundColorSecondary",
default = "#FF121212",
title = "Secondary background color",
description = "The secondary background color. (e.g. playlist list, player arist, credits). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColor = stringOption(
key = "accentColor",
default = "#FF1ED760",
title = "Accent color",
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColorPressed = stringOption(
key = "accentColorPressed",
default = "#FF169C46",
title = "Pressed dark theme accent color",
description =
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
required = true,
)

View File

@ -49,7 +49,7 @@ val unlockPremiumPatch = bytecodePatch(
}
// Override the attributes map in the getter method.
productStateProtoFingerprint.method.apply {
with(productStateProtoFingerprint.method) {
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
@ -62,11 +62,10 @@ val unlockPremiumPatch = bytecodePatch(
// Add the query parameter trackRows to show popular tracks in the artist page.
buildQueryParametersFingerprint.apply {
with(buildQueryParametersFingerprint) {
val addQueryParameterConditionIndex = method.indexOfFirstInstructionReversedOrThrow(
stringMatches!!.first().index, Opcode.IF_EQZ
)
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
}
@ -115,19 +114,17 @@ val unlockPremiumPatch = bytecodePatch(
// Disable the "Spotify Premium" upsell experiment in context menus.
contextMenuExperimentsFingerprint.apply {
with(contextMenuExperimentsFingerprint) {
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
stringMatches!!.first().index, Opcode.MOVE_RESULT
)
val isUpsellEnabledRegister = method.getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
method.replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
}
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
homeSectionFingerprint.classDef.fields.first { it.name == "featureTypeCase_" }.apply {
// Add public flag and remove private.
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
}
@ -146,7 +143,7 @@ val unlockPremiumPatch = bytecodePatch(
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
// Forcing that always on breaks unrelated code in strange ways.
// Instead, remove the method call that checks if the list is unmodifiable.
protobufListRemoveFingerprint.method.apply {
with(protobufListRemoveFingerprint.method) {
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
@ -158,7 +155,7 @@ val unlockPremiumPatch = bytecodePatch(
}
// Remove ads sections from home.
homeStructureFingerprint.method.apply {
with(homeStructureFingerprint.method) {
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA

View File

@ -2,8 +2,4 @@ package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.fingerprint
internal val getPackageInfoFingerprint = fingerprint {
strings(
"Failed to get the application signatures"
)
}
internal val getAppSignatureFingerprint = fingerprint { strings("Failed to get the application signatures") }

View File

@ -1,63 +0,0 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Suppress("unused")
val spoofPackageInfoPatch = bytecodePatch(
name = "Spoof package info",
description = "Spoofs the package info of the app to fix various functions of the app.",
) {
compatibleWith("com.spotify.music")
execute {
getPackageInfoFingerprint.method.apply {
// region Spoof signature.
val failedToGetSignaturesStringIndex =
getPackageInfoFingerprint.stringMatches!!.first().index
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringIndex,
Opcode.MOVE_RESULT_OBJECT,
)
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
// endregion
// region Spoof installer name.
val expectedInstallerName = "com.android.vending"
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
}.forEach { index ->
val returnObjectIndex = index + 1
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
returnObjectIndex
).registerA
addInstruction(
returnObjectIndex + 1,
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
)
}
// endregion
}
}
}

View File

@ -1,13 +1,33 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Deprecated("Superseded by spoofPackageInfoPatch", ReplaceWith("spoofPackageInfoPatch"))
@Suppress("unused")
val spoofSignaturePatch = bytecodePatch(
description = "Spoofs the signature of the app fix various functions of the app.",
name = "Spoof signature",
description = "Spoofs the signature of the app to fix various functions of the app.",
) {
compatibleWith("com.spotify.music")
dependsOn(spoofPackageInfoPatch)
execute {
getAppSignatureFingerprint.method.apply {
val failedToGetSignaturesStringMatch = getAppSignatureFingerprint.stringMatches!!.first()
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringMatch.index,
Opcode.MOVE_RESULT_OBJECT,
)
val register = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$register, \"$expectedSignature\"")
}
}
}

View File

@ -12,7 +12,7 @@ val dynamicColorPatch = resourcePatch(
) {
compatibleWith(
"com.twitter.android"(
"10.86.0-release.0",
"10.84.0-release.0",
"10.60.0-release.0",
"10.48.0-release.0"
)

View File

@ -13,8 +13,8 @@ fun hookPatch(
compatibleWith(
"com.twitter.android"(
// Only v10.85 uses Pairip and requires additional changes to work.
"10.86.0-release.0",
// 10.85+ uses Pairip and requires additional changes to work.
"10.84.0-release.0",
// Confirmed to not show reply ads. Slightly newer versions may also work.
"10.60.0-release.0",
"10.48.0-release.0"

View File

@ -39,7 +39,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
compatibleWith(
"com.twitter.android"(
"10.86.0-release.0",
"10.84.0-release.0",
"10.60.0-release.0",
"10.48.0-release.0"
)

View File

@ -10,7 +10,7 @@ val sanitizeSharingLinksPatch = bytecodePatch(
) {
compatibleWith(
"com.twitter.android"(
"10.86.0-release.0",
"10.84.0-release.0",
"10.60.0-release.0",
"10.48.0-release.0"
)

View File

@ -84,8 +84,7 @@ val hideAdsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -31,8 +31,7 @@ val hideGetPremiumPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -29,8 +29,7 @@ val videoAdsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -59,8 +59,7 @@ val copyVideoUrlPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -30,8 +30,7 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
val extensionMethodDescriptor =

View File

@ -74,8 +74,7 @@ val downloadsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -10,15 +10,10 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findFreeRegister
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;"
val enableSeekbarTappingPatch = bytecodePatch(
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
) {
@ -36,37 +31,39 @@ val enableSeekbarTappingPatch = bytecodePatch(
)
// Find the required methods to tap the seekbar.
val seekbarTappingMethods = onTouchEventHandlerFingerprint.let {
fun getMethodReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
.reference as MethodReference
val patternMatch = onTouchEventHandlerFingerprint.patternMatch!!
listOf(
getMethodReference(it.patternMatch!!.startIndex),
getMethodReference(it.patternMatch!!.endIndex)
)
fun getReference(index: Int) = onTouchEventHandlerFingerprint.method.getInstruction<ReferenceInstruction>(index)
.reference as MethodReference
val seekbarTappingMethods = buildMap {
put("N", getReference(patternMatch.startIndex))
put("O", getReference(patternMatch.endIndex))
}
val insertIndex = seekbarTappingFingerprint.patternMatch!!.endIndex - 1
seekbarTappingFingerprint.method.apply {
val pointIndex = indexOfNewPointInstruction(this)
val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL)
val insertIndex = invokeIndex + 1
val thisInstanceRegister = getInstruction<Instruction35c>(insertIndex - 1).registerC
val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(invokeIndex).registerC
val xAxisRegister = this.getInstruction<FiveRegisterInstruction>(pointIndex).registerD
val freeRegister = findFreeRegister(insertIndex, thisInstanceRegister, xAxisRegister)
val freeRegister = 0
val xAxisRegister = 2
val oMethod = seekbarTappingMethods[0]
val nMethod = seekbarTappingMethods[1]
val oMethod = seekbarTappingMethods["O"]!!
val nMethod = seekbarTappingMethods["N"]!!
fun MethodReference.toInvokeInstructionString() =
"invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $this"
addInstructionsWithLabels(
insertIndex,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z
move-result v$freeRegister
if-eqz v$freeRegister, :disabled
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod
""",
invoke-static { }, Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;->seekbarTappingEnabled()Z
move-result v$freeRegister
if-eqz v$freeRegister, :disabled
${oMethod.toInvokeInstructionString()}
${nMethod.toInvokeInstructionString()}
""",
ExternalLabel("disabled", getInstruction(insertIndex)),
)
}

View File

@ -1,15 +1,11 @@
package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionReversed
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
internal val swipingUpGestureParentFingerprint = fingerprint {
@ -105,17 +101,14 @@ internal val seekbarTappingFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
parameters("L")
custom { method, _ ->
method.name == "onTouchEvent"
&& method.containsLiteralInstruction(Integer.MAX_VALUE.toLong())
&& indexOfNewPointInstruction(method) >= 0
}
}
internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/graphics/Point;"
&& reference.name == "<init>"
opcodes(
Opcode.IPUT_OBJECT,
Opcode.INVOKE_VIRTUAL,
// Insert seekbar tapping instructions here.
Opcode.RETURN,
Opcode.INVOKE_VIRTUAL,
)
literal { Integer.MAX_VALUE.toLong() }
}
internal val slideToSeekFingerprint = fingerprint {

View File

@ -26,7 +26,6 @@ val seekbarPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
)
}

View File

@ -6,7 +6,6 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
@ -43,16 +42,11 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
ListPreference(
"revanced_swipe_overlay_style",
summaryKey = null,
),
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_progress_color", inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),
)
copyResources(
@ -92,8 +86,7 @@ val swipeControlsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -124,7 +117,7 @@ val swipeControlsPatch = bytecodePatch(
// region patch to enable/disable swipe to change video.
if (is_19_43_or_greater) {
swipeChangeVideoFingerprint.method.insertLiteralOverride(
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride(
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
)

View File

@ -8,9 +8,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint
val autoCaptionsPatch = bytecodePatch(
name = "Disable auto captions",
@ -30,41 +28,42 @@ val autoCaptionsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
addResources("youtube", "layout.autocaptions.autoCaptionsPatch")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_disable_auto_captions"),
)
subtitleTrackFingerprint.method.addInstructions(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableAutoCaptions()Z
move-result v0
if-eqz v0, :auto_captions_enabled
const/4 v0, 0x1
return v0
:auto_captions_enabled
nop
"""
SwitchPreference("revanced_auto_captions"),
)
mapOf(
startVideoInformerFingerprint to 0,
storyboardRendererDecoderRecommendedLevelFingerprint to 1
subtitleButtonControllerFingerprint to 1,
).forEach { (fingerprint, enabled) ->
fingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x$enabled
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setCaptionsButtonStatus(Z)V
"""
sput-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z
""",
)
}
subtitleTrackFingerprint.method.addInstructions(
0,
"""
invoke-static {}, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->autoCaptionsEnabled()Z
move-result v0
if-eqz v0, :auto_captions_enabled
sget-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z
if-nez v0, :auto_captions_enabled
const/4 v0, 0x1
return v0
:auto_captions_enabled
nop
""",
)
}
}

View File

@ -14,13 +14,6 @@ internal val startVideoInformerFingerprint = fingerprint {
strings("pc")
}
internal val storyboardRendererDecoderRecommendedLevelFingerprint = fingerprint {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
strings("#-1#")
}
internal val subtitleTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
@ -35,6 +28,6 @@ internal val subtitleTrackFingerprint = fingerprint {
)
strings("DISABLE_CAPTIONS_OPTION")
custom { _, classDef ->
classDef.endsWith("/SubtitleTrack;")
classDef.endsWith("SubtitleTrack;")
}
}

View File

@ -49,8 +49,7 @@ val customBrandingPatch = resourcePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
val appName by stringOption(

View File

@ -47,7 +47,6 @@ val changeHeaderPatch = resourcePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
)

View File

@ -28,8 +28,7 @@ val hideButtonsPatch = resourcePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -46,11 +45,10 @@ val hideButtonsPatch = resourcePatch(
SwitchPreference("revanced_hide_remix_button"),
SwitchPreference("revanced_hide_download_button"),
SwitchPreference("revanced_hide_thanks_button"),
SwitchPreference("revanced_hide_ask_button"),
SwitchPreference("revanced_hide_clip_button"),
SwitchPreference("revanced_hide_playlist_button"),
)
)
),
),
)
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")

View File

@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertLiteralOverride
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -46,8 +46,7 @@ val navigationButtonsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -120,17 +119,17 @@ val navigationButtonsPatch = bytecodePatch(
// Force on/off translucent effect on status bar and navigation buttons.
if (is_19_25_or_greater) {
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertLiteralOverride(
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
)
translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride(
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
)
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride(
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
)

View File

@ -60,8 +60,7 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -39,8 +39,7 @@ val changeFormFactorPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -65,8 +65,7 @@ val hideEndscreenCardsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -37,8 +37,7 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -35,8 +35,7 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -1,7 +1,6 @@
package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@ -16,23 +15,10 @@ internal val hideShowMoreButtonFingerprint = fingerprint {
literal { expandButtonDownId }
}
/**
* 20.12+
*/
internal val parseElementFromBufferFingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L")
opcodes(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
)
strings("Failed to parse Element") // String is a partial match.
}
/**
* 20.07+
*/
internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint {
internal val parseElementFromBufferFingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L")
opcodes(
Opcode.IGET_OBJECT,
@ -43,10 +29,7 @@ internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint {
strings("Failed to parse Element") // String is a partial match.
}
/**
* 19.01 - 20.06
*/
internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint {
internal val parseElementFromBufferLegacyFingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L")
opcodes(
Opcode.IGET_OBJECT,
@ -68,9 +51,6 @@ internal val showWatermarkFingerprint = fingerprint {
parameters("L", "L")
}
/**
* Matches same method as [wideSearchbarLayoutFingerprint].
*/
internal val yoodlesImageViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")

View File

@ -21,7 +21,6 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findFreeRegister
@ -44,12 +43,14 @@ var crowdfundingBoxId = -1L
private set
var youTubeLogo = -1L
private set
var filterBarHeightId = -1L
private set
var relatedChipCloudMarginId = -1L
private set
var barContainerHeightId = -1L
private set
var fabButtonId = -1L
private set
@ -131,8 +132,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -247,9 +247,8 @@ val hideLayoutComponentsPatch = bytecodePatch(
// region Mix playlists
(if (is_20_09_or_greater) parseElementFromBufferFingerprint
else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
else parseElementFromBufferLegacy1901Fingerprint).let {
(if (is_20_07_or_greater) parseElementFromBufferFingerprint
else parseElementFromBufferLegacyFingerprint).let {
it.method.apply {
val byteArrayParameter = "p3"
val startIndex = it.patternMatch!!.startIndex

View File

@ -63,8 +63,7 @@ val hideInfoCardsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -30,8 +30,7 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -35,8 +35,7 @@ val disableRollingNumberAnimationPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -178,8 +178,7 @@ val hideShortsComponentsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
hideShortsAppShortcutOption()

View File

@ -1,16 +1,24 @@
package app.revanced.patches.youtube.layout.hide.time
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import app.revanced.patcher.fingerprint
internal val timeCounterFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
internal val timeCounterFingerprint = fingerprint(
fuzzyPatternScanThreshold = 1,
) {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters()
opcodes(
Opcode.SUB_LONG_2ADDR,
Opcode.IGET_WIDE,
Opcode.SUB_LONG_2ADDR,
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_WIDE,
Opcode.IGET_WIDE,
Opcode.SUB_LONG_2ADDR
)
}

View File

@ -27,8 +27,7 @@ val hideTimestampPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -30,7 +30,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstructio
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@ -173,8 +172,7 @@ val miniplayerPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -280,7 +278,7 @@ val miniplayerPatch = bytecodePatch(
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
literal: Long,
extensionMethod: String,
) = method.insertLiteralOverride(
) = method.insertFeatureFlagBooleanOverride(
literal,
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
)
@ -348,12 +346,7 @@ val miniplayerPatch = bytecodePatch(
// endregion
// region Legacy tablet miniplayer hooks.
val appNameStringIndex = miniplayerOverrideFingerprint.let {
it.method.indexOfFirstInstructionOrThrow(it.stringMatches!!.first().index) {
val reference = getReference<MethodReference>()
reference?.parameterTypes?.firstOrNull() == "Landroid/content/Context;"
}
}
val appNameStringIndex = miniplayerOverrideFingerprint.stringMatches!!.first().index + 2
navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
}

View File

@ -27,8 +27,7 @@ val playerPopupPanelsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -18,8 +18,7 @@ val playerControlsBackgroundPatch = resourcePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -27,7 +27,6 @@ internal val exitFullscreenPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
)

View File

@ -5,7 +5,7 @@ import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlaye
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.insertLiteralOverride
import app.revanced.util.insertFeatureFlagBooleanOverride
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
@ -24,7 +24,7 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
return@execute
}
openVideosFullscreenPortraitFingerprint.method.insertLiteralOverride(
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
)

View File

@ -27,7 +27,6 @@ val openVideosFullscreenPatch = bytecodePatch(
"com.google.android.youtube"(
"19.47.53",
"20.07.39",
"20.12.46",
)
)

View File

@ -58,8 +58,7 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -1,7 +1,6 @@
package app.revanced.patches.youtube.layout.returnyoutubedislike
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@ -122,12 +121,3 @@ internal val textComponentLookupFingerprint = fingerprint {
parameters("L")
strings("")
}
internal const val LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG = 45675738L
internal val textComponentFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.FINAL)
returns("Z")
parameters()
literal { LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG }
}

View File

@ -12,7 +12,6 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.addSettingPreference
@ -60,8 +59,7 @@ val returnYouTubeDislikePatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -176,14 +174,6 @@ val returnYouTubeDislikePatch = bytecodePatch(
// Filter that parses the video id from the UI
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
if (is_20_07_or_greater) {
// Turn off a/b flag that enables new code for creating litho spans.
// If enabled then the litho text span hook is never called.
// Target code is very obfuscated and exactly what the code does is not clear.
// Return late so debug patch logs if the flag is enabled.
textComponentFeatureFlagFingerprint.method.returnLate(false)
}
// Player response video id is needed to search for the video ids in Shorts litho components.
hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")

View File

@ -1,27 +1,31 @@
package app.revanced.patches.youtube.layout.searchbar
import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewFingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val createSearchSuggestionsFingerprint = fingerprint {
opcodes(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
)
strings("ss_rds")
}
internal val setWordmarkHeaderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Landroid/widget/ImageView;")
custom { methodDef, _ ->
methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) &&
methodDef.containsLiteralInstruction(ytWordmarkHeaderId)
}
}
/**
* Matches the same method as [yoodlesImageViewFingerprint].
*/
internal val wideSearchbarLayoutFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters("L", "L")
literal { actionBarRingoId }
opcodes(
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.CONST,
null, // invoke-static or invoke-virtual.
)
}

View File

@ -1,67 +1,30 @@
package app.revanced.patches.youtube.layout.searchbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;"
internal var ytWordmarkHeaderId = -1L
private set
internal var ytPremiumWordmarkHeaderId = -1L
private set
internal var actionBarRingoId = -1L
private set
private val wideSearchbarResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
ytWordmarkHeaderId = resourceMappings[
"attr",
"ytWordmarkHeader",
]
ytPremiumWordmarkHeaderId = resourceMappings[
"attr",
"ytPremiumWordmarkHeader",
]
actionBarRingoId = resourceMappings[
"layout",
"action_bar_ringo",
]
}
}
val wideSearchbarPatch = bytecodePatch(
name = "Wide search bar",
description = "Adds an option to replace the search icon with a wide search bar. " +
"This will hide the YouTube logo when active.",
description = "Adds an option to replace the search icon with a wide search bar. This will hide the YouTube logo when active.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
wideSearchbarResourcePatch,
)
compatibleWith(
@ -72,8 +35,7 @@ val wideSearchbarPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -83,45 +45,37 @@ val wideSearchbarPatch = bytecodePatch(
SwitchPreference("revanced_wide_searchbar"),
)
setWordmarkHeaderFingerprint.let {
// Navigate to the method that checks if the YT logo is shown beside the search bar.
val shouldShowLogoMethod = with(it.originalMethod) {
val invokeStaticIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.returnType == "Z"
}
navigate(this).to(invokeStaticIndex).stop()
}
/**
* Navigate a fingerprints method at a given index mutably.
*
* @param index The index to navigate to.
* @param from The fingerprint to navigate the method on.
* @return The [MutableMethod] which was navigated on.
*/
fun BytecodePatchContext.walkMutable(index: Int, from: Fingerprint) =
navigate(from.originalMethod).to(index).stop()
shouldShowLogoMethod.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
/**
* Injects instructions required for certain methods.
*/
fun MutableMethod.injectSearchBarHook() {
val insertIndex = implementation!!.instructions.size - 1
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructionsAtControlFlowLabel(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z
move-result v$register
"""
)
}
}
addInstructions(
insertIndex,
"""
invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z
move-result v$insertRegister
""",
)
}
// Fix missing left padding when using wide searchbar.
wideSearchbarLayoutFingerprint.method.apply {
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/view/LayoutInflater;"
&& reference.name == "inflate"
}.forEach { inflateIndex ->
val register = getInstruction<OneRegisterInstruction>(inflateIndex + 1).registerA
addInstruction(
inflateIndex + 2,
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V"
)
}
mapOf(
setWordmarkHeaderFingerprint to 1,
createSearchSuggestionsFingerprint to createSearchSuggestionsFingerprint.patternMatch!!.startIndex,
).forEach { (fingerprint, callIndex) ->
walkMutable(callIndex, fingerprint).injectSearchBarHook()
}
}
}

View File

@ -28,8 +28,9 @@ import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.inputStreamFromBundledResource
import app.revanced.util.insertLiteralOverride
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@ -228,9 +229,16 @@ val seekbarColorPatch = bytecodePatch(
execute {
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
insertLiteralOverride(
resourceId,
"$EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I"
val index = indexOfFirstLiteralInstructionOrThrow(resourceId)
val insertIndex = indexOfFirstInstructionOrThrow(index, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
move-result v$register
"""
)
}
@ -346,7 +354,7 @@ val seekbarColorPatch = bytecodePatch(
launchScreenLayoutTypeFingerprint,
mainActivityOnCreateFingerprint
).forEach { fingerprint ->
fingerprint.method.insertLiteralOverride(
fingerprint.method.insertFeatureFlagBooleanOverride(
launchScreenLayoutTypeLotteFeatureFlag,
"$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z"
)

View File

@ -39,8 +39,7 @@ val shortsAutoplayPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -47,8 +47,7 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -20,6 +20,12 @@ internal val appendTimeFingerprint = fingerprint {
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID,
)
}

View File

@ -114,8 +114,7 @@ val sponsorBlockPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -64,8 +64,7 @@ val spoofAppVersionPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -7,9 +7,6 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
@ -38,26 +35,17 @@ val changeStartPagePatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
addResources("youtube", "layout.startpage.changeStartPagePatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
PreferenceCategory(
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = setOf(
ListPreference(
key = "revanced_change_start_page",
summaryKey = null,
),
SwitchPreference("revanced_change_start_page_always")
)
)
ListPreference(
key = "revanced_change_start_page",
summaryKey = null,
),
)
// Hook browseId.

View File

@ -40,8 +40,7 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -10,6 +10,13 @@ internal val lithoThemeFingerprint = fingerprint {
returns("V")
parameters("Landroid/graphics/Rect;")
opcodes(
Opcode.APUT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_OBJECT,
Opcode.IGET,
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,

View File

@ -21,7 +21,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.forEachChildElement
import app.revanced.util.insertLiteralOverride
import app.revanced.util.insertFeatureFlagBooleanOverride
import org.w3c.dom.Element
private const val EXTENSION_CLASS_DESCRIPTOR =
@ -223,8 +223,7 @@ val themePatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {
@ -234,7 +233,7 @@ val themePatch = bytecodePatch(
SwitchPreference("revanced_gradient_loading_screen"),
)
useGradientLoadingScreenFingerprint.method.insertLiteralOverride(
useGradientLoadingScreenFingerprint.method.insertFeatureFlagBooleanOverride(
GRADIENT_LOADING_SCREEN_AB_CONSTANT,
"$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z"
)

View File

@ -39,8 +39,7 @@ val alternativeThumbnailsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -33,8 +33,7 @@ val bypassImageRegionRestrictionsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

View File

@ -29,8 +29,7 @@ val announcementsPatch = bytecodePatch(
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
),
)
execute {

Some files were not shown because too many files have changed in this diff Show More