chore: Merge branch dev to main (#4803)

This commit is contained in:
LisoUseInAIKyrios 2025-04-25 21:17:55 +04:00 committed by GitHub
commit 6514420719
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
186 changed files with 3031 additions and 1329 deletions

View File

@ -1,3 +1,87 @@
# [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) ## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)

View File

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

View File

@ -0,0 +1 @@
<manifest/>

View File

@ -0,0 +1,28 @@
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.connectivity.wifi.spoof; package app.revanced.extension.all.misc.connectivity.wifi.spoof;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;

View File

@ -1,4 +1,4 @@
package app.revanced.extension.all.screencapture.removerestriction; package app.revanced.extension.all.misc.screencapture.removerestriction;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.os.Build; import android.os.Build;

View File

@ -1,4 +1,4 @@
package app.revanced.extension.all.screenshot.removerestriction; package app.revanced.extension.all.misc.screenshot.removerestriction;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,48 @@
package app.revanced.extension.youtube.patches; 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; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class WideSearchbarPatch { public final class WideSearchbarPatch {
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
/**
* Injection point.
*/
public static boolean enableWideSearchbar(boolean original) { public static boolean enableWideSearchbar(boolean original) {
return Settings.WIDE_SEARCHBAR.get() || 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);
}
} }
} }

View File

