mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-13 05:37:40 +02:00
feat(YouTube): Support version 19.34.42
This commit is contained in:
@ -26,9 +26,4 @@ public class InitializationPatch {
|
||||
showRestartDialog(mActivity, "revanced_extended_restart_first_run", 3000);
|
||||
Utils.runOnMainThreadDelayed(() -> BaseSettings.SETTINGS_INITIALIZED.save(true), 3000);
|
||||
}
|
||||
|
||||
public static void setDeviceInformation(@NonNull Activity mActivity) {
|
||||
ExtendedUtils.setApplicationLabel();
|
||||
ExtendedUtils.setVersionName();
|
||||
}
|
||||
}
|
@ -248,8 +248,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
var appName = ExtendedUtils.getApplicationLabel();
|
||||
var versionName = ExtendedUtils.getVersionName();
|
||||
var appName = ExtendedUtils.getAppLabel();
|
||||
var versionName = ExtendedUtils.getAppVersionName();
|
||||
var formatDate = dateFormat.format(new Date(System.currentTimeMillis()));
|
||||
var fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate);
|
||||
|
||||
|
@ -8,16 +8,25 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
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() {
|
||||
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() {
|
||||
return versionName;
|
||||
public static String getAppVersionName() {
|
||||
final PackageInfo packageInfo = getPackageInfo();
|
||||
if (packageInfo != null) {
|
||||
return packageInfo.versionName;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPackageEnabled(@NonNull String packageName) {
|
||||
@ -30,32 +39,11 @@ public class PackageUtils extends Utils {
|
||||
}
|
||||
|
||||
public static boolean isTablet() {
|
||||
return smallestScreenWidthDp >= 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;
|
||||
}
|
||||
return getSmallestScreenWidthDp() >= 600;
|
||||
}
|
||||
|
||||
public static int getSmallestScreenWidthDp() {
|
||||
return smallestScreenWidthDp;
|
||||
return context.getResources().getConfiguration().smallestScreenWidthDp;
|
||||
}
|
||||
|
||||
// utils
|
||||
|
@ -78,8 +78,8 @@ public class FeedPatch {
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean hideFloatingButton() {
|
||||
return Settings.HIDE_FLOATING_BUTTON.get();
|
||||
public static String hideFloatingButton(String fab) {
|
||||
return Settings.HIDE_FLOATING_BUTTON.get() ? null : fab;
|
||||
}
|
||||
|
||||
public static void hideLatestVideosButton(View view) {
|
||||
|
@ -1,15 +1,17 @@
|
||||
package app.revanced.extension.youtube.patches.general;
|
||||
|
||||
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_3;
|
||||
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.ORIGINAL;
|
||||
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||
import static app.revanced.extension.youtube.patches.general.MiniplayerPatch.MiniplayerType.*;
|
||||
import static app.revanced.extension.youtube.utils.ExtendedUtils.IS_19_20_OR_GREATER;
|
||||
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 android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.ResourceUtils;
|
||||
import app.revanced.extension.shared.utils.Utils;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "SpellCheckingInspection"})
|
||||
public final class MiniplayerPatch {
|
||||
|
||||
/**
|
||||
@ -27,14 +30,28 @@ public final class MiniplayerPatch {
|
||||
*/
|
||||
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),
|
||||
/**
|
||||
* Exactly the same as MINIMAL and only here for migration of user settings.
|
||||
* Eventually this should be deleted.
|
||||
*/
|
||||
@Deprecated
|
||||
PHONE(false, null),
|
||||
MINIMAL(false, null),
|
||||
TABLET(true, null),
|
||||
MODERN_1(null, 1),
|
||||
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.
|
||||
@ -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}.
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
(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 =
|
||||
CURRENT_TYPE == MODERN_1 && 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;
|
||||
CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
||||
|
||||
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 =
|
||||
(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 =
|
||||
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;
|
||||
|
||||
static {
|
||||
final int opacity = validateValue(
|
||||
Settings.MINIPLAYER_OPACITY,
|
||||
0,
|
||||
100,
|
||||
"revanced_miniplayer_opacity_invalid_toast"
|
||||
);
|
||||
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Settings.MINIPLAYER_TYPE.get().isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean enableMiniplayerDragAndDrop() {
|
||||
public static boolean enableMiniplayerDragAndDrop(boolean original) {
|
||||
if (CURRENT_TYPE == ORIGINAL) {
|
||||
return original;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
@ -169,22 +312,26 @@ public final class MiniplayerPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean hideMiniplayerSubTexts(View view) {
|
||||
// Different subviews are passed in, but only TextView and layouts are of interest here.
|
||||
final boolean hideView = HIDE_SUBTEXT_ENABLED && (view instanceof TextView || view instanceof LinearLayout);
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(hideView, view);
|
||||
return hideView || view == null;
|
||||
public static void hideMiniplayerSubTexts(View view) {
|
||||
try {
|
||||
// Different subviews are passed in, but only TextView is of interest here.
|
||||
if (HIDE_SUBTEXT_ENABLED && view instanceof TextView) {
|
||||
Logger.printDebug(() -> "Hiding subtext view");
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(true, view);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "hideMiniplayerSubTexts failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void playerOverlayGroupCreated(View group) {
|
||||
// Modern 2 has an half broken subtitle that is always present.
|
||||
// Always hide it to make the miniplayer mostly usable.
|
||||
if (CURRENT_TYPE == MODERN_2 && MODERN_OVERLAY_SUBTITLE_TEXT != 0) {
|
||||
if (group instanceof ViewGroup viewGroup) {
|
||||
View subtitleText = Utils.getChildView(viewGroup, true,
|
||||
try {
|
||||
if (HIDE_BROKEN_MODERN_2_SUBTITLE && MODERN_OVERLAY_SUBTITLE_TEXT != 0) {
|
||||
if (group instanceof ViewGroup) {
|
||||
View subtitleText = Utils.getChildView((ViewGroup) group, true,
|
||||
view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT);
|
||||
|
||||
if (subtitleText != null) {
|
||||
@ -193,5 +340,8 @@ public final class MiniplayerPatch {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "playerOverlayGroupCreated failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,9 +31,6 @@ public class InitializationPatch {
|
||||
}
|
||||
|
||||
public static void setExtendedUtils(@NonNull Activity mActivity) {
|
||||
ExtendedUtils.setApplicationLabel();
|
||||
ExtendedUtils.setSmallestScreenWidthDp();
|
||||
ExtendedUtils.setVersionName();
|
||||
ExtendedUtils.setPlayerFlyoutMenuAdditionalSettings();
|
||||
}
|
||||
}
|
@ -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_2;
|
||||
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.SKIP_AUTOMATICALLY;
|
||||
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.StartPage;
|
||||
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.misc.SpoofStreamingDataPatch;
|
||||
import app.revanced.extension.youtube.patches.misc.WatchHistoryPatch.WatchHistoryType;
|
||||
@ -167,11 +169,15 @@ public class Settings extends BaseSettings {
|
||||
|
||||
// PreferenceScreen: General - Miniplayer
|
||||
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));
|
||||
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_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true);
|
||||
private static final Setting.Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
|
||||
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_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_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));
|
||||
|
||||
// PreferenceScreen: General - Navigation bar
|
||||
|
@ -593,8 +593,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
|
||||
private void exportActivity() {
|
||||
@SuppressLint("SimpleDateFormat") final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
final String appName = ExtendedUtils.getApplicationLabel();
|
||||
final String versionName = ExtendedUtils.getVersionName();
|
||||
final String appName = ExtendedUtils.getAppLabel();
|
||||
final String versionName = ExtendedUtils.getAppVersionName();
|
||||
final String formatDate = dateFormat.format(new Date(System.currentTimeMillis()));
|
||||
final String fileName = String.format("%s_v%s_%s.txt", appName, versionName, formatDate);
|
||||
|
||||
|
@ -12,6 +12,12 @@ import app.revanced.extension.shared.utils.PackageUtils;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
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) {
|
||||
int value = settings.get();
|
||||
|
@ -14,6 +14,7 @@ import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.getAdaptiveIconResourceFile
|
||||
import app.revanced.util.getResourceGroup
|
||||
import app.revanced.util.underBarOrThrow
|
||||
import org.w3c.dom.Element
|
||||
@ -248,27 +249,11 @@ val customBrandingIconPatch = resourcePatch(
|
||||
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(
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("background"),
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("foreground")
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "background"),
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "foreground")
|
||||
).forEach { (oldIconResourceFile, newIconResourceFile) ->
|
||||
if (oldIconResourceFile != newIconResourceFile) {
|
||||
mipmapDirectories.forEach {
|
||||
val mipmapDirectory = resourceDirectory.resolve(it)
|
||||
Files.move(
|
||||
@ -282,6 +267,7 @@ val customBrandingIconPatch = resourcePatch(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -112,10 +112,6 @@ private val settingsBytecodePatch = bytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
injectOnCreateMethodCall(
|
||||
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
|
||||
"setDeviceInformation"
|
||||
)
|
||||
injectOnCreateMethodCall(
|
||||
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
|
||||
"onCreate"
|
||||
|
@ -147,18 +147,22 @@ val feedComponentsPatch = bytecodePatch(
|
||||
// region patch for hide floating button
|
||||
|
||||
onCreateMethod.apply {
|
||||
val fabIndex = indexOfFirstInstructionOrThrow {
|
||||
val stringIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.CONST_STRING &&
|
||||
getReference<StringReference>()?.string == "fab"
|
||||
}
|
||||
val fabRegister = getInstruction<OneRegisterInstruction>(fabIndex).registerA
|
||||
val jumpIndex = indexOfFirstInstructionOrThrow(fabIndex + 1, Opcode.CONST_STRING)
|
||||
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(stringIndex) {
|
||||
opcode == Opcode.INVOKE_DIRECT &&
|
||||
getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex, Opcode.CONST_STRING)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
fabIndex, """
|
||||
invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton()Z
|
||||
move-result v$fabRegister
|
||||
if-nez v$fabRegister, :hide
|
||||
insertIndex, """
|
||||
invoke-static {v$stringRegister}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$stringRegister
|
||||
if-eqz v$stringRegister, :hide
|
||||
""", ExternalLabel("hide", getInstruction(jumpIndex))
|
||||
)
|
||||
}
|
||||
|
@ -2,20 +2,18 @@
|
||||
|
||||
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.miniplayerMaxSize
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerForwardButton
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerRewindButton
|
||||
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
|
||||
import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWhite
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint(
|
||||
name = "miniplayerDimensionsCalculatorParentFingerprint",
|
||||
@ -25,6 +23,9 @@ internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint
|
||||
literals = listOf(floatyBarTopMargin),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernAddViewListenerFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
@ -32,6 +33,9 @@ internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
|
||||
parameters = listOf("Landroid/view/View;")
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernCloseButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -40,41 +44,33 @@ internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerClose),
|
||||
)
|
||||
|
||||
private var constructorMethodCount = 0
|
||||
|
||||
internal fun isMultiConstructorMethod() = constructorMethodCount > 1
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
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(
|
||||
name = "miniplayerModernConstructorFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("L"),
|
||||
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(
|
||||
name = "miniplayerModernDragAndDropFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("L"),
|
||||
literals = listOf(45628752L),
|
||||
)
|
||||
|
||||
internal val miniplayerModernEnabledFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernEnabledFingerprint",
|
||||
literals = listOf(45622882L),
|
||||
internal val miniplayerOnCloseHandlerFingerprint = legacyFingerprint(
|
||||
name = "miniplayerOnCloseHandlerFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
literals = listOf(MINIPLAYER_DISABLED_FEATURE_KEY),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernExpandButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -83,6 +79,9 @@ internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerExpand),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernExpandCloseDrawablesFingerprint",
|
||||
returnType = "V",
|
||||
@ -91,6 +90,9 @@ internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint
|
||||
literals = listOf(ytOutlinePictureInPictureWhite),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernForwardButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -99,6 +101,9 @@ internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerForwardButton),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernOverlayViewFingerprint",
|
||||
returnType = "V",
|
||||
@ -107,6 +112,9 @@ internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
|
||||
literals = listOf(scrimOverlay),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernRewindButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernRewindButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -123,11 +131,16 @@ internal val miniplayerModernViewParentFingerprint = legacyFingerprint(
|
||||
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(
|
||||
name = "miniplayerOverrideFingerprint",
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
strings = listOf("appName")
|
||||
)
|
||||
|
||||
@ -144,6 +157,7 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
|
||||
returnType = "L",
|
||||
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
||||
opcodes = listOf(
|
||||
Opcode.RETURN_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.CHECK_CAST,
|
||||
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(
|
||||
name = "youTubePlayerOverlaysLayoutFingerprint",
|
||||
customFingerprint = { _, classDef ->
|
||||
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;"
|
||||
|
@ -1,22 +1,22 @@
|
||||
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.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
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.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
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.extension.Constants.GENERAL_PATH
|
||||
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_23_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.resourceid.modernMiniPlayerClose
|
||||
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.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||
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.builder.MutableMethodImplementation
|
||||
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.ReferenceInstruction
|
||||
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.TypeReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$GENERAL_PATH/MiniplayerPatch;"
|
||||
@ -105,26 +105,22 @@ val miniplayerPatch = bytecodePatch(
|
||||
* Adds an override to specify which modern miniplayer is used.
|
||||
*/
|
||||
fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
|
||||
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
|
||||
val targetReference = (targetInstruction as ReferenceInstruction).reference
|
||||
val register = getInstruction<TwoRegisterInstruction>(iPutIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
iPutIndex + 1, """
|
||||
invoke-static { v${targetInstruction.registerA} }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
|
||||
move-result v${targetInstruction.registerA}
|
||||
# Original instruction
|
||||
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
|
||||
addInstructionsAtControlFlowLabel(
|
||||
iPutIndex,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
removeInstruction(iPutIndex)
|
||||
}
|
||||
|
||||
fun Pair<String, Fingerprint>.hookInflatedView(
|
||||
fun MutableMethod.hookInflatedView(
|
||||
literalValue: Long,
|
||||
hookedClassType: String,
|
||||
extensionMethodName: String,
|
||||
) {
|
||||
methodOrThrow(miniplayerModernViewParentFingerprint).apply {
|
||||
val imageViewIndex = indexOfFirstInstructionOrThrow(
|
||||
indexOfFirstLiteralInstructionOrThrow(literalValue)
|
||||
) {
|
||||
@ -138,7 +134,6 @@ val miniplayerPatch = bytecodePatch(
|
||||
"invoke-static { v$register }, $extensionMethodName"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Modern mini player is only present and functional in 19.15+.
|
||||
// Resource is not present in older versions. Using it to determine, if patching an old version.
|
||||
@ -184,7 +179,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
if (isPatchingOldVersion) {
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_LEGACY"
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_14"
|
||||
addPreference(settingArray, MINIPLAYER)
|
||||
|
||||
// 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 {
|
||||
it.apply {
|
||||
if (MethodUtil.isConstructor(it)) {
|
||||
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||
val iPutIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.IPUT &&
|
||||
getReference<FieldReference>()?.type == "I"
|
||||
this.opcode == Opcode.IPUT &&
|
||||
this.getReference<FieldReference>()?.type == "I"
|
||||
}
|
||||
|
||||
insertModernMiniplayerTypeOverride(iPutIndex)
|
||||
} else if (isMultiConstructorMethod()) {
|
||||
} else {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertModernMiniplayerOverride(
|
||||
index
|
||||
@ -214,27 +209,96 @@ val miniplayerPatch = bytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
if (is_19_23_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop(Z)Z"
|
||||
)
|
||||
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
|
||||
}
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
miniplayerModernEnabledFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45622882L,
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
|
||||
)
|
||||
|
||||
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
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
// 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
|
||||
|
||||
// region Enable double tap action.
|
||||
// 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")
|
||||
}
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
settingArray += "SETTINGS: MINIPLAYER_WIDTH_DIP"
|
||||
} else {
|
||||
settingArray += "SETTINGS: MINIPLAYER_REWIND_FORWARD"
|
||||
}
|
||||
|
||||
if (is_19_36_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45628823L,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction()Z"
|
||||
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(
|
||||
45630429L,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
|
||||
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setHorizontalDrag(Z)Z"
|
||||
)
|
||||
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
|
||||
|
||||
settingArray += "SETTINGS: MINIPLAYER_HORIZONTAL_DRAG"
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_43"
|
||||
} else {
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_16"
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -291,7 +355,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
"adjustMiniplayerOpacity"
|
||||
)
|
||||
).forEach { (fingerprint, literalValue, methodName) ->
|
||||
fingerprint.hookInflatedView(
|
||||
fingerprint.methodOrThrow(miniplayerModernViewParentFingerprint).hookInflatedView(
|
||||
literalValue,
|
||||
"Landroid/widget/ImageView;",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
|
||||
@ -300,23 +364,19 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
miniplayerModernAddViewListenerFingerprint.methodOrThrow(
|
||||
miniplayerModernViewParentFingerprint
|
||||
).apply {
|
||||
addInstructionsWithLabels(
|
||||
).addInstruction(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideMiniplayerSubTexts(Landroid/view/View;)Z
|
||||
move-result v0
|
||||
if-nez v0, :hidden
|
||||
""",
|
||||
ExternalLabel("hidden", getInstruction(implementation!!.instructions.lastIndex))
|
||||
"invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"hideMiniplayerSubTexts(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Modern 2 has a broken overlay subtitle view that is always present.
|
||||
// Modern 2 uses the same overlay controls as the regular video player,
|
||||
// and the overlay views are added at runtime.
|
||||
// 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 {
|
||||
it.method.apply {
|
||||
it.classDef.methods.add(
|
||||
@ -352,18 +412,6 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
// 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"
|
||||
|
||||
// region add settings
|
||||
|
@ -1,17 +1,69 @@
|
||||
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.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch
|
||||
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.indexOfGetDrawableInstruction
|
||||
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_39_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.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
|
||||
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")
|
||||
val spoofAppVersionPatch = resourcePatch(
|
||||
@ -22,6 +74,7 @@ val spoofAppVersionPatch = resourcePatch(
|
||||
|
||||
dependsOn(
|
||||
baseSpoofAppVersionPatch("$GENERAL_CLASS_DESCRIPTOR->getVersionOverride(Ljava/lang/String;)Ljava/lang/String;"),
|
||||
spoofAppVersionBytecodePatch,
|
||||
settingsPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
@ -297,6 +297,7 @@ val toolBarComponentsPatch = bytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
/*
|
||||
// region patch for hide voice search button
|
||||
|
||||
if (is_19_28_or_greater) {
|
||||
@ -311,6 +312,7 @@ val toolBarComponentsPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// endregion
|
||||
*/
|
||||
|
||||
// region patch for hide voice search button
|
||||
|
||||
|
@ -5,6 +5,8 @@ import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
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.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.settingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
@ -12,9 +14,16 @@ import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.copyFile
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.getAdaptiveIconResourceFile
|
||||
import app.revanced.util.getResourceGroup
|
||||
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 val availableIcon = mapOf(
|
||||
@ -39,8 +48,8 @@ private val drawableDirectories = sizeArray.map { "drawable-$it" }
|
||||
private val mipmapDirectories = sizeArray.map { "mipmap-$it" }
|
||||
|
||||
private val launcherIconResourceFileNames = arrayOf(
|
||||
"adaptiveproduct_youtube_background_color_108",
|
||||
"adaptiveproduct_youtube_foreground_color_108",
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME,
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME,
|
||||
"ic_launcher",
|
||||
"ic_launcher_round"
|
||||
).map { "$it.png" }.toTypedArray()
|
||||
@ -83,7 +92,10 @@ val customBrandingIconPatch = resourcePatch(
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(settingsPatch)
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
|
||||
val appIconOption = stringOption(
|
||||
@ -181,5 +193,32 @@ val customBrandingIconPatch = resourcePatch(
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -386,6 +386,8 @@ val playerComponentsPatch = bytecodePatch(
|
||||
findMethodOrThrow(syntheticReference) {
|
||||
name == "onClick"
|
||||
}.hookInitVideoPanel(0)
|
||||
} else {
|
||||
println("WARNING: target Opcode not found in ${fingerprint.first}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.inlineTimeBarPlayedNotHighlightedColor
|
||||
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.seekUndoEduOverlayStub
|
||||
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.sponsorblock.sponsorBlockBytecodePatch
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val engagementPanelBuilderFingerprint = legacyFingerprint(
|
||||
name = "engagementPanelBuilderFingerprint",
|
||||
@ -164,6 +169,23 @@ internal val seekbarOnDrawFingerprint = legacyFingerprint(
|
||||
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(
|
||||
name = "totalTimeFingerprint",
|
||||
returnType = "V",
|
||||
|
@ -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.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.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.
|
||||
)
|
||||
)
|
||||
}
|
@ -134,8 +134,8 @@ private val playerControlsBytecodePatch = bytecodePatch(
|
||||
|
||||
changeVisibilityMethod =
|
||||
findMethodOrThrow(EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR) {
|
||||
name == "changeVisibility"
|
||||
&& parameters == listOf("Z", "Z")
|
||||
name == "changeVisibility" &&
|
||||
parameters == listOf("Z", "Z")
|
||||
}
|
||||
|
||||
changeVisibilityNegatedImmediatelyMethod =
|
||||
|
@ -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.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(
|
||||
name = "browseIdClassFingerprint",
|
||||
returnType = "Ljava/lang/Object;",
|
||||
@ -45,18 +39,26 @@ internal val reelWatchPagerFingerprint = legacyFingerprint(
|
||||
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(
|
||||
name = "searchQueryClassFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L", "Ljava/util/Map;"),
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
),
|
||||
strings = listOf("force_enable_sticky_browsy_bars"),
|
||||
customFingerprint = { method, _ ->
|
||||
indexOfStringIsEmptyInstruction(method) >= 0
|
||||
}
|
||||
)
|
||||
|
||||
internal val videoStateFingerprint = legacyFingerprint(
|
||||
|
@ -135,9 +135,8 @@ val playerTypeHookPatch = bytecodePatch(
|
||||
|
||||
// region patch for hook search bar
|
||||
|
||||
searchQueryClassFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
val searchQueryIndex = it.patternMatch!!.startIndex + 1
|
||||
searchQueryClassFingerprint.methodOrThrow().apply {
|
||||
val searchQueryIndex = indexOfStringIsEmptyInstruction(this) - 1
|
||||
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
|
||||
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
|
||||
|
||||
@ -162,7 +161,6 @@ val playerTypeHookPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
|
@ -23,10 +23,22 @@ var is_19_23_or_greater = false
|
||||
private set
|
||||
var is_19_25_or_greater = false
|
||||
private set
|
||||
var is_19_26_or_greater = false
|
||||
private set
|
||||
var is_19_28_or_greater = false
|
||||
private set
|
||||
var is_19_29_or_greater = false
|
||||
private set
|
||||
var is_19_32_or_greater = false
|
||||
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
|
||||
private set
|
||||
|
||||
@ -53,8 +65,14 @@ val versionCheckPatch = resourcePatch(
|
||||
is_19_15_or_greater = 241602000 <= playStoreServicesVersion
|
||||
is_19_23_or_greater = 242402000 <= playStoreServicesVersion
|
||||
is_19_25_or_greater = 242599000 <= playStoreServicesVersion
|
||||
is_19_26_or_greater = 242705000 <= playStoreServicesVersion
|
||||
is_19_28_or_greater = 242905000 <= playStoreServicesVersion
|
||||
is_19_29_or_greater = 243005000 <= 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
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,8 @@ var menuItemView = -1L
|
||||
private set
|
||||
var metaPanel = -1L
|
||||
private set
|
||||
var miniplayerMaxSize = -1L
|
||||
private set
|
||||
var modernMiniPlayerClose = -1L
|
||||
private set
|
||||
var modernMiniPlayerExpand = -1L
|
||||
@ -444,6 +446,10 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
ID,
|
||||
"metapanel"
|
||||
]
|
||||
miniplayerMaxSize = resourceMappings[
|
||||
DIMEN,
|
||||
"miniplayer_max_size",
|
||||
]
|
||||
modernMiniPlayerClose = resourceMappings[
|
||||
ID,
|
||||
"modern_miniplayer_close"
|
||||
|
@ -1,26 +1,10 @@
|
||||
package app.revanced.patches.youtube.utils.toolbar
|
||||
|
||||
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.or
|
||||
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(
|
||||
name = "toolBarPatchFingerprint",
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
|
@ -6,12 +6,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
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.util.fingerprint.matchOrThrow
|
||||
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
|
||||
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.ReferenceInstruction
|
||||
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 =
|
||||
"$UTILS_PATH/ToolBarPatch;"
|
||||
@ -24,30 +30,35 @@ val toolBarHookPatch = bytecodePatch(
|
||||
dependsOn(sharedResourceIdPatch)
|
||||
|
||||
execute {
|
||||
toolBarButtonFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
val replaceIndex = it.patternMatch!!.startIndex
|
||||
val freeIndex = it.patternMatch!!.endIndex - 1
|
||||
toolBarButtonFingerprint.methodOrThrow().apply {
|
||||
val getDrawableIndex = indexOfGetDrawableInstruction(this)
|
||||
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
getReference<MethodReference>()?.returnType == "I"
|
||||
}
|
||||
val freeIndex = getDrawableIndex - 1
|
||||
|
||||
val replaceReference = getInstruction<ReferenceInstruction>(replaceIndex).reference
|
||||
val replaceReference = getInstruction<ReferenceInstruction>(enumOrdinalIndex).reference
|
||||
val replaceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(replaceIndex).registerC
|
||||
val enumRegister = getInstruction<FiveRegisterInstruction>(replaceIndex).registerD
|
||||
getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerC
|
||||
val enumRegister = getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerD
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
|
||||
|
||||
val imageViewIndex = replaceIndex + 2
|
||||
val imageViewIndex = indexOfFirstInstructionReversedOrThrow(enumOrdinalIndex) {
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Landroid/widget/ImageView;"
|
||||
}
|
||||
val imageViewReference =
|
||||
getInstruction<ReferenceInstruction>(imageViewIndex).reference
|
||||
|
||||
addInstructions(
|
||||
replaceIndex + 1, """
|
||||
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(replaceIndex)
|
||||
}
|
||||
removeInstruction(enumOrdinalIndex)
|
||||
}
|
||||
|
||||
toolbarMethod = toolBarPatchFingerprint.methodOrThrow()
|
||||
|
@ -5,7 +5,6 @@ import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.patcher.util.Document
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
@ -44,26 +43,6 @@ fun Node.cloneNodes(parent: Node) {
|
||||
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.
|
||||
*
|
||||
@ -74,20 +53,29 @@ fun Node.doRecursively(action: (Node) -> Unit) {
|
||||
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 ->
|
||||
ResourceGroup(
|
||||
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) {
|
||||
addEntryValues(
|
||||
"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) {
|
||||
val child = ownerDocument.createElement(tagName)
|
||||
child.block()
|
||||
@ -294,21 +274,6 @@ internal fun inputStreamFromBundledResource(
|
||||
*/
|
||||
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.
|
||||
* @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) =
|
||||
findElementByAttributeValue(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)
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,8 @@
|
||||
<item>MEMBERSHIPS_SHORTS_ONLY</item>
|
||||
<item>MEMBERSHIPS_LIVESTREAMS_ONLY</item>
|
||||
</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_2</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_6</item>
|
||||
</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>PHONE</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_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_2</item>
|
||||
<item>@string/revanced_miniplayer_type_entry_3</item>
|
||||
</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>PHONE</item>
|
||||
<item>MINIMAL</item>
|
||||
<item>TABLET</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_return_youtube_username_display_format_entries">
|
||||
|
@ -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_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_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_4">Modern 1</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_enable_double_tap_action_title">Enable double-tap action</string>
|
||||
<string name="revanced_miniplayer_enable_double_tap_action_summary_on">"Double-tap action is enabled.
|
||||
<string name="revanced_miniplayer_rounded_corners_title">Enable rounded corners</string>
|
||||
<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 once more to change to the original size."</string>
|
||||
<string name="revanced_miniplayer_enable_double_tap_action_summary_off">Double-tap action is disabled.</string>
|
||||
<string name="revanced_miniplayer_enable_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_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>
|
||||
<string name="revanced_miniplayer_hide_expand_close_summary_on">Buttons are hidden.\n(swipe miniplayer to expand or close)</string>
|
||||
<string name="revanced_miniplayer_hide_expand_close_summary_off">Expand and close buttons are shown.</string>
|
||||
• Double tap to increase miniplayer size.
|
||||
• Double tap again to restore original size."</string>
|
||||
<string name="revanced_miniplayer_double_tap_action_summary_off">Double-tap action and pinch to resize is disabled.</string>
|
||||
<string name="revanced_miniplayer_drag_and_drop_title">Enable drag and drop</string>
|
||||
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Drag and drop is enabled.
|
||||
|
||||
Miniplayer can be dragged to any corner of the screen."</string>
|
||||
<string name="revanced_miniplayer_drag_and_drop_summary_off">Drag and drop is disabled.</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_summary_on">Subtexts are hidden.</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_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_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_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>
|
||||
|
@ -160,19 +160,42 @@
|
||||
</PreferenceScreen>SETTINGS: HOOK_BUTTONS -->
|
||||
|
||||
<!-- 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">
|
||||
<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 -->
|
||||
<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 -->
|
||||
|
||||
<!-- 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
|
||||
<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
|
||||
<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
|
||||
<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" />
|
||||
</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_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
|
||||
<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 -->
|
||||
<!-- SETTINGS: MINIPLAYER_TYPE_19_14
|
||||
<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
|
||||
<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 -->
|
||||
|
Reference in New Issue
Block a user