mirror of
https://github.com/revanced/revanced-patches
synced 2024-11-19 03:57:27 +01:00
feat(YouTube - Miniplayer): Add horizontal drag gesture (#3859)
This commit is contained in:
parent
e377b1e6ad
commit
e32b19e170
@ -10,12 +10,44 @@ import app.revanced.extension.shared.settings.BaseSettings;
|
||||
public final class EnableDebuggingPatch {
|
||||
|
||||
private static final ConcurrentMap<Long, Boolean> featureFlags
|
||||
= new ConcurrentHashMap<>(150, 0.75f, 1);
|
||||
= new ConcurrentHashMap<>(300, 0.75f, 1);
|
||||
|
||||
public static boolean isFeatureFlagEnabled(long flag, boolean value) {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
|
||||
if (value && BaseSettings.DEBUG.get()) {
|
||||
if (featureFlags.putIfAbsent(flag, true) == null) {
|
||||
Logger.printDebug(() -> "feature is enabled: " + flag);
|
||||
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static double isDoubleFeatureFlagEnabled(double value, long flag, double defaultValue) {
|
||||
if (defaultValue != value && BaseSettings.DEBUG.get()) {
|
||||
if (featureFlags.putIfAbsent(flag, true) == null) {
|
||||
// Align the log outputs to make post processing easier.
|
||||
Logger.printDebug(() -> " double feature is enabled: " + flag
|
||||
+ " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static long isLongFeatureFlagEnabled(long value, long flag, long defaultValue) {
|
||||
if (defaultValue != value && BaseSettings.DEBUG.get()) {
|
||||
if (featureFlags.putIfAbsent(flag, true) == null) {
|
||||
Logger.printDebug(() -> " long feature is enabled: " + flag
|
||||
+ " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,9 @@ public final class MiniplayerPatch {
|
||||
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.
|
||||
@ -131,6 +134,13 @@ public final class MiniplayerPatch {
|
||||
|
||||
private static final int OPACITY_LEVEL;
|
||||
|
||||
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Settings.MINIPLAYER_TYPE.get().isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MiniplayerHideExpandCloseAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
@ -248,21 +258,15 @@ public final class MiniplayerPatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static float setMovementBoundFactor(float original) {
|
||||
// Not clear if customizing this is useful or not.
|
||||
// So for now just log this and use the original value.
|
||||
if (original != 1.0) Logger.printDebug(() -> "setMovementBoundFactor original: " + original);
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean setDropShadow(boolean original) {
|
||||
public static boolean setHorizontalDrag(boolean original) {
|
||||
if (CURRENT_TYPE.isModern()) {
|
||||
return MINIPLAYER_HORIZONTAL_DRAG_ENABLED;
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import static java.lang.Boolean.TRUE;
|
||||
import static app.revanced.extension.shared.settings.Setting.*;
|
||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.*;
|
||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||
@ -138,6 +139,7 @@ public class Settings extends BaseSettings {
|
||||
private static final 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 MiniplayerHorizontalDragAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new 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));
|
||||
|
@ -1133,17 +1133,6 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
|
||||
public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/miniplayer/FingerprintsKt {
|
||||
public static final field ANIMATION_INTERPOLATION_FEATURE_KEY J
|
||||
public static final field DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL J
|
||||
public static final field DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL J
|
||||
public static final field DROP_SHADOW_FEATURE_KEY J
|
||||
public static final field INITIAL_SIZE_FEATURE_KEY_LITERAL J
|
||||
public static final field MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL J
|
||||
public static final field MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY J
|
||||
public static final field ROUNDED_CORNERS_FEATURE_KEY J
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt {
|
||||
public static final fun getFloatyBarButtonTopMargin ()J
|
||||
public static final fun getMiniplayerMaxSize ()J
|
||||
@ -1188,10 +1177,6 @@ public final class app/revanced/patches/youtube/layout/searchbar/WideSearchbarPa
|
||||
public static final fun getWideSearchbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/seekbar/FingerprintsKt {
|
||||
public static final field PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG J
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatchKt {
|
||||
public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -1274,10 +1259,6 @@ public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPa
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/fix/cairo/FingerprintsKt {
|
||||
public static final field CAIRO_CONFIG_LITERAL_VALUE J
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatchKt {
|
||||
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
@file:Suppress("SpellCheckingInspection")
|
||||
|
||||
package app.revanced.patches.youtube.layout.miniplayer
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
@ -33,16 +35,14 @@ internal val miniplayerModernCloseButtonFingerprint = fingerprint {
|
||||
literal { modernMiniplayerClose }
|
||||
}
|
||||
|
||||
const val MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL = 45622882L
|
||||
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
const val MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY = 45630429L
|
||||
const val DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL = 45628823L
|
||||
const val DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL = 45628752L
|
||||
const val INITIAL_SIZE_FEATURE_KEY_LITERAL = 45640023L
|
||||
const val ANIMATION_INTERPOLATION_FEATURE_KEY = 45647018L
|
||||
const val DROP_SHADOW_FEATURE_KEY = 45652223L
|
||||
const val ROUNDED_CORNERS_FEATURE_KEY = 45652224L
|
||||
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 val miniplayerModernConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
|
@ -1,3 +1,5 @@
|
||||
@file:Suppress("SpellCheckingInspection")
|
||||
|
||||
package app.revanced.patches.youtube.layout.miniplayer
|
||||
|
||||
import app.revanced.patcher.Match
|
||||
@ -204,6 +206,10 @@ val miniplayerPatch = bytecodePatch(
|
||||
preferences += SwitchPreference("revanced_miniplayer_drag_and_drop")
|
||||
}
|
||||
|
||||
if (is_19_43_or_greater) {
|
||||
preferences += SwitchPreference("revanced_miniplayer_horizontal_drag")
|
||||
}
|
||||
|
||||
if (is_19_36_or_greater) {
|
||||
preferences += SwitchPreference("revanced_miniplayer_rounded_corners")
|
||||
}
|
||||
@ -291,7 +297,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
@ -302,13 +308,13 @@ val miniplayerPatch = bytecodePatch(
|
||||
* Adds an override to specify which modern miniplayer is used.
|
||||
*/
|
||||
fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
|
||||
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
|
||||
val register = getInstruction<TwoRegisterInstruction>(iPutIndex).registerA
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
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
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
@ -378,24 +384,31 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
if (is_19_23_or_greater) {
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL,
|
||||
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
||||
"enableMiniplayerDragAndDrop",
|
||||
)
|
||||
}
|
||||
|
||||
if (is_19_43_or_greater) {
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
|
||||
"setHorizontalDrag",
|
||||
)
|
||||
}
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY,
|
||||
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
|
||||
"getModernMiniplayerOverride",
|
||||
)
|
||||
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL,
|
||||
MINIPLAYER_MODERN_FEATURE_KEY,
|
||||
"getModernFeatureFlagsActiveOverride",
|
||||
)
|
||||
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL,
|
||||
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
|
||||
"enableMiniplayerDoubleTapAction",
|
||||
)
|
||||
}
|
||||
@ -403,7 +416,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
if (is_19_26_or_greater) {
|
||||
miniplayerModernConstructorMatch.mutableMethod.apply {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||
INITIAL_SIZE_FEATURE_KEY_LITERAL,
|
||||
MINIPLAYER_INITIAL_SIZE_FEATURE_KEY,
|
||||
)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
|
||||
|
||||
@ -418,7 +431,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
// Override a mininimum miniplayer size constant.
|
||||
// Override a minimum size constant.
|
||||
miniplayerMinimumSizeMatch.mutableMethod.apply {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192
|
||||
@ -432,22 +445,9 @@ val miniplayerPatch = bytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
if (is_19_32_or_greater) {
|
||||
// Feature is not exposed in the settings, and currently only for debugging.
|
||||
miniplayerModernConstructorMatch.insertLiteralValueFloatOverride(
|
||||
ANIMATION_INTERPOLATION_FEATURE_KEY,
|
||||
"setMovementBoundFactor",
|
||||
)
|
||||
}
|
||||
|
||||
if (is_19_36_or_greater) {
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
DROP_SHADOW_FEATURE_KEY,
|
||||
"setDropShadow",
|
||||
)
|
||||
|
||||
miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride(
|
||||
ROUNDED_CORNERS_FEATURE_KEY,
|
||||
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
|
||||
"setRoundedCorners",
|
||||
)
|
||||
}
|
||||
@ -551,9 +551,9 @@ val miniplayerPatch = bytecodePatch(
|
||||
invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V
|
||||
return-void
|
||||
""",
|
||||
"""
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
@ -34,7 +34,7 @@ internal val shortsSeekbarColorFingerprint = fingerprint {
|
||||
literal { reelTimeBarPlayedColorId }
|
||||
}
|
||||
|
||||
const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
|
||||
internal const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
|
||||
|
||||
internal val playerSeekbarGradientConfigFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
|
@ -57,22 +57,62 @@ val enableDebuggingPatch = bytecodePatch(
|
||||
),
|
||||
)
|
||||
|
||||
// Hook the method that looks up if a feature flag is active or not.
|
||||
experimentalFeatureFlagFingerprint.applyMatch(
|
||||
// Hook the methods that look up if a feature flag is active.
|
||||
|
||||
experimentalBooleanFeatureFlagFingerprint.applyMatch(
|
||||
context,
|
||||
experimentalFeatureFlagParentMatch
|
||||
).mutableMethod.apply {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT)
|
||||
|
||||
// It appears that all usage of this method has a default of 'false',
|
||||
// so there's no need to pass in the default.
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
move-result v0
|
||||
invoke-static { p1, p2, v0 }, $EXTENSION_CLASS_DESCRIPTOR->isFeatureFlagEnabled(JZ)Z
|
||||
invoke-static { v0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZJ)Z
|
||||
move-result v0
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
experimentalDoubleFeatureFlagFingerprint.applyMatch(
|
||||
context,
|
||||
experimentalFeatureFlagParentMatch
|
||||
).mutableMethod.apply {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
|
||||
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
|
||||
move-result-wide v0
|
||||
return-wide v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
experimentalLongFeatureFlagFingerprint.applyMatch(
|
||||
context,
|
||||
experimentalFeatureFlagParentMatch
|
||||
).mutableMethod.apply {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
move-result-wide v0
|
||||
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
|
||||
move-result-wide v0
|
||||
return-wide v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// There exists other experimental accessor methods for String, byte[], and wrappers for obfuscated classes,
|
||||
// but currently none of those are hooked.
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,21 @@ internal val experimentalFeatureFlagParentFingerprint = fingerprint {
|
||||
strings("Unable to parse proto typed experiment flag: ")
|
||||
}
|
||||
|
||||
internal val experimentalFeatureFlagFingerprint = fingerprint {
|
||||
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
parameters("J", "Z")
|
||||
}
|
||||
|
||||
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("D")
|
||||
parameters("J", "D")
|
||||
}
|
||||
|
||||
internal val experimentalLongFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("J")
|
||||
parameters("J", "J")
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
* When this value is true, Cairo Fragment is used.
|
||||
* In this case, some of the patches may be broken, so set this value to FALSE.
|
||||
*/
|
||||
const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
|
||||
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
|
||||
|
||||
internal val cairoFragmentConfigFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
|
@ -1035,6 +1035,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<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\n\nMiniplayer 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\n\nMiniplayer 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>
|
||||
|
Loading…
Reference in New Issue
Block a user