feat(YouTube): Support version 19.34.42

This commit is contained in:
inotia00
2024-12-09 22:50:53 +09:00
parent 977b7f481b
commit 53c64552dd
31 changed files with 744 additions and 400 deletions

View File

@ -26,9 +26,4 @@ public class InitializationPatch {
showRestartDialog(mActivity, "revanced_extended_restart_first_run", 3000); showRestartDialog(mActivity, "revanced_extended_restart_first_run", 3000);
Utils.runOnMainThreadDelayed(() -> BaseSettings.SETTINGS_INITIALIZED.save(true), 3000); Utils.runOnMainThreadDelayed(() -> BaseSettings.SETTINGS_INITIALIZED.save(true), 3000);
} }
public static void setDeviceInformation(@NonNull Activity mActivity) {
ExtendedUtils.setApplicationLabel();
ExtendedUtils.setVersionName();
}
} }

View File

@ -248,8 +248,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
var appName = ExtendedUtils.getApplicationLabel(); var appName = ExtendedUtils.getAppLabel();
var versionName = ExtendedUtils.getVersionName(); var versionName = ExtendedUtils.getAppVersionName();
var formatDate = dateFormat.format(new Date(System.currentTimeMillis())); var formatDate = dateFormat.format(new Date(System.currentTimeMillis()));
var fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate); var fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate);

View File

@ -8,16 +8,25 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
public class PackageUtils extends Utils { public class PackageUtils extends Utils {
private static String applicationLabel = "";
private static int smallestScreenWidthDp = 0;
private static String versionName = "";
public static String getApplicationLabel() { public static String getAppLabel() {
return applicationLabel; final PackageInfo packageInfo = getPackageInfo();
if (packageInfo != null) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if (applicationInfo != null && applicationInfo.loadLabel(getPackageManager()) instanceof String applicationLabel) {
return applicationLabel;
}
}
return "";
} }
public static String getVersionName() { public static String getAppVersionName() {
return versionName; final PackageInfo packageInfo = getPackageInfo();
if (packageInfo != null) {
return packageInfo.versionName;
} else {
return "";
}
} }
public static boolean isPackageEnabled(@NonNull String packageName) { public static boolean isPackageEnabled(@NonNull String packageName) {
@ -30,32 +39,11 @@ public class PackageUtils extends Utils {
} }
public static boolean isTablet() { public static boolean isTablet() {
return smallestScreenWidthDp >= 600; return getSmallestScreenWidthDp() >= 600;
}
public static void setApplicationLabel() {
final PackageInfo packageInfo = getPackageInfo();
if (packageInfo != null) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if (applicationInfo != null) {
applicationLabel = (String) applicationInfo.loadLabel(getPackageManager());
}
}
}
public static void setSmallestScreenWidthDp() {
smallestScreenWidthDp = context.getResources().getConfiguration().smallestScreenWidthDp;
}
public static void setVersionName() {
final PackageInfo packageInfo = getPackageInfo();
if (packageInfo != null) {
versionName = packageInfo.versionName;
}
} }
public static int getSmallestScreenWidthDp() { public static int getSmallestScreenWidthDp() {
return smallestScreenWidthDp; return context.getResources().getConfiguration().smallestScreenWidthDp;
} }
// utils // utils

View File

@ -78,8 +78,8 @@ public class FeedPatch {
); );
} }
public static boolean hideFloatingButton() { public static String hideFloatingButton(String fab) {
return Settings.HIDE_FLOATING_BUTTON.get(); return Settings.HIDE_FLOATING_BUTTON.get() ? null : fab;
} }
public static void hideLatestVideosButton(View view) { public static void hideLatestVideosButton(View view) {

View File

@ -1,15 +1,17 @@
package app.revanced.extension.youtube.patches.general; package app.revanced.extension.youtube.patches.general;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_1; import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_2; import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.*;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_3; import static app.revanced.extension.youtube.utils.ExtendedUtils.IS_19_20_OR_GREATER;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.ORIGINAL; import static app.revanced.extension.youtube.utils.ExtendedUtils.IS_19_21_OR_GREATER;
import static app.revanced.extension.youtube.utils.ExtendedUtils.IS_19_26_OR_GREATER;
import static app.revanced.extension.youtube.utils.ExtendedUtils.IS_19_29_OR_GREATER;
import static app.revanced.extension.youtube.utils.ExtendedUtils.validateValue; import static app.revanced.extension.youtube.utils.ExtendedUtils.validateValue;
import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -17,9 +19,10 @@ import androidx.annotation.Nullable;
import app.revanced.extension.shared.utils.Logger; import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils; import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.shared.utils.Utils; import app.revanced.extension.shared.utils.Utils;
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", "SpellCheckingInspection"})
public final class MiniplayerPatch { public final class MiniplayerPatch {
/** /**
@ -27,14 +30,28 @@ public final class MiniplayerPatch {
*/ */
public enum MiniplayerType { public enum MiniplayerType {
/** /**
* Unmodified type, and same as un-patched. * Disabled. When swiped down the miniplayer is immediately closed.
* Only available with 19.43+
*/ */
DISABLED(false, null),
/** Unmodified type, and same as un-patched. */
ORIGINAL(null, null), ORIGINAL(null, null),
/**
* Exactly the same as MINIMAL and only here for migration of user settings.
* Eventually this should be deleted.
*/
@Deprecated
PHONE(false, null), PHONE(false, null),
MINIMAL(false, null),
TABLET(true, null), TABLET(true, null),
MODERN_1(null, 1), MODERN_1(null, 1),
MODERN_2(null, 2), MODERN_2(null, 2),
MODERN_3(null, 3); MODERN_3(null, 3),
/**
* Half broken miniplayer, that might be work in progress or left over abandoned code.
* Can force this type by editing the import/export settings.
*/
MODERN_4(null, 4);
/** /**
* Legacy tablet hook value. * Legacy tablet hook value.
@ -58,6 +75,44 @@ public final class MiniplayerPatch {
} }
} }
private static final int MINIPLAYER_SIZE;
static {
// YT appears to use the device screen dip width, plus an unknown fixed horizontal padding size.
DisplayMetrics displayMetrics = Utils.getContext().getResources().getDisplayMetrics();
final int deviceDipWidth = (int) (displayMetrics.widthPixels / displayMetrics.density);
// YT seems to use a minimum height to calculate the minimum miniplayer width based on the video.
// 170 seems to be the smallest that can be used and using less makes no difference.
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
final int WIDTH_DIP_MAX = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
if (dipWidth < WIDTH_DIP_MIN || dipWidth > WIDTH_DIP_MAX) {
Utils.showToastLong(str("revanced_miniplayer_width_dip_invalid_toast",
WIDTH_DIP_MIN, WIDTH_DIP_MAX));
// Instead of resetting, clamp the size at the bounds.
dipWidth = Math.max(WIDTH_DIP_MIN, Math.min(dipWidth, WIDTH_DIP_MAX));
Settings.MINIPLAYER_WIDTH_DIP.save(dipWidth);
}
MINIPLAYER_SIZE = dipWidth;
final int opacity = validateValue(
Settings.MINIPLAYER_OPACITY,
0,
100,
"revanced_miniplayer_opacity_invalid_toast"
);
OPACITY_LEVEL = (opacity * 255) / 100;
}
/** /**
* Modern subtitle overlay for {@link MiniplayerType#MODERN_2}. * Modern subtitle overlay for {@link MiniplayerType#MODERN_2}.
* Resource is not present in older targets, and this field will be zero. * Resource is not present in older targets, and this field will be zero.
@ -67,19 +122,21 @@ public final class MiniplayerPatch {
private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get(); private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get();
/**
* Cannot turn off double tap with modern 2 or 3 with later targets,
* as forcing it off breakings tapping the miniplayer.
*/
private static final boolean DOUBLE_TAP_ACTION_ENABLED = private static final boolean DOUBLE_TAP_ACTION_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_2 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get(); // 19.29+ is very broken if double tap is not enabled.
IS_19_29_OR_GREATER ||
(CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get());
private static final boolean DRAG_AND_DROP_ENABLED = private static final boolean DRAG_AND_DROP_ENABLED =
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_DRAG_AND_DROP.get(); CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
private static final boolean HIDE_EXPAND_CLOSE_AVAILABLE =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) &&
!DOUBLE_TAP_ACTION_ENABLED &&
!DRAG_AND_DROP_ENABLED;
private static final boolean HIDE_EXPAND_CLOSE_ENABLED = private static final boolean HIDE_EXPAND_CLOSE_ENABLED =
HIDE_EXPAND_CLOSE_AVAILABLE && Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get(); Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get()
&& Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.isAvailable();
private static final boolean HIDE_SUBTEXT_ENABLED = private static final boolean HIDE_SUBTEXT_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get(); (CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
@ -87,17 +144,49 @@ public final class MiniplayerPatch {
private static final boolean HIDE_REWIND_FORWARD_ENABLED = private static final boolean HIDE_REWIND_FORWARD_ENABLED =
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get(); CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
private static final boolean MINIPLAYER_HORIZONTAL_DRAG_ENABLED =
DRAG_AND_DROP_ENABLED && Settings.MINIPLAYER_HORIZONTAL_DRAG.get();
/**
* Remove a broken and always present subtitle text that is only
* present with {@link MiniplayerType#MODERN_2}. Bug was fixed in 19.21.
*/
private static final boolean HIDE_BROKEN_MODERN_2_SUBTITLE =
CURRENT_TYPE == MODERN_2 && !IS_19_21_OR_GREATER;
private static final int OPACITY_LEVEL; private static final int OPACITY_LEVEL;
static { public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
final int opacity = validateValue( @Override
Settings.MINIPLAYER_OPACITY, public boolean isAvailable() {
0, return Settings.MINIPLAYER_TYPE.get().isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
100, }
"revanced_miniplayer_opacity_invalid_toast" }
);
OPACITY_LEVEL = (opacity * 255) / 100; public static final class MiniplayerHideExpandCloseAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
MiniplayerType type = Settings.MINIPLAYER_TYPE.get();
return (!IS_19_20_OR_GREATER && (type == MODERN_1 || type == MODERN_3))
|| (!IS_19_26_OR_GREATER && type == MODERN_1
&& !Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get() && !Settings.MINIPLAYER_DRAG_AND_DROP.get())
|| (IS_19_29_OR_GREATER && type == MODERN_3);
}
}
/**
* Injection point.
* <p>
* Enables a handler that immediately closes the miniplayer when the video is minimized,
* effectively disabling the miniplayer.
*/
public static boolean getMiniplayerOnCloseHandler(boolean original) {
return CURRENT_TYPE == ORIGINAL
? original
: CURRENT_TYPE == DISABLED;
} }
/** /**
@ -141,17 +230,71 @@ public final class MiniplayerPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static boolean enableMiniplayerDoubleTapAction() { public static boolean getModernFeatureFlagsActiveOverride(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
return original;
}
return CURRENT_TYPE.isModern();
}
/**
* Injection point.
*/
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
return original;
}
return DOUBLE_TAP_ACTION_ENABLED; return DOUBLE_TAP_ACTION_ENABLED;
} }
/** /**
* Injection point. * Injection point.
*/ */
public static boolean enableMiniplayerDragAndDrop() { public static boolean enableMiniplayerDragAndDrop(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
return original;
}
return DRAG_AND_DROP_ENABLED; return DRAG_AND_DROP_ENABLED;
} }
/**
* Injection point.
*/
public static boolean setRoundedCorners(boolean original) {
if (CURRENT_TYPE.isModern()) {
return MINIPLAYER_ROUNDED_CORNERS_ENABLED;
}
return original;
}
/**
* Injection point.
*/
public static int setMiniplayerDefaultSize(int original) {
if (CURRENT_TYPE.isModern()) {
return MINIPLAYER_SIZE;
}
return original;
}
/**
* Injection point.
*/
public static boolean setHorizontalDrag(boolean original) {
if (CURRENT_TYPE.isModern()) {
return MINIPLAYER_HORIZONTAL_DRAG_ENABLED;
}
return original;
}
/** /**
* Injection point. * Injection point.
*/ */
@ -169,29 +312,36 @@ public final class MiniplayerPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static boolean hideMiniplayerSubTexts(View view) { public static void hideMiniplayerSubTexts(View view) {
// Different subviews are passed in, but only TextView and layouts are of interest here. try {
final boolean hideView = HIDE_SUBTEXT_ENABLED && (view instanceof TextView || view instanceof LinearLayout); // Different subviews are passed in, but only TextView is of interest here.
Utils.hideViewByRemovingFromParentUnderCondition(hideView, view); if (HIDE_SUBTEXT_ENABLED && view instanceof TextView) {
return hideView || view == null; Logger.printDebug(() -> "Hiding subtext view");
Utils.hideViewByRemovingFromParentUnderCondition(true, view);
}
} catch (Exception ex) {
Logger.printException(() -> "hideMiniplayerSubTexts failure", ex);
}
} }
/** /**
* Injection point. * Injection point.
*/ */
public static void playerOverlayGroupCreated(View group) { public static void playerOverlayGroupCreated(View group) {
// Modern 2 has an half broken subtitle that is always present. try {
// Always hide it to make the miniplayer mostly usable. if (HIDE_BROKEN_MODERN_2_SUBTITLE && MODERN_OVERLAY_SUBTITLE_TEXT != 0) {
if (CURRENT_TYPE == MODERN_2 && MODERN_OVERLAY_SUBTITLE_TEXT != 0) { if (group instanceof ViewGroup) {
if (group instanceof ViewGroup viewGroup) { View subtitleText = Utils.getChildView((ViewGroup) group, true,
View subtitleText = Utils.getChildView(viewGroup, true, view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT);
view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT);
if (subtitleText != null) { if (subtitleText != null) {
subtitleText.setVisibility(View.GONE); subtitleText.setVisibility(View.GONE);
Logger.printDebug(() -> "Modern overlay subtitle view set to hidden"); Logger.printDebug(() -> "Modern overlay subtitle view set to hidden");
}
} }
} }
} catch (Exception ex) {
Logger.printException(() -> "playerOverlayGroupCreated failure", ex);
} }
} }
} }