@ -31,7 +31,7 @@ final class ButtonsFilter extends Filter {
bufferFilterPathGroup = new StringFilterGroup( bufferFilterPathGroup = new StringFilterGroup(
null, null,
"|ContainerType|button.eml|" "|ContainerType|button.eml"
); );
addPathCallbacks( addPathCallbacks(
@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_DOWNLOAD_BUTTON, Settings.HIDE_DOWNLOAD_BUTTON,
"|download_button.eml|" "|download_button.eml"
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_PLAYLIST_BUTTON, Settings.HIDE_PLAYLIST_BUTTON,
@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|" "|clip_button.eml"
) )
); );
@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
Settings.HIDE_REMIX_BUTTON, Settings.HIDE_REMIX_BUTTON,
"yt_outline_youtube_shorts_plus" "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, // 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' // as there's a chance the path is a generic action button and won't contain 'clip_button'
new ByteArrayFilterGroup( new ByteArrayFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"yt_outline_scissors" "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( compactChannelBarInnerButton = new StringFilterGroup(
null, null,
"|button.eml|" "|button.eml"
); );
joinMembershipButton = new ByteArrayFilterGroup( joinMembershipButton = new ByteArrayFilterGroup(

View File

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

View File

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

View File

@ -7,6 +7,7 @@ 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.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny; 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.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.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode; import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability; import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
@ -24,6 +25,7 @@ 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.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
import android.graphics.Color; import android.graphics.Color;
@ -124,6 +126,7 @@ public class Settings extends BaseSettings {
// Player // Player
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE); 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 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_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 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); public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
@ -196,6 +199,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE); 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_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_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 // 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_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); public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
@ -221,6 +225,8 @@ 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 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 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 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)); 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 // Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE); public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
@ -294,7 +300,6 @@ public class Settings extends BaseSettings {
// Misc // Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE); 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 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 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 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); public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
@ -320,12 +325,14 @@ public class Settings extends BaseSettings {
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true, public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME)); public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true, public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true, public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true, public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); 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, public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); 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)); public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
@ -403,6 +410,7 @@ 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 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_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_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 { static {
// region Migration // region Migration
@ -456,6 +464,11 @@ public class Settings extends BaseSettings {
SPOOF_APP_VERSION_TARGET.resetToDefault(); SPOOF_APP_VERSION_TARGET.resetToDefault();
} }
if (!DEPRECATED_AUTO_CAPTIONS.isSetToDefault()) {
DISABLE_AUTO_CAPTIONS.save(true);
DEPRECATED_AUTO_CAPTIONS.resetToDefault();
}
// endregion // endregion
// region SB import/export callbacks // region SB import/export callbacks

View File

@ -1,95 +1,98 @@
package app.revanced.extension.youtube.swipecontrols package app.revanced.extension.youtube.swipecontrols
import android.annotation.SuppressLint
import android.graphics.Color import android.graphics.Color
import app.revanced.extension.shared.Logger
import app.revanced.extension.shared.StringRef.str import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.settings.Settings import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType import app.revanced.extension.youtube.shared.PlayerType
/** /**
* provider for configuration for volume and brightness swipe controls * Provides configuration settings for volume and brightness swipe controls in the YouTube player.
* Manages enabling/disabling gestures, overlay appearance, and behavior preferences.
*/ */
class SwipeControlsConfigurationProvider { class SwipeControlsConfigurationProvider {
//region swipe enable //region swipe enable
/** /**
* should swipe controls be enabled? (global setting) * Indicates whether swipe controls are enabled globally.
* Returns true if either volume or brightness controls are enabled and the video is in fullscreen mode.
*/ */
val enableSwipeControls: Boolean val enableSwipeControls: Boolean
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
/** /**
* should swipe controls for volume be enabled? * Indicates whether swipe controls for adjusting volume are enabled.
*/ */
val enableVolumeControls = Settings.SWIPE_VOLUME.get() val enableVolumeControls = Settings.SWIPE_VOLUME.get()
/** /**
* should swipe controls for volume be enabled? * Indicates whether swipe controls for adjusting brightness are enabled.
*/ */
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get() val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
/** /**
* is the video player currently in fullscreen mode? * Checks if the video player is currently in fullscreen mode.
*/ */
private val isFullscreenVideo: Boolean private val isFullscreenVideo: Boolean
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
//endregion //endregion
//region keys enable //region keys enable
/** /**
* should volume key controls be overwritten? (global setting) * 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.
*/ */
val overwriteVolumeKeyControls: Boolean val overwriteVolumeKeyControls: Boolean
get() = enableVolumeControls && isFullscreenVideo get() = enableVolumeControls && isFullscreenVideo
//endregion //endregion
//region gesture adjustments //region gesture adjustments
/** /**
* should press-to-swipe be enabled? * Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
*/ */
val shouldEnablePressToSwipe: Boolean val shouldEnablePressToSwipe: Boolean
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get() get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
/** /**
* threshold for swipe detection * The threshold for detecting swipe gestures, in pixels.
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant * Loaded once to ensure consistent behavior during rapid scroll events.
*/ */
val swipeMagnitudeThreshold: Int val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get() get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
/** /**
* How much volume will change by single swipe. * The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
* If it is set to 0, it will reset to the default value because 0 would disable swiping. * Resets to default if set to 0, as it would disable swiping.
* */ */
val volumeSwipeSensitivity: Int val volumeSwipeSensitivity: Int
get() { get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get() val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) { if (sensitivity < 1) {
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault() return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
} }
return sensitivity return sensitivity
} }
//endregion //endregion
//region overlay adjustments //region overlay adjustments
/** /**
* should the overlay enable haptic feedback? * Indicates whether haptic feedback should be enabled for swipe control interactions.
*/ */
val shouldEnableHapticFeedback: Boolean val shouldEnableHapticFeedback: Boolean
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get() get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
/** /**
* how long the overlay should be shown on changes * The duration in milliseconds that the overlay should remain visible after a change.
*/ */
val overlayShowTimeoutMillis: Long val overlayShowTimeoutMillis: Long
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get() get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/** /**
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency. * The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
* If the opacity value is out of range, it resets to the default and displays a warning message. * Resets to default and shows a toast if the value is out of range.
*/ */
val overlayBackgroundOpacity: Int val overlayBackgroundOpacity: Int
get() { get() {
@ -97,8 +100,7 @@ class SwipeControlsConfigurationProvider {
if (opacity < 0 || opacity > 100) { if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast")) Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault() opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
} }
opacity = opacity * 255 / 100 opacity = opacity * 255 / 100
@ -106,55 +108,125 @@ class SwipeControlsConfigurationProvider {
} }
/** /**
* The color of the progress overlay. * The color of the progress bar in the overlay.
* Resets to default and shows a toast if the color string is invalid or empty.
*/ */
val overlayProgressColor: Int val overlayProgressColor: Int
get() = 0xBFFFFFFF.toInt() 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.
}
}
/** /**
* The color used for the background of the progress overlay fill. * The background color used for the filled portion of the progress bar in the overlay.
*/ */
val overlayFillBackgroundPaint: Int val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt() get() = 0x80D3D3D3.toInt()
/** /**
* The color used for the text and icons in the overlay. * The color used for text and icons in the overlay.
*/ */
val overlayTextColor: Int val overlayTextColor: Int
get() = Color.WHITE get() = Color.WHITE
/** /**
* A flag that determines if the overlay should only show the icon. * 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.
*/ */
val overlayShowOverlayMinimalStyle: Boolean val overlayTextSize: Int
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get() 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
}
/** /**
* A flag that determines if the progress bar should be circular. * 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.
*/ */
val isCircularProgressBar: Boolean @Suppress("unused")
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get() enum class SwipeOverlayStyle(
//endregion 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,
//region behaviour /**
* 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)
}
/** /**
* should the brightness be saved and restored when exiting or entering fullscreen * 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.
*/ */
val shouldSaveAndRestoreBrightness: Boolean val shouldSaveAndRestoreBrightness: Boolean
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get() get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
/** /**
* should auto-brightness be enabled at the lowest value of the brightness gesture * Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
*/ */
val shouldLowestValueEnableAutoBrightness: Boolean val shouldLowestValueEnableAutoBrightness: Boolean
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get() get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
/** /**
* variable that stores the brightness gesture value in the settings * The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
*/ */
var savedScreenBrightnessValue: Float var savedScreenBrightnessValue: Float
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get() get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value) set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
//endregion //endregion
} }

