Compare commits

...

12 Commits

Author SHA1 Message Date
semantic-release-bot
300b12f948 chore: Release v5.47.0-dev.7 [skip ci]
# [5.47.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.6...v5.47.0-dev.7) (2025-12-03)

### Features

* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](ac583d40d0))
2025-12-03 15:05:05 +00:00
rospino74
ac583d40d0 feat(Spoof SIM provider): Spoof additional TelephonyManager methods (#6293) 2025-12-03 16:01:08 +01:00
semantic-release-bot
c400188c38 chore: Release v5.47.0-dev.6 [skip ci]
# [5.47.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.5...v5.47.0-dev.6) (2025-11-24)

### Features

* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](0af0ee92c4))
2025-11-24 12:01:20 +00:00
Swakshan
0af0ee92c4 feat(Letterboxd): Add Hide ads patch (#6309)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-11-24 12:56:55 +01:00
semantic-release-bot
fff29544b9 chore: Release v5.47.0-dev.5 [skip ci]
# [5.47.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.4...v5.47.0-dev.5) (2025-11-13)

### Bug Fixes

* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](9495cf49ef))
2025-11-13 07:44:09 +00:00
LisoUseInAIKyrios
9495cf49ef fix(YouTube - Hide player flyout menu items): Allow hiding audio menu with 'Android No SDK' client type 2025-11-13 09:40:28 +02:00
semantic-release-bot
15675b5164 chore: Release v5.47.0-dev.4 [skip ci]
# [5.47.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.3...v5.47.0-dev.4) (2025-11-12)

### Bug Fixes

* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](654d091e65))
2025-11-12 19:01:00 +00:00
LisoUseInAIKyrios
654d091e65 fix(YouTube - Sanitize sharing links): Handle non hierarchical urls 2025-11-12 20:55:32 +02:00
semantic-release-bot
98371be33c chore: Release v5.47.0-dev.3 [skip ci]
# [5.47.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.2...v5.47.0-dev.3) (2025-11-12)

### Features

* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](2f0de15e67))
2025-11-12 07:46:04 +00:00
brosssh
2f0de15e67 feat(Instagram): Add Disable auto story flipping patch (#6262)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-11-12 08:41:15 +01:00
semantic-release-bot
df160370e2 chore: Release v5.47.0-dev.2 [skip ci]
# [5.47.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.1...v5.47.0-dev.2) (2025-11-12)

### Bug Fixes

* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](bb745b555b))
2025-11-12 06:19:34 +00:00
LisoUseInAIKyrios
bb745b555b fix(Instagram - Disable signature check): Change patch to default excluded (#6283) 2025-11-12 08:14:16 +02:00
15 changed files with 336 additions and 124 deletions

View File

@@ -1,3 +1,45 @@
# [5.47.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.6...v5.47.0-dev.7) (2025-12-03)
### Features
* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](https://github.com/ReVanced/revanced-patches/commit/ac583d40d0f4c0e6544e3661ff3e82a25912f2b0))
# [5.47.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.5...v5.47.0-dev.6) (2025-11-24)
### Features
* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](https://github.com/ReVanced/revanced-patches/commit/0af0ee92c48bb2ffc332197e05439e20c5c05d83))
# [5.47.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.4...v5.47.0-dev.5) (2025-11-13)
### Bug Fixes
* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](https://github.com/ReVanced/revanced-patches/commit/9495cf49ef8a872be64de6c971c1919b4b9a8720))
# [5.47.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.3...v5.47.0-dev.4) (2025-11-12)
### Bug Fixes
* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](https://github.com/ReVanced/revanced-patches/commit/654d091e650cda37650b57cbf3ba6f1cdd6d47d3))
# [5.47.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.2...v5.47.0-dev.3) (2025-11-12)
### Features
* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](https://github.com/ReVanced/revanced-patches/commit/2f0de15e67e4f99ed6ecdc136d04cceb23b0d069))
# [5.47.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.1...v5.47.0-dev.2) (2025-11-12)
### Bug Fixes
* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](https://github.com/ReVanced/revanced-patches/commit/bb745b555b3808b7679c5995319aa365630fbd76))
# [5.47.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.46.0...v5.47.0-dev.1) (2025-11-12)

View File

@@ -35,6 +35,15 @@ public class LinkSanitizer {
public Uri sanitizeUri(Uri uri) {
try {
String scheme = uri.getScheme();
if (scheme == null || !(scheme.equals("http") || scheme.equals("https"))) {
// Opening YouTube share sheet 'other' option passes the video title as a URI.
// Checking !uri.isHierarchical() works for all cases, except if the
// video title starts with / and then it's hierarchical but still an invalid URI.
Logger.printDebug(() -> "Ignoring uri: " + uri);
return uri;
}
Uri.Builder builder = uri.buildUpon().clearQuery();
if (!parametersToRemove.isEmpty()) {

View File

@@ -72,7 +72,7 @@ public class SpoofVideoStreamsPatch {
public static boolean spoofingToClientWithNoMultiAudioStreams() {
return isPatchIncluded()
&& SPOOF_STREAMING_DATA
&& preferredClient != ClientType.IPADOS;
&& !preferredClient.supportsMultiAudioTracks;
}
/**

View File

@@ -21,13 +21,14 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public final class LayoutComponentsFilter extends Filter {
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
private static final StringTrieSearch mixPlaylistsContextExceptions = new StringTrieSearch(
"V.ED", // Playlist browse id.
"java.lang.ref.WeakReference"
);
private static final ByteArrayFilterGroup mixPlaylistsExceptions2 = new ByteArrayFilterGroup(
private static final ByteArrayFilterGroup mixPlaylistsBufferExceptions = new ByteArrayFilterGroup(
null,
"cell_description_body"
"cell_description_body",
"channel_profile"
);
private static final ByteArrayFilterGroup mixPlaylists = new ByteArrayFilterGroup(
null,
@@ -380,17 +381,15 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// Prevent playlist items being hidden, if a mix playlist is present in it.
if (mixPlaylistsExceptions.matches(conversionContext.toString())) {
return false;
}
// Prevent hiding the description of some videos accidentally.
if (mixPlaylistsExceptions2.check(bytes).isFiltered()) {
return false;
}
if (mixPlaylists.check(bytes).isFiltered()) {
if (mixPlaylists.check(bytes).isFiltered()
// Prevent hiding the description of some videos accidentally.
&& !mixPlaylistsBufferExceptions.check(bytes).isFiltered()
// Prevent playlist items being hidden, if a mix playlist is present in it.
// Check last since it requires creating a context string.
//
// FIXME: The conversion context passed in does not always generate a valid toString.
// This string check may no longer be needed, or the patch may be broken.
&& !mixPlaylistsContextExceptions.matches(conversionContext.toString())) {
Logger.printDebug(() -> "Filtered mix playlist");
return true;
}

View File

@@ -12,13 +12,9 @@ import java.util.List;
public class PlayerFlyoutMenuItemsFilter extends Filter {
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
private static final boolean AVAILABLE_ON_LAUNCH = !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return AVAILABLE_ON_LAUNCH && !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
return !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
}
@Override

View File

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

View File

@@ -56,6 +56,10 @@ public final class app/revanced/patches/all/misc/connectivity/telephony/sim/spoo
public static final fun getSpoofSimCountryPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatchKt {
public static final fun getSpoofSimProviderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatchKt {
public static final fun getSpoofWifiPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -320,10 +324,18 @@ public final class app/revanced/patches/instagram/misc/signature/SignatureCheckP
public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatchKt {
public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/letterboxd/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -1,105 +1,9 @@
package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
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.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.*
@Deprecated("Patch was renamed", ReplaceWith("spoofSimProviderPatch"))
@Suppress("unused")
val spoofSimCountryPatch = bytecodePatch(
name = "Spoof SIM country",
description = "Spoofs country information returned by the SIM card provider.",
use = false,
) {
val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry }
fun isoCountryPatchOption(
key: String,
title: String,
) = stringOption(
key,
null,
countries,
title,
"ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.",
false,
validator = { it: String? -> it == null || it.uppercase() in countries.values },
)
val networkCountryIso by isoCountryPatchOption(
"networkCountryIso",
"Network ISO country code",
)
val simCountryIso by isoCountryPatchOption(
"simCountryIso",
"SIM ISO country code",
)
dependsOn(
transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
val match = MethodCall.entries.firstOrNull { search ->
MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@transformInstructionsPatch null
val iso = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso
MethodCall.SimCountryIso -> simCountryIso
}?.lowercase()
iso?.let { instructionIndex to it }
},
transform = { mutableMethod, entry: Pair<Int, String> ->
transformMethodCall(entry, mutableMethod)
},
),
)
}
private fun transformMethodCall(
entry: Pair<Int, String>,
mutableMethod: MutableMethod,
) {
val (instructionIndex, methodCallValue) = entry
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
mutableMethod.replaceInstruction(
instructionIndex + 1,
"const-string v$register, \"$methodCallValue\"",
)
}
private enum class MethodCall(
val reference: MethodReference,
) {
NetworkCountryIso(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getNetworkCountryIso",
emptyList(),
"Ljava/lang/String;",
),
),
SimCountryIso(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimCountryIso",
emptyList(),
"Ljava/lang/String;",
),
),
}
val spoofSimCountryPatch = bytecodePatch {
dependsOn(spoofSimProviderPatch)
}

View File

@@ -0,0 +1,169 @@
package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
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.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.Locale
@Suppress("unused")
val spoofSimProviderPatch = bytecodePatch(
name = "Spoof SIM provider",
description = "Spoofs information about the SIM card provider.",
use = false,
) {
val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry }
fun isoCountryPatchOption(
key: String,
title: String,
) = stringOption(
key,
null,
countries,
title,
"ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.",
false,
validator = { it: String? -> it == null || it.uppercase() in countries.values },
)
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
val networkCountryIso by isoCountryPatchOption(
"networkCountryIso",
"Network ISO country code",
)
val networkOperator by intOption(
key = "networkOperator",
title = "MCC+MNC network operator code",
description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the network operator.",
validator = { isMccMncValid(it) }
)
val networkOperatorName by stringOption(
key = "networkOperatorName",
title = "Network operator name",
description = "The full name of the network operator.",
)
val simCountryIso by isoCountryPatchOption(
"simCountryIso",
"SIM ISO country code",
)
val simOperator by intOption(
key = "simOperator",
title = "MCC+MNC SIM operator code",
description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the SIM operator.",
validator = { isMccMncValid(it) }
)
val simOperatorName by stringOption(
key = "simOperatorName",
title = "SIM operator name",
description = "The full name of the SIM operator.",
)
dependsOn(
transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
val match = MethodCall.entries.firstOrNull { search ->
MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@transformInstructionsPatch null
val replacement = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
MethodCall.NetworkOperator -> networkOperator?.toString()
MethodCall.NetworkOperatorName -> networkOperatorName
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
MethodCall.SimOperator -> simOperator?.toString()
MethodCall.SimOperatorName -> simOperatorName
}
replacement?.let { instructionIndex to it }
},
transform = ::transformMethodCall,
),
)
}
private fun transformMethodCall(
mutableMethod: MutableMethod,
entry: Pair<Int, String>,
) {
val (instructionIndex, methodCallValue) = entry
// Get the register which would have contained the return value
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
// Replace the move-result instruction with our fake value
mutableMethod.replaceInstruction(
instructionIndex + 1,
"const-string v$register, \"$methodCallValue\"",
)
}
private enum class MethodCall(
val reference: MethodReference,
) {
NetworkCountryIso(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getNetworkCountryIso",
emptyList(),
"Ljava/lang/String;",
),
),
NetworkOperator(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getNetworkOperator",
emptyList(),
"Ljava/lang/String;",
),
),
NetworkOperatorName(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getNetworkOperatorName",
emptyList(),
"Ljava/lang/String;",
),
),
SimCountryIso(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimCountryIso",
emptyList(),
"Ljava/lang/String;",
),
),
SimOperator(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimOperator",
emptyList(),
"Ljava/lang/String;",
),
),
SimOperatorName(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimOperatorName",
emptyList(),
"Ljava/lang/String;",
),
),
}

View File

@@ -6,7 +6,9 @@ import app.revanced.util.returnEarly
@Suppress("unused")
val signatureCheckPatch = bytecodePatch(
name = "Disable signature check",
description = "Disables the signature check that causes the app to crash on startup."
description = "Disables the signature check that can cause the app to crash on startup. " +
"Including this patch may cause issues with sharing or opening external Instagram links.",
use = false
) {
compatibleWith("com.instagram.android")

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.instagram.story.flipping
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val disableStoryAutoFlippingPatch = bytecodePatch(
name = "Disable story auto flipping",
description = "Disable stories automatically flipping/skipping after some seconds.",
use = false
) {
compatibleWith("com.instagram.android")
execute {
onStoryTimeoutActionFingerprint.method.returnEarly()
}
}

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.instagram.story.flipping
import app.revanced.patcher.fingerprint
internal val onStoryTimeoutActionFingerprint = fingerprint {
parameters("Ljava/lang/Object;")
returns("V")
strings("userSession")
custom { _, classDef ->
classDef.type == "Linstagram/features/stories/fragment/ReelViewerFragment;"
}
}

View File

@@ -0,0 +1,29 @@
package app.revanced.patches.letterboxd.ads
import app.revanced.patcher.fingerprint
internal const val admobHelperClassName = "Lcom/letterboxd/letterboxd/helpers/AdmobHelper;"
internal val admobHelperSetShowAdsFingerprint = fingerprint {
custom { method, classDef ->
method.name == "setShowAds" && classDef.type == admobHelperClassName
}
}
internal val admobHelperShouldShowAdsFingerprint = fingerprint {
custom { method, classDef ->
method.name == "shouldShowAds" && classDef.type == admobHelperClassName
}
}
internal val filmFragmentShowAdsFingerprint = fingerprint {
custom { method, classDef ->
method.name == "showAds" && classDef.type.endsWith("/FilmFragment;")
}
}
internal val memberExtensionShowAdsFingerprint = fingerprint {
custom { method, classDef ->
method.name == "showAds" && classDef.type.endsWith("/AMemberExtensionKt;")
}
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patches.letterboxd.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads",
) {
compatibleWith("com.letterboxd.letterboxd")
execute {
admobHelperSetShowAdsFingerprint.method.addInstruction(0, "const p1, 0x0")
listOf(admobHelperShouldShowAdsFingerprint, filmFragmentShowAdsFingerprint, memberExtensionShowAdsFingerprint).forEach {
it.method.returnEarly(false)
}
}
}

View File

@@ -807,10 +807,11 @@ If changing this setting does not take effect, try switching to Incognito mode."
<string name="revanced_hide_player_flyout_audio_track_title">Hide Audio track</string>
<string name="revanced_hide_player_flyout_audio_track_summary_on">Audio track menu is hidden</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">Audio track menu is shown</string>
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'.
'Android No SDK' must be kept untranslated. -->
<string name="revanced_hide_player_flyout_audio_track_not_available">"Audio track menu is hidden
To show the Audio track menu, change \'Spoof video streams\' to iPadOS"</string>
To show the Audio track menu, change \'Spoof video streams\' to \'Android No SDK\'"</string>
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_watch_in_vr_title">Hide Watch in VR</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Watch in VR menu is hidden</string>