View File

@ -31,9 +31,6 @@ public class InitializationPatch {
} }
public static void setExtendedUtils(@NonNull Activity mActivity) { public static void setExtendedUtils(@NonNull Activity mActivity) {
ExtendedUtils.setApplicationLabel();
ExtendedUtils.setSmallestScreenWidthDp();
ExtendedUtils.setVersionName();
ExtendedUtils.setPlayerFlyoutMenuAdditionalSettings(); ExtendedUtils.setPlayerFlyoutMenuAdditionalSettings();
} }
} }

View File

@ -10,6 +10,7 @@ import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.Min
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_1; import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_1;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_2; import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_2;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_3; import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_3;
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.MODERN_4;
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;
@ -34,6 +35,7 @@ import app.revanced.extension.youtube.patches.alternativethumbnails.AlternativeT
import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch; import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch;
import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch.StartPage; import app.revanced.extension.youtube.patches.general.ChangeStartPagePatch.StartPage;
import app.revanced.extension.youtube.patches.general.LayoutSwitchPatch.FormFactor; import app.revanced.extension.youtube.patches.general.LayoutSwitchPatch.FormFactor;
import app.revanced.extension.youtube.patches.general.MiniplayerPatch;
import app.revanced.extension.youtube.patches.general.YouTubeMusicActionsPatch; import app.revanced.extension.youtube.patches.general.YouTubeMusicActionsPatch;
import app.revanced.extension.youtube.patches.misc.SpoofStreamingDataPatch; import app.revanced.extension.youtube.patches.misc.SpoofStreamingDataPatch;
import app.revanced.extension.youtube.patches.misc.WatchHistoryPatch.WatchHistoryType; import app.revanced.extension.youtube.patches.misc.WatchHistoryPatch.WatchHistoryType;
@ -167,11 +169,15 @@ public class Settings extends BaseSettings {
// PreferenceScreen: General - Miniplayer // PreferenceScreen: General - Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true); public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true);
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_enable_double_tap_action", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3)); private static final Setting.Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_enable_drag_and_drop", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1)); public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true); public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_drag_and_drop", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerPatch.MiniplayerHorizontalDragAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerPatch.MiniplayerHideExpandCloseAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3)); public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1)); public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1)); public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
// PreferenceScreen: General - Navigation bar // PreferenceScreen: General - Navigation bar

View File

@ -593,8 +593,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
private void exportActivity() { private void exportActivity() {
@SuppressLint("SimpleDateFormat") final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); @SuppressLint("SimpleDateFormat") final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
final String appName = ExtendedUtils.getApplicationLabel(); final String appName = ExtendedUtils.getAppLabel();
final String versionName = ExtendedUtils.getVersionName(); final String versionName = ExtendedUtils.getAppVersionName();
final String formatDate = dateFormat.format(new Date(System.currentTimeMillis())); final String formatDate = dateFormat.format(new Date(System.currentTimeMillis()));
final String fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate); final String fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate);

View File

@ -12,6 +12,12 @@ import app.revanced.extension.shared.utils.PackageUtils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
public class ExtendedUtils extends PackageUtils { public class ExtendedUtils extends PackageUtils {
public static final boolean IS_19_17_OR_GREATER = getAppVersionName().compareTo("19.17.00") >= 0;
public static final boolean IS_19_20_OR_GREATER = getAppVersionName().compareTo("19.20.00") >= 0;
public static final boolean IS_19_21_OR_GREATER = getAppVersionName().compareTo("19.21.00") >= 0;
public static final boolean IS_19_26_OR_GREATER = getAppVersionName().compareTo("19.26.00") >= 0;
public static final boolean IS_19_29_OR_GREATER = getAppVersionName().compareTo("19.29.00") >= 0;
public static final boolean IS_19_34_OR_GREATER = getAppVersionName().compareTo("19.34.00") >= 0;
public static int validateValue(IntegerSetting settings, int min, int max, String message) { public static int validateValue(IntegerSetting settings, int min, int max, String message) {
int value = settings.get(); int value = settings.get();

View File

@ -14,6 +14,7 @@ import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup import app.revanced.util.ResourceGroup
import app.revanced.util.Utils.trimIndentMultiline import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.getAdaptiveIconResourceFile
import app.revanced.util.getResourceGroup import app.revanced.util.getResourceGroup
import app.revanced.util.underBarOrThrow import app.revanced.util.underBarOrThrow
import org.w3c.dom.Element import org.w3c.dom.Element
@ -248,38 +249,23 @@ val customBrandingIconPatch = resourcePatch(
return@execute return@execute
} }
fun getAdaptiveIconResourceFile(tag: String): String {
document("res/mipmap-anydpi/ic_launcher_release.xml").use { document ->
val adaptiveIcon = document
.getElementsByTagName("adaptive-icon")
.item(0) as Element
val childNodes = adaptiveIcon.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i)
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
return node.getAttribute("android:drawable").split("/")[1]
}
}
throw PatchException("Element not found: $tag")
}
}
mapOf( mapOf(
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("background"), ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "background"),
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("foreground") ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "foreground")
).forEach { (oldIconResourceFile, newIconResourceFile) -> ).forEach { (oldIconResourceFile, newIconResourceFile) ->
mipmapDirectories.forEach { if (oldIconResourceFile != newIconResourceFile) {
val mipmapDirectory = resourceDirectory.resolve(it) mipmapDirectories.forEach {
Files.move( val mipmapDirectory = resourceDirectory.resolve(it)
mipmapDirectory Files.move(
.resolve("$oldIconResourceFile.png") mipmapDirectory
.toPath(), .resolve("$oldIconResourceFile.png")
mipmapDirectory .toPath(),
.resolve("$newIconResourceFile.png") mipmapDirectory
.toPath(), .resolve("$newIconResourceFile.png")
StandardCopyOption.REPLACE_EXISTING .toPath(),
) StandardCopyOption.REPLACE_EXISTING
)
}
} }
} }