View File

@ -23,9 +23,7 @@ import java.lang.ref.WeakReference
/** /**
* The main controller for volume and brightness swipe controls. * 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() { class SwipeControlsHostActivity : Activity() {
/** /**

View File

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

View File

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

View File

@ -2,6 +2,10 @@ public final class app/revanced/patches/all/misc/activity/exportall/ExportAllAct
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; 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 final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
} }
@ -1416,6 +1420,8 @@ 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_07_or_greater ()Z
public static final fun is_20_09_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_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 { public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {

View File

@ -0,0 +1,75 @@
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 import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch" "Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" 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 = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch" "Lapp/revanced/extension/all/misc/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @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 import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch" "Lapp/revanced/extension/all/misc/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @Suppress("unused")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@ 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.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch 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.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.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
@ -42,9 +43,13 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_haptic_feedback"), SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_save_and_restore_brightness"), SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"), SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
SwitchPreference("revanced_swipe_show_circular_overlay"), ListPreference(
SwitchPreference("revanced_swipe_overlay_minimal_style"), "revanced_swipe_overlay_style",
summaryKey = null,
),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER), 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_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER), TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),
@ -87,7 +92,8 @@ val swipeControlsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@ -8,7 +8,9 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
val autoCaptionsPatch = bytecodePatch( val autoCaptionsPatch = bytecodePatch(
name = "Disable auto captions", name = "Disable auto captions",
@ -28,42 +30,41 @@ val autoCaptionsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
addResources("youtube", "layout.autocaptions.autoCaptionsPatch") addResources("youtube", "layout.autocaptions.autoCaptionsPatch")
PreferenceScreen.PLAYER.addPreferences( PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_auto_captions"), 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
"""
) )
mapOf( mapOf(
startVideoInformerFingerprint to 0, startVideoInformerFingerprint to 0,
subtitleButtonControllerFingerprint to 1, storyboardRendererDecoderRecommendedLevelFingerprint to 1
).forEach { (fingerprint, enabled) -> ).forEach { (fingerprint, enabled) ->
fingerprint.method.addInstructions( fingerprint.method.addInstructions(
0, 0,
""" """
const/4 v0, 0x$enabled const/4 v0, 0x$enabled
sput-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setCaptionsButtonStatus(Z)V
""", """
) )
} }
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,6 +14,13 @@ internal val startVideoInformerFingerprint = fingerprint {
strings("pc") strings("pc")
} }
internal val storyboardRendererDecoderRecommendedLevelFingerprint = fingerprint {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
strings("#-1#")
}
internal val subtitleTrackFingerprint = fingerprint { internal val subtitleTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z") returns("Z")
@ -28,6 +35,6 @@ internal val subtitleTrackFingerprint = fingerprint {
) )
strings("DISABLE_CAPTIONS_OPTION") strings("DISABLE_CAPTIONS_OPTION")
custom { _, classDef -> custom { _, classDef ->
classDef.endsWith("SubtitleTrack;") classDef.endsWith("/SubtitleTrack;")
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@ 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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction 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.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.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@ -172,7 +173,8 @@ val miniplayerPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@ -346,7 +348,12 @@ val miniplayerPatch = bytecodePatch(
// endregion // endregion
// region Legacy tablet miniplayer hooks. // region Legacy tablet miniplayer hooks.
val appNameStringIndex = miniplayerOverrideFingerprint.stringMatches!!.first().index + 2 val appNameStringIndex = miniplayerOverrideFingerprint.let {
it.method.indexOfFirstInstructionOrThrow(it.stringMatches!!.first().index) {
val reference = getReference<MethodReference>()
reference?.parameterTypes?.firstOrNull() == "Landroid/content/Context;"
}
}
navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply { navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.returnyoutubedislike package app.revanced.patches.youtube.layout.returnyoutubedislike
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -121,3 +122,12 @@ internal val textComponentLookupFingerprint = fingerprint {
parameters("L") parameters("L")
strings("") 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,6 +12,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch 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_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.is_20_10_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.addSettingPreference import app.revanced.patches.youtube.misc.settings.addSettingPreference
@ -59,7 +60,8 @@ val returnYouTubeDislikePatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@ -174,6 +176,14 @@ val returnYouTubeDislikePatch = bytecodePatch(
// Filter that parses the video id from the UI // Filter that parses the video id from the UI
addLithoFilter(FILTER_CLASS_DESCRIPTOR) 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. // 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") hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,9 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch 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.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.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@ -35,17 +38,26 @@ val changeStartPagePatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
addResources("youtube", "layout.startpage.changeStartPagePatch") addResources("youtube", "layout.startpage.changeStartPagePatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences( PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference( PreferenceCategory(
key = "revanced_change_start_page", titleKey = null,
summaryKey = 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")
)
)
) )
// Hook browseId. // Hook browseId.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,9 +9,7 @@ internal val onBackPressedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
opcodes(Opcode.RETURN_VOID) opcodes(Opcode.RETURN_VOID)
custom { method, classDef -> custom { method, classDef ->
method.name == "onBackPressed" && method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
// Old versions of YouTube called this class "WatchWhileActivity" instead.
(classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;"))
} }
} }
@ -27,6 +25,9 @@ internal val scrollPositionFingerprint = fingerprint {
strings("scroll_position") strings("scroll_position")
} }
/**
* Resolves using class found in [recyclerViewTopScrollingParentFingerprint].
*/
internal val recyclerViewTopScrollingFingerprint = fingerprint { internal val recyclerViewTopScrollingFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V") returns("V")

View File

@ -41,7 +41,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
} }

