Compare commits

..

8 Commits

Author SHA1 Message Date
semantic-release-bot
14ca4d3288 chore: Release v5.10.1-dev.3 [skip ci]
## [5.10.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.2...v5.10.1-dev.3) (2025-02-03)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([a06c031](a06c0318bf))
2025-02-03 10:15:53 +00:00
ILoveOpenSourceApplications
a06c0318bf fix(YouTube - Hide layout components): Hide new type of community post (#4404) 2025-02-03 12:13:15 +02:00
semantic-release-bot
7f9f668435 chore: Release v5.10.1-dev.2 [skip ci]
## [5.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.1...v5.10.1-dev.2) (2025-02-03)

### Bug Fixes

* **YouTube - Enable slide to seek:** Change patch to default include ([76fd33c](76fd33ca54))
2025-02-03 10:11:28 +00:00
LisoUseInAIKyrios
76fd33ca54 fix(YouTube - Enable slide to seek): Change patch to default include 2025-02-03 12:08:28 +02:00
semantic-release-bot
9a653e9c5a chore: Release v5.10.1-dev.1 [skip ci]
## [5.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.10.1-dev.1) (2025-02-02)

### Bug Fixes

* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([f81b658](f81b658fb7))
2025-02-02 09:14:21 +00:00
LisoUseInAIKyrios
f81b658fb7 fix(YouTube - Theme): Use custom seekbar color for cairo startup animation (#4399) 2025-02-02 11:10:57 +02:00
LisoUseInAIKyrios
7ff39d89d6 refactor(YouTube - Spoof app version): Use more concise description of 19.26.42 2025-01-31 12:51:49 +02:00
LisoUseInAIKyrios
78ab0ec2bd refactor(YouTube - Swipe controls): Use more consistent settings language of 'opacity' and 0-100 scale 2025-01-31 12:40:45 +02:00
12 changed files with 305 additions and 24 deletions

View File

@@ -1,3 +1,24 @@
## [5.10.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.2...v5.10.1-dev.3) (2025-02-03)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
## [5.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.1...v5.10.1-dev.2) (2025-02-03)
### Bug Fixes
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
## [5.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.10.1-dev.1) (2025-02-02)
### Bug Fixes
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
# [5.10.0](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.10.0) (2025-01-31)

View File

@@ -81,7 +81,8 @@ public final class LayoutComponentsFilter extends Filter {
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml",
"poll_post_root.eml",
"videos_post_root.eml"
"videos_post_root.eml",
"post_shelf_slim.eml"
);
final var communityGuidelines = new StringFilterGroup(

View File

@@ -6,11 +6,19 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.AnimatedVectorDrawable;
import com.airbnb.lottie.LottieAnimationView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Locale;
import java.util.Scanner;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -93,17 +101,6 @@ public final class SeekbarColorPatch {
return customSeekbarColor;
}
/**
* Injection point
*/
public static boolean useLotteLaunchSplashScreen(boolean original) {
Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original);
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
}
private static int colorChannelTo3Bits(int channel8Bits) {
final float channel3Bits = channel8Bits * 7 / 255f;
@@ -127,6 +124,17 @@ public final class SeekbarColorPatch {
/**
* Injection point
*/
public static boolean useLotteLaunchSplashScreen(boolean original) {
// This method is only used for development purposes to force the old style launch screen.
// Forcing this off on some devices can cause unexplained startup crashes,
// where the lottie animation is still used even though this condition appears to bypass it.
return original; // false = drawable style, true = lottie style.
}
/**
* Injection point.
* Old drawable style launch screen.
*/
public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) {
// Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable
// without using any styles, but a color filter cannot selectively change the seekbar
@@ -134,6 +142,8 @@ public final class SeekbarColorPatch {
// Even if the seekbar color xml value is changed to a completely different color (such as green),
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
try {
// Must set the color even if custom seekbar is off,
// because the xml color was replaced with a themed value.
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
@@ -154,6 +164,77 @@ public final class SeekbarColorPatch {
}
}
/**
* Injection point.
* Modern Lottie style animation.
*/
public static void setSplashAnimationLottie(LottieAnimationView view, int resourceId) {
try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
view.patch_setAnimation(resourceId);
return;
}
//noinspection ConstantConditions
if (false) { // Set true to force slow animation for development.
final int longAnimation = Utils.getResourceIdentifier(
Utils.isDarkModeEnabled(Utils.getContext())
? "startup_animation_5s_30fps_dark"
: "startup_animation_5s_30fps_light",
"raw");
if (longAnimation != 0) {
resourceId = longAnimation;
}
}
// Must specify primary key name otherwise the morphing YT logo color is also changed.
String originalKey = "\"k\":";
String originalPrimary = originalKey + "[1,0,0.2,1]";
String originalAccent = originalKey + "[1,0.152941176471,0.56862745098,1]";
String replacementPrimary = originalKey + getColorStringArray(customSeekbarColor);
String replacementAccent = originalKey + getColorStringArray(customSeekbarColorGradient[1]);
String json = loadRawResourceAsString(resourceId);
if (json == null) {
return; // Should never happen.
}
if (BaseSettings.DEBUG.get() && (!json.contains(originalPrimary) || !json.contains(originalAccent))) {
String jsonFinal = json;
Logger.printException(() -> "Could not replace launch animation colors: " + jsonFinal);
}
Logger.printDebug(() -> "Replacing Lottie animation JSON");
json = json.replace(originalPrimary, replacementPrimary);
json = json.replace(originalAccent, replacementAccent);
// cacheKey is not needed since the animation will not be reused.
view.patch_setAnimation(new ByteArrayInputStream(json.getBytes()), null);
} catch (Exception ex) {
Logger.printException(() -> "setSplashAnimationLottie failure", ex);
}
}
private static String getColorStringArray(int color) {
return Arrays.toString(new double[]{
Color.red(color) / 255.0,
Color.green(color) / 255.0,
Color.blue(color) / 255.0,
Color.alpha(color) / 255.0
});
}
private static String loadRawResourceAsString(int resourceId) {
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
return scanner.next();
} catch (IOException e) {
Logger.printException(() -> "Could not load resource: " + resourceId);
return null;
}
}
/**
* Injection point.
*/

View File

@@ -314,8 +314,9 @@ public class Settings extends BaseSettings {
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127, true,
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 50, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
// Debugging
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
@@ -436,6 +437,11 @@ public class Settings extends BaseSettings {
DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
}
if (!DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.isSetToDefault()) {
SWIPE_OVERLAY_OPACITY.save(DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.get() / 255);
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
}
// endregion
// region SB import/export callbacks

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.swipecontrols
import android.content.Context
import android.graphics.Color
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
@@ -86,7 +88,18 @@ class SwipeControlsConfigurationProvider(
* get the background color for text on the overlay, as a color int
*/
val overlayTextBackgroundColor: Int
get() = Color.argb(Settings.SWIPE_OVERLAY_BACKGROUND_ALPHA.get(), 0, 0, 0)
get() {
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
}
opacity = opacity * 255 / 100
return Color.argb(opacity, 0, 0, 0)
}
/**
* get the foreground color for text on the overlay, as a color int

View File

@@ -0,0 +1,15 @@
package com.airbnb.lottie;
import java.io.InputStream;
@SuppressWarnings("unused")
public class LottieAnimationView {
public void patch_setAnimation(InputStream stream, String cacheKey) {
throw new RuntimeException("stub");
}
public final void patch_setAnimation(int rawResInt) {
throw new RuntimeException("stub");
}
}

View File

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

View File

@@ -24,9 +24,7 @@ internal const val EXTENSION_METHOD_DESCRIPTOR =
val enableSlideToSeekPatch = bytecodePatch(
name = "Enable slide to seek",
description = "Adds an option to enable slide to seek " +
"instead of playing at 2x speed when pressing and holding in the video player. " +
"Including this patch may cause issues with tapping or double tapping the video player overlay.",
use = false,
"instead of playing at 2x speed when pressing and holding in the video player."
) {
dependsOn(
sharedExtensionPatch,

View File

@@ -44,7 +44,7 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_background_alpha", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
)

View File

@@ -2,9 +2,12 @@ package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val fullscreenSeekbarThumbnailsFingerprint = fingerprint {
returns("Z")
@@ -109,3 +112,52 @@ internal val launchScreenLayoutTypeFingerprint = fingerprint {
&& method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag)
}
}
internal const val LOTTIE_ANIMATION_VIEW_CLASS_TYPE = "Lcom/airbnb/lottie/LottieAnimationView;"
internal val lottieAnimationViewSetAnimationIntFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("I")
returns("V")
custom { methodDef, classDef ->
classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;"
&& reference.name == "isInEditMode"
} >= 0
}
}
internal val lottieAnimationViewSetAnimationStreamFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
returns("V")
custom { methodDef, classDef ->
classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/util/Set;"
&& reference.name == "add"
} >= 0 && methodDef.containsLiteralInstruction(0)
}
}
internal val lottieCompositionFactoryZipFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("Landroid/content/Context;", "Ljava/lang/String;", "Ljava/lang/String;")
returns("L")
strings(".zip", ".lottie")
}
/**
* Resolves using class found in [lottieCompositionFactoryZipFingerprint].
*
* [Original method](https://github.com/airbnb/lottie-android/blob/26ad8bab274eac3f93dccccfa0cafc39f7408d13/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java#L386)
*/
internal val lottieCompositionFactoryFromJsonInputStreamFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("Ljava/io/InputStream;", "Ljava/lang/String;")
returns("L")
literal { 2 }
}

View File

@@ -3,10 +3,12 @@ package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
@@ -22,15 +24,21 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.copyXmlNode
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.inputStreamFromBundledResource
import app.revanced.util.insertFeatureFlagBooleanOverride
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.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import org.w3c.dom.Element
import java.io.ByteArrayInputStream
import kotlin.use
@@ -307,7 +315,11 @@ val seekbarColorPatch = bytecodePatch(
// region apply seekbar custom color to splash screen animation.
// Don't use the lotte splash screen layout if using custom seekbar.
if (!is_19_34_or_greater) {
return@execute // 19.25 does not have a cairo launch animation.
}
// Add development hook to force old drawable splash animation.
arrayOf(
launchScreenLayoutTypeFingerprint,
mainActivityOnCreateFingerprint
@@ -318,7 +330,7 @@ val seekbarColorPatch = bytecodePatch(
)
}
// Hook the splash animation drawable to set the a seekbar color theme.
// Hook the splash animation to set the a seekbar color.
mainActivityOnCreateFingerprint.method.apply {
val drawableIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
@@ -333,6 +345,87 @@ val seekbarColorPatch = bytecodePatch(
"invoke-static { v$drawableRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V"
)
// Replace the Lottie animation view setAnimation(int) call.
val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;"
&& reference.name == setAnimationIntMethodName
}.forEach { index ->
val instruction = getInstruction<FiveRegisterInstruction>(index)
replaceInstruction(
index,
"invoke-static { v${instruction.registerC}, v${instruction.registerD} }, " +
"$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie" +
"(Lcom/airbnb/lottie/LottieAnimationView;I)V"
)
}
}
// Add non obfuscated method aliases for `setAnimation(int)`
// and `setAnimation(InputStream, String)` so extension code can call them.
lottieAnimationViewSetAnimationIntFingerprint.classDef.methods.apply {
val addedMethodName = "patch_setAnimation"
val setAnimationIntName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name
add(ImmutableMethod(
LOTTIE_ANIMATION_VIEW_CLASS_TYPE,
addedMethodName,
listOf(ImmutableMethodParameter("I", null, null)),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
"""
invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntName(I)V
return-void
"""
)
})
val factoryStreamClass : CharSequence
val factoryStreamName : CharSequence
val factoryStreamReturnType : CharSequence
lottieCompositionFactoryFromJsonInputStreamFingerprint.match(
lottieCompositionFactoryZipFingerprint.originalClassDef
).originalMethod.apply {
factoryStreamClass = definingClass
factoryStreamName = name
factoryStreamReturnType = returnType
}
val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint
.originalMethod.name
add(ImmutableMethod(
LOTTIE_ANIMATION_VIEW_CLASS_TYPE,
addedMethodName,
listOf(
ImmutableMethodParameter("Ljava/io/InputStream;", null, null),
ImmutableMethodParameter("Ljava/lang/String;", null, null)
),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(4),
).toMutable().apply {
addInstructions(
"""
invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType
move-result-object v0
invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamName($factoryStreamReturnType)V
return-void
"""
)
})
}
// endregion

View File

@@ -511,8 +511,9 @@ This feature is only available for older devices"</string>
<string name="revanced_swipe_overlay_timeout_summary">The amount of milliseconds the overlay is visible</string>
<string name="revanced_swipe_text_overlay_size_title">Swipe overlay text size</string>
<string name="revanced_swipe_text_overlay_size_summary">The text size for swipe overlay</string>
<string name="revanced_swipe_overlay_background_alpha_title">Swipe background visibility</string>
<string name="revanced_swipe_overlay_background_alpha_summary">The visibility of swipe overlay background</string>
<string name="revanced_swipe_overlay_background_opacity_title">Swipe overlay background opacity</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Opacity value between 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Swipe opacity must be between 0-100</string>
<string name="revanced_swipe_threshold_title">Swipe magnitude threshold</string>
<string name="revanced_swipe_threshold_summary">The amount of threshold for swipe to occur</string>
<string name="revanced_swipe_change_video_title">Enable swipe to change videos</string>
@@ -1125,7 +1126,7 @@ If later turned off, it is recommended to clear the app data to prevent UI bugs.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Spoof app version target</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - Restore old Shorts player icons</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - Restore old navigation and toolbar icons</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - Restore old navigation icons</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - Restore RYD on Shorts incognito mode</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - Restore wide video speed &amp; quality menu</string>