View File

@ -112,10 +112,6 @@ private val settingsBytecodePatch = bytecodePatch(
// endregion // endregion
injectOnCreateMethodCall(
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
"setDeviceInformation"
)
injectOnCreateMethodCall( injectOnCreateMethodCall(
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR, EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
"onCreate" "onCreate"

View File

@ -147,18 +147,22 @@ val feedComponentsPatch = bytecodePatch(
// region patch for hide floating button // region patch for hide floating button
onCreateMethod.apply { onCreateMethod.apply {
val fabIndex = indexOfFirstInstructionOrThrow { val stringIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_STRING && opcode == Opcode.CONST_STRING &&
getReference<StringReference>()?.string == "fab" getReference<StringReference>()?.string == "fab"
} }
val fabRegister = getInstruction<OneRegisterInstruction>(fabIndex).registerA val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(fabIndex + 1, Opcode.CONST_STRING) val insertIndex = indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.name == "<init>"
}
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex, Opcode.CONST_STRING)
addInstructionsWithLabels( addInstructionsWithLabels(
fabIndex, """ insertIndex, """
invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton()Z invoke-static {v$stringRegister}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton(Ljava/lang/String;)Ljava/lang/String;
move-result v$fabRegister move-result-object v$stringRegister
if-nez v$fabRegister, :hide if-eqz v$stringRegister, :hide
""", ExternalLabel("hide", getInstruction(jumpIndex)) """, ExternalLabel("hide", getInstruction(jumpIndex))
) )
} }

View File

@ -2,20 +2,18 @@
package app.revanced.patches.youtube.general.miniplayer package app.revanced.patches.youtube.general.miniplayer
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.resourceid.floatyBarTopMargin import app.revanced.patches.youtube.utils.resourceid.floatyBarTopMargin
import app.revanced.patches.youtube.utils.resourceid.miniplayerMaxSize
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerForwardButton import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerForwardButton
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerRewindButton import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerRewindButton
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWhite import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWhite
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or import app.revanced.util.or
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.util.MethodUtil
internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint( internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint(
name = "miniplayerDimensionsCalculatorParentFingerprint", name = "miniplayerDimensionsCalculatorParentFingerprint",
@ -25,6 +23,9 @@ internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint
literals = listOf(floatyBarTopMargin), literals = listOf(floatyBarTopMargin),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint( internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
name = "miniplayerModernAddViewListenerFingerprint", name = "miniplayerModernAddViewListenerFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
@ -32,6 +33,9 @@ internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
parameters = listOf("Landroid/view/View;") parameters = listOf("Landroid/view/View;")
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint( internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
name = "miniplayerModernCloseButtonFingerprint", name = "miniplayerModernCloseButtonFingerprint",
returnType = "Landroid/widget/ImageView;", returnType = "Landroid/widget/ImageView;",
@ -40,41 +44,33 @@ internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerClose), literals = listOf(modernMiniPlayerClose),
) )
private var constructorMethodCount = 0 internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
// In later targets this feature flag does nothing and is dead code.
internal fun isMultiConstructorMethod() = constructorMethodCount > 1 internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L
internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L
internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L
internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L
internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L
internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L
internal val miniplayerModernConstructorFingerprint = legacyFingerprint( internal val miniplayerModernConstructorFingerprint = legacyFingerprint(
name = "miniplayerModernConstructorFingerprint", name = "miniplayerModernConstructorFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"), parameters = listOf("L"),
literals = listOf(45623000L), literals = listOf(45623000L),
customFingerprint = custom@{ method, classDef ->
classDef.methods.forEach {
if (MethodUtil.isConstructor(it)) constructorMethodCount += 1
}
if (!is_19_25_or_greater)
return@custom true
// Double tap action (Used in YouTube 19.25.39+).
method.containsLiteralInstruction(45628823L)
&& method.containsLiteralInstruction(45630429L)
}
) )
internal val miniplayerModernDragAndDropFingerprint = legacyFingerprint( internal val miniplayerOnCloseHandlerFingerprint = legacyFingerprint(
name = "miniplayerModernDragAndDropFingerprint", name = "miniplayerOnCloseHandlerFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"), returnType = "Z",
literals = listOf(45628752L), literals = listOf(MINIPLAYER_DISABLED_FEATURE_KEY),
)
internal val miniplayerModernEnabledFingerprint = legacyFingerprint(
name = "miniplayerModernEnabledFingerprint",
literals = listOf(45622882L),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint( internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
name = "miniplayerModernExpandButtonFingerprint", name = "miniplayerModernExpandButtonFingerprint",
returnType = "Landroid/widget/ImageView;", returnType = "Landroid/widget/ImageView;",
@ -83,6 +79,9 @@ internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerExpand), literals = listOf(modernMiniPlayerExpand),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint( internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint(
name = "miniplayerModernExpandCloseDrawablesFingerprint", name = "miniplayerModernExpandCloseDrawablesFingerprint",
returnType = "V", returnType = "V",
@ -91,6 +90,9 @@ internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint
literals = listOf(ytOutlinePictureInPictureWhite), literals = listOf(ytOutlinePictureInPictureWhite),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint( internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
name = "miniplayerModernForwardButtonFingerprint", name = "miniplayerModernForwardButtonFingerprint",
returnType = "Landroid/widget/ImageView;", returnType = "Landroid/widget/ImageView;",
@ -99,6 +101,9 @@ internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerForwardButton), literals = listOf(modernMiniPlayerForwardButton),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint( internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
name = "miniplayerModernOverlayViewFingerprint", name = "miniplayerModernOverlayViewFingerprint",
returnType = "V", returnType = "V",
@ -107,6 +112,9 @@ internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
literals = listOf(scrimOverlay), literals = listOf(scrimOverlay),
) )
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernRewindButtonFingerprint = legacyFingerprint( internal val miniplayerModernRewindButtonFingerprint = legacyFingerprint(
name = "miniplayerModernRewindButtonFingerprint", name = "miniplayerModernRewindButtonFingerprint",
returnType = "Landroid/widget/ImageView;", returnType = "Landroid/widget/ImageView;",
@ -123,11 +131,16 @@ internal val miniplayerModernViewParentFingerprint = legacyFingerprint(
strings = listOf("player_overlay_modern_mini_player_controls") strings = listOf("player_overlay_modern_mini_player_controls")
) )
internal val miniplayerMinimumSizeFingerprint = legacyFingerprint(
name = "miniplayerMinimumSizeFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literals = listOf(192L, 128L, miniplayerMaxSize),
)
internal val miniplayerOverrideFingerprint = legacyFingerprint( internal val miniplayerOverrideFingerprint = legacyFingerprint(
name = "miniplayerOverrideFingerprint", name = "miniplayerOverrideFingerprint",
returnType = "L", returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("appName") strings = listOf("appName")
) )
@ -144,6 +157,7 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
returnType = "L", returnType = "L",
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"), parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
opcodes = listOf( opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST, Opcode.CHECK_CAST,
Opcode.CHECK_CAST, Opcode.CHECK_CAST,
Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC,
@ -152,12 +166,12 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
) )
) )
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
internal val youTubePlayerOverlaysLayoutFingerprint = legacyFingerprint( internal val youTubePlayerOverlaysLayoutFingerprint = legacyFingerprint(
name = "youTubePlayerOverlaysLayoutFingerprint", name = "youTubePlayerOverlaysLayoutFingerprint",
customFingerprint = { _, classDef -> customFingerprint = { _, classDef ->
classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
} }
) )
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"

View File

@ -1,22 +1,22 @@
package app.revanced.patches.youtube.general.miniplayer package app.revanced.patches.youtube.general.miniplayer
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
import app.revanced.patches.youtube.utils.patch.PatchList.MINIPLAYER import app.revanced.patches.youtube.utils.patch.PatchList.MINIPLAYER
import app.revanced.patches.youtube.utils.playservice.is_19_15_or_greater import app.revanced.patches.youtube.utils.playservice.is_19_15_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_26_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_29_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_36_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_43_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWh
import app.revanced.patches.youtube.utils.resourceid.ytOutlineXWhite import app.revanced.patches.youtube.utils.resourceid.ytOutlineXWhite
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.matchOrThrow
@ -41,14 +42,13 @@ 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.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
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.ReferenceInstruction
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.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
import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"$GENERAL_PATH/MiniplayerPatch;" "$GENERAL_PATH/MiniplayerPatch;"
@ -83,7 +83,7 @@ val miniplayerPatch = bytecodePatch(
""" """
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register move-result v$register
""" """
) )
} }
@ -105,39 +105,34 @@ val miniplayerPatch = bytecodePatch(
* Adds an override to specify which modern miniplayer is used. * Adds an override to specify which modern miniplayer is used.
*/ */
fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) { fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex) val register = getInstruction<TwoRegisterInstruction>(iPutIndex).registerA
val targetReference = (targetInstruction as ReferenceInstruction).reference
addInstructions( addInstructionsAtControlFlowLabel(
iPutIndex + 1, """ iPutIndex,
invoke-static { v${targetInstruction.registerA} }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I """
move-result v${targetInstruction.registerA} invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
# Original instruction move-result v$register
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference """,
"""
) )
removeInstruction(iPutIndex)
} }
fun Pair<String, Fingerprint>.hookInflatedView( fun MutableMethod.hookInflatedView(
literalValue: Long, literalValue: Long,
hookedClassType: String, hookedClassType: String,
extensionMethodName: String, extensionMethodName: String,
) { ) {
methodOrThrow(miniplayerModernViewParentFingerprint).apply { val imageViewIndex = indexOfFirstInstructionOrThrow(
val imageViewIndex = indexOfFirstInstructionOrThrow( indexOfFirstLiteralInstructionOrThrow(literalValue)
indexOfFirstLiteralInstructionOrThrow(literalValue) ) {
) { opcode == Opcode.CHECK_CAST &&
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type == hookedClassType
getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $extensionMethodName"
)
} }
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $extensionMethodName"
)
} }
// Modern mini player is only present and functional in 19.15+. // Modern mini player is only present and functional in 19.15+.
@ -184,7 +179,7 @@ val miniplayerPatch = bytecodePatch(
} }
if (isPatchingOldVersion) { if (isPatchingOldVersion) {
settingArray += "SETTINGS: MINIPLAYER_TYPE_LEGACY" settingArray += "SETTINGS: MINIPLAYER_TYPE_19_14"
addPreference(settingArray, MINIPLAYER) addPreference(settingArray, MINIPLAYER)
// Return here, as patch below is only intended for new versions of the app. // Return here, as patch below is only intended for new versions of the app.
@ -197,14 +192,14 @@ val miniplayerPatch = bytecodePatch(
miniplayerModernConstructorFingerprint.mutableClassOrThrow().methods.forEach { miniplayerModernConstructorFingerprint.mutableClassOrThrow().methods.forEach {
it.apply { it.apply {
if (MethodUtil.isConstructor(it)) { if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
val iPutIndex = indexOfFirstInstructionOrThrow { val iPutIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT && this.opcode == Opcode.IPUT &&
getReference<FieldReference>()?.type == "I" this.getReference<FieldReference>()?.type == "I"
} }
insertModernMiniplayerTypeOverride(iPutIndex) insertModernMiniplayerTypeOverride(iPutIndex)
} else if (isMultiConstructorMethod()) { } else {
findReturnIndicesReversed().forEach { index -> findReturnIndicesReversed().forEach { index ->
insertModernMiniplayerOverride( insertModernMiniplayerOverride(
index index
@ -214,27 +209,96 @@ val miniplayerPatch = bytecodePatch(
} }
} }
if (is_19_25_or_greater) { if (is_19_23_or_greater) {
miniplayerModernEnabledFingerprint.injectLiteralInstructionBooleanCall( miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
45622882L, MINIPLAYER_DRAG_DROP_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop(Z)Z"
) )
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
} }
// endregion
// region Enable double tap action.
if (is_19_25_or_greater) { if (is_19_25_or_greater) {
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall( miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
45628823L, MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction()Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
45630429L,
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
) )
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_MODERN_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getModernFeatureFlagsActiveOverride(Z)Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction(Z)Z"
)
if (!is_19_29_or_greater) {
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
}
}
if (is_19_26_or_greater) {
miniplayerModernConstructorFingerprint.methodOrThrow().apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
MINIPLAYER_INITIAL_SIZE_FEATURE_KEY,
)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I
move-result v$register
""",
)
}
// Override a minimum size constant.
miniplayerMinimumSizeFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_16 &&
(this as NarrowLiteralInstruction).narrowLiteral == 192
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
// Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller.
// The 170 initial limit probably could be patched to allow even smaller initial sizes,
// but 170 is already half the horizontal space and smaller does not seem useful.
replaceInstruction(index, "const/16 v$register, 170")
}
settingArray += "SETTINGS: MINIPLAYER_WIDTH_DIP"
} else {
settingArray += "SETTINGS: MINIPLAYER_REWIND_FORWARD"
}
if (is_19_36_or_greater) {
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->setRoundedCorners(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_ROUNDED_CONERS"
}
if (is_19_43_or_greater) {
miniplayerOnCloseHandlerFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_DISABLED_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getMiniplayerOnCloseHandler(Z)Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->setHorizontalDrag(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_HORIZONTAL_DRAG"
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_43"
} else {
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_16"
} }
// endregion // endregion
@ -291,7 +355,7 @@ val miniplayerPatch = bytecodePatch(
"adjustMiniplayerOpacity" "adjustMiniplayerOpacity"
) )
).forEach { (fingerprint, literalValue, methodName) -> ).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.hookInflatedView( fingerprint.methodOrThrow(miniplayerModernViewParentFingerprint).hookInflatedView(
literalValue, literalValue,
"Landroid/widget/ImageView;", "Landroid/widget/ImageView;",
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V" "$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
@ -300,23 +364,19 @@ val miniplayerPatch = bytecodePatch(
miniplayerModernAddViewListenerFingerprint.methodOrThrow( miniplayerModernAddViewListenerFingerprint.methodOrThrow(
miniplayerModernViewParentFingerprint miniplayerModernViewParentFingerprint
).apply { ).addInstruction(
addInstructionsWithLabels( 0,
0, "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" +
""" "hideMiniplayerSubTexts(Landroid/view/View;)V",
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideMiniplayerSubTexts(Landroid/view/View;)Z )
move-result v0
if-nez v0, :hidden
""",
ExternalLabel("hidden", getInstruction(implementation!!.instructions.lastIndex))
)
}
// Modern 2 has a broken overlay subtitle view that is always present. // Modern 2 has a broken overlay subtitle view that is always present.
// Modern 2 uses the same overlay controls as the regular video player, // Modern 2 uses the same overlay controls as the regular video player,
// and the overlay views are added at runtime. // and the overlay views are added at runtime.
// Add a hook to the overlay class, and pass the added views to extension. // Add a hook to the overlay class, and pass the added views to extension.
//
// NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller.
// This patch code could be used to hide other player overlays that do not use Litho.
youTubePlayerOverlaysLayoutFingerprint.matchOrThrow().let { youTubePlayerOverlaysLayoutFingerprint.matchOrThrow().let {
it.method.apply { it.method.apply {
it.classDef.methods.add( it.classDef.methods.add(
@ -352,18 +412,6 @@ val miniplayerPatch = bytecodePatch(
// endregion // endregion
// region Enable drag and drop.
if (is_19_23_or_greater) {
miniplayerModernDragAndDropFingerprint.injectLiteralInstructionBooleanCall(
45628752L,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop()Z"
)
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
}
// endregion
settingArray += "SETTINGS: MINIPLAYER_TYPE_MODERN" settingArray += "SETTINGS: MINIPLAYER_TYPE_MODERN"
// region add settings // region add settings

View File

@ -1,17 +1,69 @@
package app.revanced.patches.youtube.general.spoofappversion package app.revanced.patches.youtube.general.spoofappversion
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_APP_VERSION import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_APP_VERSION
import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_39_or_greater import app.revanced.patches.youtube.utils.playservice.is_18_39_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
import app.revanced.util.appendAppVersion import app.revanced.util.appendAppVersion
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private val spoofAppVersionBytecodePatch = bytecodePatch(
description = "spoofAppVersionBytecodePatch"
) {
dependsOn(versionCheckPatch)
execute {
if (!is_19_23_or_greater) {
return@execute
}
/**
* When spoofing the app version to YouTube 19.20.xx or earlier via Spoof app version on YouTube 19.23.xx+, the Library tab will crash.
* As a temporary workaround, do not set an image in the toolbar when the enum name is UNKNOWN.
*/
toolBarButtonFingerprint.methodOrThrow().apply {
val getDrawableIndex = indexOfGetDrawableInstruction(this)
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.returnType == "I"
}
val insertIndex = enumOrdinalIndex + 2
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setImageDrawable"
} + 1
addInstructionsWithLabels(
insertIndex, """
if-eqz v$insertRegister, :ignore
""", ExternalLabel("ignore", getInstruction(jumpIndex))
)
}
}
}
@Suppress("unused") @Suppress("unused")
val spoofAppVersionPatch = resourcePatch( val spoofAppVersionPatch = resourcePatch(
@ -22,6 +74,7 @@ val spoofAppVersionPatch = resourcePatch(
dependsOn( dependsOn(
baseSpoofAppVersionPatch("$GENERAL_CLASS_DESCRIPTOR->getVersionOverride(Ljava/lang/String;)Ljava/lang/String;"), baseSpoofAppVersionPatch("$GENERAL_CLASS_DESCRIPTOR->getVersionOverride(Ljava/lang/String;)Ljava/lang/String;"),
spoofAppVersionBytecodePatch,
settingsPatch, settingsPatch,
versionCheckPatch, versionCheckPatch,
) )

View File

@ -297,6 +297,7 @@ val toolBarComponentsPatch = bytecodePatch(
// endregion // endregion
/*
// region patch for hide voice search button // region patch for hide voice search button
if (is_19_28_or_greater) { if (is_19_28_or_greater) {
@ -311,6 +312,7 @@ val toolBarComponentsPatch = bytecodePatch(
} }
// endregion // endregion
*/
// region patch for hide voice search button // region patch for hide voice search button

View File

@ -5,6 +5,8 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption import app.revanced.patcher.patch.stringOption
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusIcon import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusIcon
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup import app.revanced.util.ResourceGroup
@ -12,9 +14,16 @@ import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyFile import app.revanced.util.copyFile
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode import app.revanced.util.copyXmlNode
import app.revanced.util.getAdaptiveIconResourceFile
import app.revanced.util.getResourceGroup import app.revanced.util.getResourceGroup
import app.revanced.util.underBarOrThrow import app.revanced.util.underBarOrThrow
import java.nio.file.Files
import java.nio.file.StandardCopyOption
private const val ADAPTIVE_ICON_BACKGROUND_FILE_NAME =
"adaptiveproduct_youtube_background_color_108"
private const val ADAPTIVE_ICON_FOREGROUND_FILE_NAME =
"adaptiveproduct_youtube_foreground_color_108"
private const val DEFAULT_ICON = "revancify_blue" private const val DEFAULT_ICON = "revancify_blue"
private val availableIcon = mapOf( private val availableIcon = mapOf(
@ -39,8 +48,8 @@ private val drawableDirectories = sizeArray.map { "drawable-$it" }
private val mipmapDirectories = sizeArray.map { "mipmap-$it" } private val mipmapDirectories = sizeArray.map { "mipmap-$it" }
private val launcherIconResourceFileNames = arrayOf( private val launcherIconResourceFileNames = arrayOf(
"adaptiveproduct_youtube_background_color_108", ADAPTIVE_ICON_BACKGROUND_FILE_NAME,
"adaptiveproduct_youtube_foreground_color_108", ADAPTIVE_ICON_FOREGROUND_FILE_NAME,
"ic_launcher", "ic_launcher",
"ic_launcher_round" "ic_launcher_round"
).map { "$it.png" }.toTypedArray() ).map { "$it.png" }.toTypedArray()
@ -83,7 +92,10 @@ val customBrandingIconPatch = resourcePatch(
) { ) {
compatibleWith(COMPATIBLE_PACKAGE) compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch) dependsOn(
settingsPatch,
versionCheckPatch,
)
val appIconOption = stringOption( val appIconOption = stringOption(
@ -181,5 +193,32 @@ val customBrandingIconPatch = resourcePatch(
updatePatchStatusIcon(appIcon) updatePatchStatusIcon(appIcon)
} }
if (!is_19_34_or_greater) {
return@execute
}
if (appIcon == "youtube") {
return@execute
}
mapOf(
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "background"),
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "foreground")
).forEach { (oldIconResourceFile, newIconResourceFile) ->
if (oldIconResourceFile != newIconResourceFile) {
mipmapDirectories.forEach {
val mipmapDirectory = get("res").resolve(it)
Files.copy(
mipmapDirectory
.resolve("$oldIconResourceFile.png")
.toPath(),
mipmapDirectory
.resolve("$newIconResourceFile.png")
.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
}
} }
} }

View File

@ -386,6 +386,8 @@ val playerComponentsPatch = bytecodePatch(
findMethodOrThrow(syntheticReference) { findMethodOrThrow(syntheticReference) {
name == "onClick" name == "onClick"
}.hookInitVideoPanel(0) }.hookInitVideoPanel(0)
} else {
println("WARNING: target Opcode not found in ${fingerprint.first}")
} }
} }
} }

View File

@ -5,6 +5,7 @@ import app.revanced.patches.youtube.utils.resourceid.fadeDurationFast
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor
import app.revanced.patches.youtube.utils.resourceid.insetOverlayViewLayout import app.revanced.patches.youtube.utils.resourceid.insetOverlayViewLayout
import app.revanced.patches.youtube.utils.resourceid.menuItemView
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
import app.revanced.patches.youtube.utils.resourceid.seekUndoEduOverlayStub import app.revanced.patches.youtube.utils.resourceid.seekUndoEduOverlayStub
import app.revanced.patches.youtube.utils.resourceid.totalTime import app.revanced.patches.youtube.utils.resourceid.totalTime
@ -12,9 +13,13 @@ import app.revanced.patches.youtube.utils.resourceid.varispeedUnavailableTitle
import app.revanced.patches.youtube.utils.resourceid.videoQualityBottomSheet import app.revanced.patches.youtube.utils.resourceid.videoQualityBottomSheet
import app.revanced.patches.youtube.utils.sponsorblock.sponsorBlockBytecodePatch import app.revanced.patches.youtube.utils.sponsorblock.sponsorBlockBytecodePatch
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or import app.revanced.util.or
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
internal val engagementPanelBuilderFingerprint = legacyFingerprint( internal val engagementPanelBuilderFingerprint = legacyFingerprint(
name = "engagementPanelBuilderFingerprint", name = "engagementPanelBuilderFingerprint",
@ -164,6 +169,23 @@ internal val seekbarOnDrawFingerprint = legacyFingerprint(
customFingerprint = { method, _ -> method.name == "onDraw" } customFingerprint = { method, _ -> method.name == "onDraw" }
) )
internal fun indexOfGetDrawableInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() == "Landroid/content/res/Resources;->getDrawable(I)Landroid/graphics/drawable/Drawable;"
}
internal val toolBarButtonFingerprint = legacyFingerprint(
name = "toolBarButtonFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/MenuItem;"),
literals = listOf(menuItemView),
customFingerprint = { method, _ ->
indexOfGetDrawableInstruction(method) >= 0
}
)
internal val totalTimeFingerprint = legacyFingerprint( internal val totalTimeFingerprint = legacyFingerprint(
name = "totalTimeFingerprint", name = "totalTimeFingerprint",
returnType = "V", returnType = "V",

View File

@ -14,7 +14,8 @@ internal object Constants {
"18.38.44", // This is the last version with no delay in applying video quality on the server side. "18.38.44", // This is the last version with no delay in applying video quality on the server side.
"18.48.39", // This is the last version that do not use Rolling Number. "18.48.39", // This is the last version that do not use Rolling Number.
"19.05.36", // This is the last version with the least YouTube experimental flag. "19.05.36", // This is the last version with the least YouTube experimental flag.
"19.16.39", // This is the latest version supported by the RVX patch. "19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
"19.34.42", // This is the latest version supported by the RVX patch.
) )
) )
} }

View File

@ -134,8 +134,8 @@ private val playerControlsBytecodePatch = bytecodePatch(
changeVisibilityMethod = changeVisibilityMethod =
findMethodOrThrow(EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR) { findMethodOrThrow(EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR) {
name == "changeVisibility" name == "changeVisibility" &&
&& parameters == listOf("Z", "Z") parameters == listOf("Z", "Z")
} }
changeVisibilityNegatedImmediatelyMethod = changeVisibilityNegatedImmediatelyMethod =

View File

@ -10,12 +10,6 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method 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.MethodReference
internal fun indexOfLayoutDirectionInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Landroid/view/View;->setLayoutDirection(I)V"
}
internal val browseIdClassFingerprint = legacyFingerprint( internal val browseIdClassFingerprint = legacyFingerprint(
name = "browseIdClassFingerprint", name = "browseIdClassFingerprint",
returnType = "Ljava/lang/Object;", returnType = "Ljava/lang/Object;",
@ -45,18 +39,26 @@ internal val reelWatchPagerFingerprint = legacyFingerprint(
literals = listOf(reelWatchPlayer), literals = listOf(reelWatchPlayer),
) )
internal fun indexOfStringIsEmptyInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Ljava/lang/String;->isEmpty()Z"
}
internal val searchQueryClassFingerprint = legacyFingerprint( internal val searchQueryClassFingerprint = legacyFingerprint(
name = "searchQueryClassFingerprint", name = "searchQueryClassFingerprint",
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "Ljava/util/Map;"), parameters = listOf("L", "Ljava/util/Map;"),
opcodes = listOf( opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT, Opcode.MOVE_RESULT,
), ),
strings = listOf("force_enable_sticky_browsy_bars"), strings = listOf("force_enable_sticky_browsy_bars"),
customFingerprint = { method, _ ->
indexOfStringIsEmptyInstruction(method) >= 0
}
) )
internal val videoStateFingerprint = legacyFingerprint( internal val videoStateFingerprint = legacyFingerprint(

View File

@ -135,32 +135,30 @@ val playerTypeHookPatch = bytecodePatch(
// region patch for hook search bar // region patch for hook search bar
searchQueryClassFingerprint.matchOrThrow().let { searchQueryClassFingerprint.methodOrThrow().apply {
it.method.apply { val searchQueryIndex = indexOfStringIsEmptyInstruction(this) - 1
val searchQueryIndex = it.patternMatch!!.startIndex + 1 val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
findMethodOrThrow(searchQueryClass).apply { findMethodOrThrow(searchQueryClass).apply {
val smaliInstructions = val smaliInstructions =
"""
if-eqz v0, :ignore
iget-object v0, v0, $searchQueryFieldReference
if-eqz v0, :ignore
return-object v0
:ignore
const-string v0, ""
return-object v0
""" """
if-eqz v0, :ignore
iget-object v0, v0, $searchQueryFieldReference
if-eqz v0, :ignore
return-object v0
:ignore
const-string v0, ""
return-object v0
"""
addStaticFieldToExtension( addStaticFieldToExtension(
EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR, EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR,
"getSearchQuery", "getSearchQuery",
"searchQueryClass", "searchQueryClass",
definingClass, definingClass,
smaliInstructions smaliInstructions
) )
}
} }
} }

View File

@ -23,10 +23,22 @@ var is_19_23_or_greater = false
private set private set
var is_19_25_or_greater = false var is_19_25_or_greater = false
private set private set
var is_19_26_or_greater = false
private set
var is_19_28_or_greater = false var is_19_28_or_greater = false
private set private set
var is_19_29_or_greater = false
private set
var is_19_32_or_greater = false var is_19_32_or_greater = false
private set private set
var is_19_34_or_greater = false
private set
var is_19_36_or_greater = false
private set
var is_19_41_or_greater = false
private set
var is_19_43_or_greater = false
private set
var is_19_44_or_greater = false var is_19_44_or_greater = false
private set private set
@ -53,8 +65,14 @@ val versionCheckPatch = resourcePatch(
is_19_15_or_greater = 241602000 <= playStoreServicesVersion is_19_15_or_greater = 241602000 <= playStoreServicesVersion
is_19_23_or_greater = 242402000 <= playStoreServicesVersion is_19_23_or_greater = 242402000 <= playStoreServicesVersion
is_19_25_or_greater = 242599000 <= playStoreServicesVersion is_19_25_or_greater = 242599000 <= playStoreServicesVersion
is_19_26_or_greater = 242705000 <= playStoreServicesVersion
is_19_28_or_greater = 242905000 <= playStoreServicesVersion is_19_28_or_greater = 242905000 <= playStoreServicesVersion
is_19_29_or_greater = 243005000 <= playStoreServicesVersion
is_19_32_or_greater = 243305000 <= playStoreServicesVersion is_19_32_or_greater = 243305000 <= playStoreServicesVersion
is_19_34_or_greater = 243499000 <= playStoreServicesVersion
is_19_36_or_greater = 243705000 <= playStoreServicesVersion
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
is_19_44_or_greater = 244505000 <= playStoreServicesVersion is_19_44_or_greater = 244505000 <= playStoreServicesVersion
} }
} }

View File

@ -120,6 +120,8 @@ var menuItemView = -1L
private set private set
var metaPanel = -1L var metaPanel = -1L
private set private set
var miniplayerMaxSize = -1L
private set
var modernMiniPlayerClose = -1L var modernMiniPlayerClose = -1L
private set private set
var modernMiniPlayerExpand = -1L var modernMiniPlayerExpand = -1L
@ -444,6 +446,10 @@ internal val sharedResourceIdPatch = resourcePatch(
ID, ID,
"metapanel" "metapanel"
] ]
miniplayerMaxSize = resourceMappings[
DIMEN,
"miniplayer_max_size",
]
modernMiniPlayerClose = resourceMappings[ modernMiniPlayerClose = resourceMappings[
ID, ID,
"modern_miniplayer_close" "modern_miniplayer_close"

View File

@ -1,26 +1,10 @@
package app.revanced.patches.youtube.utils.toolbar package app.revanced.patches.youtube.utils.toolbar
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.resourceid.menuItemView
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val toolBarButtonFingerprint = legacyFingerprint(
name = "toolBarButtonFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/MenuItem;"),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL
),
literals = listOf(menuItemView),
)
internal val toolBarPatchFingerprint = legacyFingerprint( internal val toolBarPatchFingerprint = legacyFingerprint(
name = "toolBarPatchFingerprint", name = "toolBarPatchFingerprint",
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC, accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,

View File

@ -6,12 +6,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.util.fingerprint.matchOrThrow import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.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.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"$UTILS_PATH/ToolBarPatch;" "$UTILS_PATH/ToolBarPatch;"
@ -24,30 +30,35 @@ val toolBarHookPatch = bytecodePatch(
dependsOn(sharedResourceIdPatch) dependsOn(sharedResourceIdPatch)
execute { execute {
toolBarButtonFingerprint.matchOrThrow().let { toolBarButtonFingerprint.methodOrThrow().apply {
it.method.apply { val getDrawableIndex = indexOfGetDrawableInstruction(this)
val replaceIndex = it.patternMatch!!.startIndex val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
val freeIndex = it.patternMatch!!.endIndex - 1 opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.returnType == "I"
val replaceReference = getInstruction<ReferenceInstruction>(replaceIndex).reference
val replaceRegister =
getInstruction<FiveRegisterInstruction>(replaceIndex).registerC
val enumRegister = getInstruction<FiveRegisterInstruction>(replaceIndex).registerD
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
val imageViewIndex = replaceIndex + 2
val imageViewReference =
getInstruction<ReferenceInstruction>(imageViewIndex).reference
addInstructions(
replaceIndex + 1, """
iget-object v$freeRegister, p0, $imageViewReference
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
"""
)
removeInstruction(replaceIndex)
} }
val freeIndex = getDrawableIndex - 1
val replaceReference = getInstruction<ReferenceInstruction>(enumOrdinalIndex).reference
val replaceRegister =
getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerC
val enumRegister = getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerD
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
val imageViewIndex = indexOfFirstInstructionReversedOrThrow(enumOrdinalIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/widget/ImageView;"
}
val imageViewReference =
getInstruction<ReferenceInstruction>(imageViewIndex).reference
addInstructions(
enumOrdinalIndex + 1, """
iget-object v$freeRegister, p0, $imageViewReference
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
"""
)
removeInstruction(enumOrdinalIndex)
} }
toolbarMethod = toolBarPatchFingerprint.methodOrThrow() toolbarMethod = toolBarPatchFingerprint.methodOrThrow()

View File

@ -5,7 +5,6 @@ import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatchContext import app.revanced.patcher.patch.ResourcePatchContext
import app.revanced.patcher.util.Document import app.revanced.patcher.util.Document
import org.w3c.dom.Attr
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.Node import org.w3c.dom.Node
import org.w3c.dom.NodeList import org.w3c.dom.NodeList
@ -44,26 +43,6 @@ fun Node.cloneNodes(parent: Node) {
parent.removeChild(this) parent.removeChild(this)
} }
/**
* Returns a sequence for all child nodes.
*/
fun NodeList.asSequence() = (0 until this.length).asSequence().map { this.item(it) }
/**
* Returns a sequence for all child nodes.
*/
@Suppress("UNCHECKED_CAST")
fun Node.childElementsSequence() =
this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } as Sequence<Element>
/**
* Performs the given [action] on each child element.
*/
inline fun Node.forEachChildElement(action: (Element) -> Unit) =
childElementsSequence().forEach {
action(it)
}
/** /**
* Recursively traverse the DOM tree starting from the given root node. * Recursively traverse the DOM tree starting from the given root node.
* *
@ -74,20 +53,29 @@ fun Node.doRecursively(action: (Node) -> Unit) {
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action) for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
} }
fun String.startsWithAny(vararg prefixes: String): Boolean {
for (prefix in prefixes)
if (this.startsWith(prefix))
return true
return false
}
fun List<String>.getResourceGroup(fileNames: Array<String>) = map { directory -> fun List<String>.getResourceGroup(fileNames: Array<String>) = map { directory ->
ResourceGroup( ResourceGroup(
directory, *fileNames directory, *fileNames
) )
} }
fun ResourcePatchContext.getAdaptiveIconResourceFile(path: String, tag: String): String {
document(path).use { document ->
val adaptiveIcon = document
.getElementsByTagName("adaptive-icon")
.item(0) as Element
val childNodes = adaptiveIcon.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i)
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
return node.getAttribute("android:drawable").split("/")[1]
}
}
throw PatchException("Element not found: $tag")
}
}
fun ResourcePatchContext.appendAppVersion(appVersion: String) { fun ResourcePatchContext.appendAppVersion(appVersion: String) {
addEntryValues( addEntryValues(
"revanced_spoof_app_version_target_entries", "revanced_spoof_app_version_target_entries",
@ -227,14 +215,6 @@ fun ResourcePatchContext.removeStringsElements(
} }
} }
fun Node.insertFirst(node: Node) {
if (hasChildNodes()) {
insertBefore(node, firstChild)
} else {
appendChild(node)
}
}
fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) { fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName) val child = ownerDocument.createElement(tagName)
child.block() child.block()
@ -294,21 +274,6 @@ internal fun inputStreamFromBundledResource(
*/ */
class ResourceGroup(val resourceDirectoryName: String, vararg val resources: String) class ResourceGroup(val resourceDirectoryName: String, vararg val resources: String)
/**
* Iterate through the children of a node by its tag.
* @param resource The xml resource.
* @param targetTag The target xml node.
* @param callback The callback to call when iterating over the nodes.
*/
fun ResourcePatchContext.iterateXmlNodeChildren(
resource: String,
targetTag: String,
callback: (node: Node) -> Unit,
) = document(classLoader.getResourceAsStream(resource)!!).use { document ->
val stringsNode = document.getElementsByTagName(targetTag).item(0).childNodes
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
}
/** /**
* Copy resources from the current class loader to the resource directory. * Copy resources from the current class loader to the resource directory.
* @param resourceDirectory The directory of the resource. * @param resourceDirectory The directory of the resource.
@ -383,12 +348,3 @@ internal fun NodeList.findElementByAttributeValue(attributeName: String, value:
internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String) = internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String) =
findElementByAttributeValue(attributeName, value) findElementByAttributeValue(attributeName, value)
?: throw PatchException("Could not find: $attributeName $value") ?: throw PatchException("Could not find: $attributeName $value")
internal fun Element.copyAttributesFrom(oldContainer: Element) {
// Copy attributes from the old element to the new element
val attributes = oldContainer.attributes
for (i in 0 until attributes.length) {
val attr = attributes.item(i) as Attr
setAttribute(attr.name, attr.value)
}
}

View File

@ -163,7 +163,8 @@
<item>MEMBERSHIPS_SHORTS_ONLY</item> <item>MEMBERSHIPS_SHORTS_ONLY</item>
<item>MEMBERSHIPS_LIVESTREAMS_ONLY</item> <item>MEMBERSHIPS_LIVESTREAMS_ONLY</item>
</string-array> </string-array>
<string-array name="revanced_miniplayer_type_19_15_entries"> <string-array name="revanced_miniplayer_type_19_43_entries">
<item>@string/revanced_miniplayer_type_entry_0</item>
<item>@string/revanced_miniplayer_type_entry_1</item> <item>@string/revanced_miniplayer_type_entry_1</item>
<item>@string/revanced_miniplayer_type_entry_2</item> <item>@string/revanced_miniplayer_type_entry_2</item>
<item>@string/revanced_miniplayer_type_entry_3</item> <item>@string/revanced_miniplayer_type_entry_3</item>
@ -171,22 +172,39 @@
<item>@string/revanced_miniplayer_type_entry_5</item> <item>@string/revanced_miniplayer_type_entry_5</item>
<item>@string/revanced_miniplayer_type_entry_6</item> <item>@string/revanced_miniplayer_type_entry_6</item>
</string-array> </string-array>
<string-array name="revanced_miniplayer_type_19_15_entry_values"> <string-array name="revanced_miniplayer_type_19_43_entry_values">
<item>DISABLED</item>
<item>ORIGINAL</item> <item>ORIGINAL</item>
<item>PHONE</item> <item>MINIMAL</item>
<item>TABLET</item> <item>TABLET</item>
<item>MODERN_1</item> <item>MODERN_1</item>
<item>MODERN_2</item> <item>MODERN_2</item>
<item>MODERN_3</item> <item>MODERN_3</item>
</string-array> </string-array>
<string-array name="revanced_miniplayer_type_legacy_entries"> <string-array name="revanced_miniplayer_type_19_16_entries">
<item>@string/revanced_miniplayer_type_entry_1</item>
<item>@string/revanced_miniplayer_type_entry_2</item>
<item>@string/revanced_miniplayer_type_entry_3</item>
<item>@string/revanced_miniplayer_type_entry_4</item>
<item>@string/revanced_miniplayer_type_entry_5</item>
<item>@string/revanced_miniplayer_type_entry_6</item>
</string-array>
<string-array name="revanced_miniplayer_type_19_16_entry_values">
<item>ORIGINAL</item>
<item>MINIMAL</item>
<item>TABLET</item>
<item>MODERN_1</item>
<item>MODERN_2</item>
<item>MODERN_3</item>
</string-array>
<string-array name="revanced_miniplayer_type_19_14_entries">
<item>@string/revanced_miniplayer_type_entry_1</item> <item>@string/revanced_miniplayer_type_entry_1</item>
<item>@string/revanced_miniplayer_type_entry_2</item> <item>@string/revanced_miniplayer_type_entry_2</item>
<item>@string/revanced_miniplayer_type_entry_3</item> <item>@string/revanced_miniplayer_type_entry_3</item>
</string-array> </string-array>
<string-array name="revanced_miniplayer_type_legacy_entry_values"> <string-array name="revanced_miniplayer_type_19_14_entry_values">
<item>ORIGINAL</item> <item>ORIGINAL</item>
<item>PHONE</item> <item>MINIMAL</item>
<item>TABLET</item> <item>TABLET</item>
</string-array> </string-array>
<string-array name="revanced_return_youtube_username_display_format_entries"> <string-array name="revanced_return_youtube_username_display_format_entries">

View File

@ -449,30 +449,49 @@ Some components may not be hidden."</string>
<string name="revanced_preference_screen_miniplayer_summary">Change the style of the in app minimized player.</string> <string name="revanced_preference_screen_miniplayer_summary">Change the style of the in app minimized player.</string>
<string name="revanced_miniplayer_type_title">Miniplayer type</string> <string name="revanced_miniplayer_type_title">Miniplayer type</string>
<string name="revanced_miniplayer_type_entry_0">Disabled</string>
<string name="revanced_miniplayer_type_entry_1">Original</string> <string name="revanced_miniplayer_type_entry_1">Original</string>
<string name="revanced_miniplayer_type_entry_2">Phone</string> <string name="revanced_miniplayer_type_entry_2">Minimal</string>
<string name="revanced_miniplayer_type_entry_3">Tablet</string> <string name="revanced_miniplayer_type_entry_3">Tablet</string>
<string name="revanced_miniplayer_type_entry_4">Modern 1</string> <string name="revanced_miniplayer_type_entry_4">Modern 1</string>
<string name="revanced_miniplayer_type_entry_5">Modern 2</string> <string name="revanced_miniplayer_type_entry_5">Modern 2</string>
<string name="revanced_miniplayer_type_entry_6">Modern 3</string> <string name="revanced_miniplayer_type_entry_6">Modern 3</string>
<string name="revanced_miniplayer_enable_double_tap_action_title">Enable double-tap action</string> <string name="revanced_miniplayer_rounded_corners_title">Enable rounded corners</string>
<string name="revanced_miniplayer_enable_double_tap_action_summary_on">"Double-tap action is enabled. <string name="revanced_miniplayer_rounded_corners_summary_on">Corners are rounded.</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Corners are square.</string>
<string name="revanced_miniplayer_double_tap_action_title">Enable double-tap and pinch to resize</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Double-tap action and pinch to resize is enabled.
• Double-tap to change the minimized video to a larger size. • Double tap to increase miniplayer size.
• Double-tap once more to change to the original size."</string> • Double tap again to restore original size."</string>
<string name="revanced_miniplayer_enable_double_tap_action_summary_off">Double-tap action is disabled.</string> <string name="revanced_miniplayer_double_tap_action_summary_off">Double-tap action and pinch to resize is disabled.</string>
<string name="revanced_miniplayer_enable_drag_and_drop_title">Enable drag and drop</string> <string name="revanced_miniplayer_drag_and_drop_title">Enable drag and drop</string>
<string name="revanced_miniplayer_enable_drag_and_drop_summary_on">Drag and drop is enabled.</string> <string name="revanced_miniplayer_drag_and_drop_summary_on">"Drag and drop is enabled.
<string name="revanced_miniplayer_enable_drag_and_drop_summary_off">Drag and drop is disabled.</string>
<string name="revanced_miniplayer_hide_expand_close_title">Hide expand and close buttons</string> Miniplayer can be dragged to any corner of the screen."</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Buttons are hidden.\n(swipe miniplayer to expand or close)</string> <string name="revanced_miniplayer_drag_and_drop_summary_off">Drag and drop is disabled.</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Expand and close buttons are shown.</string> <string name="revanced_miniplayer_horizontal_drag_title">Enable horizontal drag gesture.</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Horizontal drag gesture enabled.
Miniplayer can be dragged off screen to the left or right."</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Horizontal drag gesture disabled.</string>
<string name="revanced_miniplayer_hide_expand_close_title">Hide close button</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Close button is hidden.</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Close button is shown.</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Hide expand and close buttons</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Buttons are hidden.
Swipe to expand or close."</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Expand and close buttons are shown.</string>
<string name="revanced_miniplayer_hide_subtext_title">Hide subtexts</string> <string name="revanced_miniplayer_hide_subtext_title">Hide subtexts</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Subtexts are hidden.</string> <string name="revanced_miniplayer_hide_subtext_summary_on">Subtexts are hidden.</string>
<string name="revanced_miniplayer_hide_subtext_summary_off">Subtexts are shown.</string> <string name="revanced_miniplayer_hide_subtext_summary_off">Subtexts are shown.</string>
<string name="revanced_miniplayer_hide_rewind_forward_title">Hide skip forward and back buttons</string> <string name="revanced_miniplayer_hide_rewind_forward_title">Hide skip forward and back buttons</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Skip forward and back are hidden.</string> <string name="revanced_miniplayer_hide_rewind_forward_summary_on">Skip forward and back are hidden.</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Skip forward and back are shown.</string> <string name="revanced_miniplayer_hide_rewind_forward_summary_off">Skip forward and back are shown.</string>
<string name="revanced_miniplayer_width_dip_title">Initial size</string>
<string name="revanced_miniplayer_width_dip_summary">Initial on screen size, in pixels.</string>
<string name="revanced_miniplayer_width_dip_invalid_toast">Pixel size must be between %1$s and %2$s.</string>
<string name="revanced_miniplayer_opacity_title">Overlay opacity</string> <string name="revanced_miniplayer_opacity_title">Overlay opacity</string>
<string name="revanced_miniplayer_opacity_summary">Opacity value between 0-100, where 0 is transparent.</string> <string name="revanced_miniplayer_opacity_summary">Opacity value between 0-100, where 0 is transparent.</string>
<string name="revanced_miniplayer_opacity_invalid_toast">Miniplayer overlay opacity must be between 0-100.</string> <string name="revanced_miniplayer_opacity_invalid_toast">Miniplayer overlay opacity must be between 0-100.</string>

View File

@ -160,19 +160,42 @@
</PreferenceScreen>SETTINGS: HOOK_BUTTONS --> </PreferenceScreen>SETTINGS: HOOK_BUTTONS -->
<!-- SETTINGS: MINIPLAYER_TYPE_MODERN <!-- SETTINGS: MINIPLAYER_TYPE_MODERN
<PreferenceScreen android:title="@string/revanced_preference_screen_miniplayer_title" android:key="revanced_preference_screen_miniplayer" android:summary="@string/revanced_preference_screen_miniplayer_summary"> <PreferenceScreen android:title="@string/revanced_preference_screen_miniplayer_title" android:key="revanced_preference_screen_miniplayer" android:summary="@string/revanced_preference_screen_miniplayer_summary">SETTINGS: MINIPLAYER_TYPE_MODERN -->
<ListPreference android:entries="@array/revanced_miniplayer_type_19_15_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_19_15_entry_values" />SETTINGS: MINIPLAYER_TYPE_MODERN -->
<!-- SETTINGS: MINIPLAYER_TYPE_19_16
<ListPreference android:entries="@array/revanced_miniplayer_type_19_16_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_19_16_entry_values" />SETTINGS: MINIPLAYER_TYPE_19_16 -->
<!-- SETTINGS: MINIPLAYER_TYPE_19_43
<ListPreference android:entries="@array/revanced_miniplayer_type_19_43_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_19_43_entry_values" />SETTINGS: MINIPLAYER_TYPE_19_43 -->
<!-- SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION <!-- SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION
<SwitchPreference android:title="@string/revanced_miniplayer_enable_double_tap_action_title" android:key="revanced_miniplayer_enable_double_tap_action" android:summaryOn="@string/revanced_miniplayer_enable_double_tap_action_summary_on" android:summaryOff="@string/revanced_miniplayer_enable_double_tap_action_summary_off" />SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION --> <SwitchPreference android:title="@string/revanced_miniplayer_double_tap_action_title" android:key="revanced_miniplayer_double_tap_action" android:summaryOn="@string/revanced_miniplayer_double_tap_action_summary_on" android:summaryOff="@string/revanced_miniplayer_double_tap_action_summary_off" />SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION -->
<!-- SETTINGS: MINIPLAYER_DRAG_AND_DROP <!-- SETTINGS: MINIPLAYER_DRAG_AND_DROP
<SwitchPreference android:title="@string/revanced_miniplayer_enable_drag_and_drop_title" android:key="revanced_miniplayer_enable_drag_and_drop" android:summaryOn="@string/revanced_miniplayer_enable_drag_and_drop_summary_on" android:summaryOff="@string/revanced_miniplayer_enable_drag_and_drop_summary_off" />SETTINGS: MINIPLAYER_DRAG_AND_DROP --> <SwitchPreference android:title="@string/revanced_miniplayer_drag_and_drop_title" android:key="revanced_miniplayer_drag_and_drop" android:summaryOn="@string/revanced_miniplayer_drag_and_drop_summary_on" android:summaryOff="@string/revanced_miniplayer_drag_and_drop_summary_off" />SETTINGS: MINIPLAYER_DRAG_AND_DROP -->
<!-- SETTINGS: MINIPLAYER_HORIZONTAL_DRAG
<SwitchPreference android:title="@string/revanced_miniplayer_horizontal_drag_title" android:key="revanced_miniplayer_horizontal_drag" android:summaryOn="@string/revanced_miniplayer_horizontal_drag_summary_on" android:summaryOff="@string/revanced_miniplayer_horizontal_drag_summary_off" />SETTINGS: MINIPLAYER_HORIZONTAL_DRAG -->
<!-- SETTINGS: MINIPLAYER_ROUNDED_CONERS
<SwitchPreference android:title="@string/revanced_miniplayer_rounded_corners_title" android:key="revanced_miniplayer_rounded_corners" android:summaryOn="@string/revanced_miniplayer_rounded_corners_summary_on" android:summaryOff="@string/revanced_miniplayer_rounded_corners_summary_off" />SETTINGS: MINIPLAYER_ROUNDED_CONERS -->
<!-- SETTINGS: MINIPLAYER_TYPE_MODERN
<SwitchPreference android:title="@string/revanced_miniplayer_hide_subtext_title" android:key="revanced_miniplayer_hide_subtext" android:summaryOn="@string/revanced_miniplayer_hide_subtext_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_subtext_summary_off" />SETTINGS: MINIPLAYER_TYPE_MODERN -->
<!-- SETTINGS: MINIPLAYER_EXPAND_CLOSE_19_25
<SwitchPreference android:title="@string/revanced_miniplayer_hide_expand_close_title" android:key="revanced_miniplayer_hide_expand_close" android:summaryOn="@string/revanced_miniplayer_hide_expand_close_legacy_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_expand_close_legacy_summary_off" />SETTINGS: MINIPLAYER_EXPAND_CLOSE_19_25 -->
<!-- SETTINGS: MINIPLAYER_EXPAND_CLOSE_19_26
<SwitchPreference android:title="@string/revanced_miniplayer_hide_expand_close_title" android:key="revanced_miniplayer_hide_expand_close" android:summaryOn="@string/revanced_miniplayer_hide_expand_close_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_expand_close_summary_off" />SETTINGS: MINIPLAYER_EXPAND_CLOSE_19_26 -->
<!-- SETTINGS: MINIPLAYER_REWIND_FORWARD
<SwitchPreference android:title="@string/revanced_miniplayer_hide_rewind_forward_title" android:key="revanced_miniplayer_hide_rewind_forward" android:summaryOn="@string/revanced_miniplayer_hide_rewind_forward_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_rewind_forward_summary_off" />SETTINGS: MINIPLAYER_REWIND_FORWARD -->
<!-- SETTINGS: MINIPLAYER_WIDTH_DIP
<app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_miniplayer_width_dip_title" android:key="revanced_miniplayer_width_dip" android:summary="@string/revanced_miniplayer_width_dip_summary" android:inputType="number" />SETTINGS: MINIPLAYER_WIDTH_DIP -->
<!-- SETTINGS: MINIPLAYER_TYPE_MODERN <!-- SETTINGS: MINIPLAYER_TYPE_MODERN
<SwitchPreference android:title="@string/revanced_miniplayer_hide_expand_close_title" android:key="revanced_miniplayer_hide_expand_close" android:summaryOn="@string/revanced_miniplayer_hide_expand_close_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_expand_close_summary_off" />
<SwitchPreference android:title="@string/revanced_miniplayer_hide_subtext_title" android:key="revanced_miniplayer_hide_subtext" android:summaryOn="@string/revanced_miniplayer_hide_subtext_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_subtext_summary_off" />
<SwitchPreference android:title="@string/revanced_miniplayer_hide_rewind_forward_title" android:key="revanced_miniplayer_hide_rewind_forward" android:summaryOn="@string/revanced_miniplayer_hide_rewind_forward_summary_on" android:summaryOff="@string/revanced_miniplayer_hide_rewind_forward_summary_off" />
<app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_miniplayer_opacity_title" android:key="revanced_miniplayer_opacity" android:summary="@string/revanced_miniplayer_opacity_summary" android:inputType="number" /> <app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_miniplayer_opacity_title" android:key="revanced_miniplayer_opacity" android:summary="@string/revanced_miniplayer_opacity_summary" android:inputType="number" />
</PreferenceScreen>SETTINGS: MINIPLAYER_TYPE_MODERN --> </PreferenceScreen>SETTINGS: MINIPLAYER_TYPE_MODERN -->
@ -266,8 +289,8 @@
<SwitchPreference android:title="@string/revanced_hide_gray_separator_title" android:key="revanced_hide_gray_separator" android:summaryOn="@string/revanced_hide_gray_separator_summary_on" android:summaryOff="@string/revanced_hide_gray_separator_summary_off" /> <SwitchPreference android:title="@string/revanced_hide_gray_separator_title" android:key="revanced_hide_gray_separator" android:summaryOn="@string/revanced_hide_gray_separator_summary_on" android:summaryOff="@string/revanced_hide_gray_separator_summary_off" />
<SwitchPreference android:title="@string/revanced_hide_snack_bar_title" android:key="revanced_hide_snack_bar" android:summaryOn="@string/revanced_hide_snack_bar_summary_on" android:summaryOff="@string/revanced_hide_snack_bar_summary_off" />SETTINGS: HIDE_LAYOUT_COMPONENTS --> <SwitchPreference android:title="@string/revanced_hide_snack_bar_title" android:key="revanced_hide_snack_bar" android:summaryOn="@string/revanced_hide_snack_bar_summary_on" android:summaryOff="@string/revanced_hide_snack_bar_summary_off" />SETTINGS: HIDE_LAYOUT_COMPONENTS -->
<!-- SETTINGS: MINIPLAYER_TYPE_LEGACY <!-- SETTINGS: MINIPLAYER_TYPE_19_14
<ListPreference android:entries="@array/revanced_miniplayer_type_legacy_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_legacy_entry_values" />SETTINGS: MINIPLAYER_TYPE_LEGACY --> <ListPreference android:entries="@array/revanced_miniplayer_type_19_14_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_19_14_entry_values" />SETTINGS: MINIPLAYER_TYPE_19_14 -->
<!-- SETTINGS: REMOVE_VIEWER_DISCRETION_DIALOG <!-- SETTINGS: REMOVE_VIEWER_DISCRETION_DIALOG
<SwitchPreference android:title="@string/revanced_remove_viewer_discretion_dialog_title" android:key="revanced_remove_viewer_discretion_dialog" android:summary="@string/revanced_remove_viewer_discretion_dialog_summary" />SETTINGS: REMOVE_VIEWER_DISCRETION_DIALOG --> <SwitchPreference android:title="@string/revanced_remove_viewer_discretion_dialog_title" android:key="revanced_remove_viewer_discretion_dialog" android:summary="@string/revanced_remove_viewer_discretion_dialog_summary" />SETTINGS: REMOVE_VIEWER_DISCRETION_DIALOG -->