View File

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

View File

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

View File

@ -47,11 +47,7 @@ internal val mainActivityOnBackPressedFingerprint = fingerprint {
returns("V") returns("V")
parameters() parameters()
custom { method, classDef -> custom { method, classDef ->
val matchesClass = classDef.endsWith("MainActivity;") || method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
// Old versions of YouTube called this class "WatchWhileActivity" instead.
classDef.endsWith("WatchWhileActivity;")
matchesClass && method.name == "onBackPressed"
} }
} }

View File

@ -58,6 +58,10 @@ var is_20_09_or_greater = false
private set private set
var is_20_10_or_greater = false var is_20_10_or_greater = false
private set private set
var is_20_14_or_greater = false
private set
var is_20_15_or_greater = false
private set
val versionCheckPatch = resourcePatch( val versionCheckPatch = resourcePatch(
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.", description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
@ -98,5 +102,7 @@ val versionCheckPatch = resourcePatch(
is_20_07_or_greater = 250805000 <= playStoreServicesVersion is_20_07_or_greater = 250805000 <= playStoreServicesVersion
is_20_09_or_greater = 251006000 <= playStoreServicesVersion is_20_09_or_greater = 251006000 <= playStoreServicesVersion
is_20_10_or_greater = 251105000 <= playStoreServicesVersion is_20_10_or_greater = 251105000 <= playStoreServicesVersion
is_20_14_or_greater = 251505000 <= playStoreServicesVersion
is_20_15_or_greater = 251605000 <= playStoreServicesVersion
} }
} }

View File

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

View File

@ -9,6 +9,7 @@ import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_03_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_03_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@ -22,7 +23,8 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
dependsOn( dependsOn(
@ -33,7 +35,8 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
}, { }, {
is_19_34_or_greater is_19_34_or_greater
}, { }, {
is_20_10_or_greater // In 20.14 the flag was merged with 20.03 start playback flag.
is_20_10_or_greater && !is_20_14_or_greater
}, { }, {
is_20_03_or_greater is_20_03_or_greater
}, { }, {

View File

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

View File

@ -33,8 +33,7 @@ internal val mainActivityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters() parameters()
custom { _, classDef -> custom { _, classDef ->
// Old versions of YouTube called this class "WatchWhileActivity" instead. classDef.endsWith("MainActivity;")
classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;")
} }
} }
@ -42,12 +41,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
returns("V") returns("V")
parameters("Landroid/os/Bundle;") parameters("Landroid/os/Bundle;")
custom { method, classDef -> custom { method, classDef ->
method.name == "onCreate" && method.name == "onCreate" && classDef.endsWith("MainActivity;")
(
classDef.endsWith("MainActivity;") ||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
classDef.endsWith("WatchWhileActivity;")
)
} }
} }

View File

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

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