Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97b4fc5b5a | ||
|
|
7427432759 |
+11
@@ -236,6 +236,17 @@ public final class AdsFilter extends Filter {
|
|||||||
return Settings.HIDE_GENERAL_ADS.get();
|
return Settings.HIDE_GENERAL_ADS.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* 21.34+ the player overlay timely shelf handler no longer gets a dedicated method,
|
||||||
|
* so instead the result of the overlay type `String.equals` comparison is passed in.
|
||||||
|
*
|
||||||
|
* @param original Whether the overlay is the timely shelf (already being shown when not hiding ads).
|
||||||
|
*/
|
||||||
|
public static boolean allowAds(boolean original) {
|
||||||
|
return Settings.HIDE_GENERAL_ADS.get() ? false : original;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,14 +31,11 @@ internal val BytecodePatchContext.buildInitPlaybackRequestMethodMatch by composi
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val BytecodePatchContext.buildPlayerRequestURIMethodMatch by composingFirstMethod("key", "asig") {
|
internal val BytecodePatchContext.buildPlayerRequestURIMethodMatch by composingFirstMethod("key", "asig") {
|
||||||
returnType("Ljava/lang/String;")
|
returnType($$"Landroid/net/Uri$Builder;")
|
||||||
opcodes(
|
opcodes(
|
||||||
Opcode.INVOKE_VIRTUAL, // Register holds player request URI.
|
Opcode.INVOKE_VIRTUAL, // The last Uri$Builder mutating call. Register holds the player request Uri$Builder.
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.IPUT_OBJECT,
|
Opcode.RETURN_OBJECT, // Returns the built player request Uri$Builder.
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.MONITOR_EXIT,
|
|
||||||
Opcode.RETURN_OBJECT,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +151,7 @@ internal val BytecodePatchContext.nerdsStatsVideoFormatBuilderMethod by gettingF
|
|||||||
returnType("Ljava/lang/String;")
|
returnType("Ljava/lang/String;")
|
||||||
parameterTypes("L")
|
parameterTypes("L")
|
||||||
instructions(
|
instructions(
|
||||||
"codecs=\""(),
|
", codecs="(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -82,14 +82,19 @@ internal fun spoofVideoStreamsPatch(
|
|||||||
// region Block /get_watch requests to fall back to /player requests.
|
// region Block /get_watch requests to fall back to /player requests.
|
||||||
|
|
||||||
buildPlayerRequestURIMethodMatch.method.apply {
|
buildPlayerRequestURIMethodMatch.method.apply {
|
||||||
val invokeToStringIndex = buildPlayerRequestURIMethodMatch[0]
|
val returnIndex = buildPlayerRequestURIMethodMatch[-1]
|
||||||
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
val builderRegister = getInstruction<OneRegisterInstruction>(returnIndex).registerA
|
||||||
|
val freeRegister = findFreeRegister(returnIndex, builderRegister)
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
invokeToStringIndex,
|
returnIndex,
|
||||||
"""
|
$$"""
|
||||||
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
invoke-virtual { v$builderRegister }, Landroid/net/Uri$Builder;->build()Landroid/net/Uri;
|
||||||
move-result-object v$uriRegister
|
move-result-object v$freeRegister
|
||||||
|
invoke-static { v$freeRegister }, $$EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
||||||
|
move-result-object v$freeRegister
|
||||||
|
invoke-virtual { v$freeRegister }, Landroid/net/Uri;->buildUpon()Landroid/net/Uri$Builder;
|
||||||
|
move-result-object v$builderRegister
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,3 +71,27 @@ internal val BytecodePatchContext.playerOverlayTimelyShelfMethod by gettingFirst
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 21.34+ removed the [String]"innertube_cue_range", "Null id" and "Null onExitActions" strings
|
||||||
|
* from the player overlay timely shelf handler, and the handler moved into a synthetic lambda
|
||||||
|
* (`a(Ljava/lang/Object;)V`) that dispatches on the overlay type via a `String.equals` comparison.
|
||||||
|
* Only [String]"player_overlay_timely_shelf" remains, so match on it plus the following
|
||||||
|
* `String.equals`/`move-result` used to check if the overlay is the timely shelf.
|
||||||
|
*/
|
||||||
|
internal val BytecodePatchContext.playerOverlayTimelyShelfMethod21_34 by composingFirstMethod {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returnType("V")
|
||||||
|
parameterTypes("Ljava/lang/Object;")
|
||||||
|
instructions(
|
||||||
|
"player_overlay_timely_shelf"(),
|
||||||
|
afterAtMost(
|
||||||
|
5,
|
||||||
|
allOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL(),
|
||||||
|
method { name == "equals" && definingClass == "Ljava/lang/String;" },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
after(Opcode.MOVE_RESULT()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import app.revanced.patches.youtube.misc.engagement.engagementPanelHookPatch
|
|||||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
|
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
@@ -34,6 +35,7 @@ import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
|||||||
import app.revanced.util.injectHideViewCall
|
import app.revanced.util.injectHideViewCall
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
@@ -94,7 +96,8 @@ val hideAdsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -154,6 +157,29 @@ val hideAdsPatch = bytecodePatch(
|
|||||||
// Hide player overlay view. This can be hidden with a regular litho filter
|
// Hide player overlay view. This can be hidden with a regular litho filter
|
||||||
// but an empty space remains.
|
// but an empty space remains.
|
||||||
if (is_20_14_or_greater) {
|
if (is_20_14_or_greater) {
|
||||||
|
if (is_21_34_or_greater) {
|
||||||
|
// 21.34+ the overlay handler moved into a synthetic lambda `a(Object)V`
|
||||||
|
// that dispatches on overlay type. Hook right after the `String.equals` /
|
||||||
|
// `move-result` that determines the overlay is the timely shelf so that
|
||||||
|
// only the timely shelf is hidden and not every player overlay.
|
||||||
|
playerOverlayTimelyShelfMethod21_34.let { match ->
|
||||||
|
val insertIndex = match[-1]
|
||||||
|
val equalsResultRegister =
|
||||||
|
match.method.getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
|
match.method.addInstructionsWithLabels(
|
||||||
|
insertIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-static { v$equalsResultRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->allowAds(Z)Z
|
||||||
|
move-result v$equalsResultRegister
|
||||||
|
if-nez v$equalsResultRegister, :show
|
||||||
|
return-void
|
||||||
|
:show
|
||||||
|
nop
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
playerOverlayTimelyShelfMethod.addInstructionsWithLabels(
|
playerOverlayTimelyShelfMethod.addInstructionsWithLabels(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
@@ -166,6 +192,7 @@ val hideAdsPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Hide end screen store banner.
|
// Hide end screen store banner.
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ val videoAdsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ val disableAutoCaptionsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ val customBrandingPatch = baseCustomBrandingPatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-1
@@ -39,7 +39,8 @@ val hideAutoplayPreviewPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -13,6 +13,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
@@ -68,7 +69,8 @@ val hideEndScreenCardsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,7 +92,11 @@ val hideEndScreenCardsPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_19_43_or_greater) {
|
// 21.34+ the end screen cards are no longer shown through a dedicated 5-method
|
||||||
|
// container class that the legacy showEndscreenCardsMethod fingerprint can locate.
|
||||||
|
// Instead each card view (circle/icon/video) is created and hidden individually by the
|
||||||
|
// three layout fingerprints hooked above, which cover hiding the whole card on 21.34.
|
||||||
|
if (is_19_43_or_greater && !is_21_34_or_greater) {
|
||||||
showEndscreenCardsMethod.addInstructionsWithLabels(
|
showEndscreenCardsMethod.addInstructionsWithLabels(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ val disablePlayerPopupPanelsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ val hideRelatedVideoOverlayPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ val disableRollingNumberAnimationsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -32,7 +32,8 @@ val disableSignInToTVPopupPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -30,7 +30,8 @@ val hideTimestampPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+41
-3
@@ -92,8 +92,15 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
val preferences = mutableSetOf<BasePreference>()
|
val preferences = mutableSetOf<BasePreference>()
|
||||||
|
|
||||||
preferences +=
|
preferences +=
|
||||||
if (is_20_37_or_greater) {
|
if (is_20_37_or_greater && !is_21_34_or_greater) {
|
||||||
ListPreference("revanced_miniplayer_type")
|
ListPreference("revanced_miniplayer_type")
|
||||||
|
} else if (is_21_34_or_greater) {
|
||||||
|
// YouTube 21.29+ removed all modern miniplayers except "Modern 4",
|
||||||
|
// so the type picker no longer has any effect.
|
||||||
|
NonInteractivePreference(
|
||||||
|
key = "revanced_miniplayer_type",
|
||||||
|
summaryKey = "revanced_miniplayer_type_not_available_summary",
|
||||||
|
)
|
||||||
} else if (is_20_03_or_greater) {
|
} else if (is_20_03_or_greater) {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_miniplayer_type",
|
key = "revanced_miniplayer_type",
|
||||||
@@ -153,7 +160,9 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
|
preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
|
||||||
|
}
|
||||||
|
|
||||||
PreferenceScreen.PLAYER.addPreferences(
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
PreferenceScreenPreference(
|
PreferenceScreenPreference(
|
||||||
@@ -265,6 +274,10 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
|
|
||||||
// region Enable modern miniplayer.
|
// region Enable modern miniplayer.
|
||||||
|
|
||||||
|
// Select which modern miniplayer type is used.
|
||||||
|
// YouTube 21.29+ removed all modern miniplayers except "Modern 4", so there is
|
||||||
|
// nothing to choose between and this override is dead code.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
miniplayerModernConstructorMethod.classDef.methods.forEach {
|
miniplayerModernConstructorMethod.classDef.methods.forEach {
|
||||||
it.apply {
|
it.apply {
|
||||||
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||||
@@ -282,7 +295,14 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern miniplayer feature-flag overrides.
|
||||||
|
// YouTube 21.29+ removed all modern miniplayers except "Modern 4" and moved/removed the
|
||||||
|
// feature flags for drag & drop, double-tap, rounded corners, horizontal drag and animated
|
||||||
|
// expand. The default/minimum size flags were removed in 21.32+. The fingerprints below
|
||||||
|
// therefore no longer resolve on 21.34+, so these overrides only apply to older versions.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
if (is_19_23_or_greater) {
|
if (is_19_23_or_greater) {
|
||||||
miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride(
|
miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
||||||
@@ -361,6 +381,7 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
"getMaximizeAnimation",
|
"getMaximizeAnimation",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@@ -391,7 +412,8 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
|
|
||||||
// region fix minimal miniplayer using the wrong pause/play bold icons.
|
// region fix minimal miniplayer using the wrong pause/play bold icons.
|
||||||
|
|
||||||
if (is_20_31_or_greater) {
|
// 21.34 does not have the yt_fill_pause_* drawables this fingerprint anchors on.
|
||||||
|
if (is_20_31_or_greater && !is_21_34_or_greater) {
|
||||||
miniplayerSetIconsMethod.apply {
|
miniplayerSetIconsMethod.apply {
|
||||||
findInstructionIndicesReversedOrThrow {
|
findInstructionIndicesReversedOrThrow {
|
||||||
val reference = getReference<MethodReference>()
|
val reference = getReference<MethodReference>()
|
||||||
@@ -421,7 +443,6 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
ClassDef::miniplayerModernActionButtonMethodMatch to "hideMiniplayerActionButton",
|
ClassDef::miniplayerModernActionButtonMethodMatch to "hideMiniplayerActionButton",
|
||||||
ClassDef::miniplayerModernRewindButtonMethodMatch to "hideMiniplayerRewindForward",
|
ClassDef::miniplayerModernRewindButtonMethodMatch to "hideMiniplayerRewindForward",
|
||||||
ClassDef::miniplayerModernForwardButtonMethodMatch to "hideMiniplayerRewindForward",
|
ClassDef::miniplayerModernForwardButtonMethodMatch to "hideMiniplayerRewindForward",
|
||||||
ClassDef::miniplayerModernOverlayViewMethodMatch to "adjustMiniplayerOpacity",
|
|
||||||
).forEach { (matching, methodName) ->
|
).forEach { (matching, methodName) ->
|
||||||
val match = matching.get(miniplayerModernViewParentMethod.immutableClassDef)
|
val match = matching.get(miniplayerModernViewParentMethod.immutableClassDef)
|
||||||
|
|
||||||
@@ -436,6 +457,23 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the overlay opacity. The modern overlay was restructured in 21.29+ alongside the
|
||||||
|
// removal of the default/minimum size flags, so this hook is only applied on older versions.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
|
val match = ClassDef::miniplayerModernOverlayViewMethodMatch
|
||||||
|
.get(miniplayerModernViewParentMethod.immutableClassDef)
|
||||||
|
|
||||||
|
match.method.apply {
|
||||||
|
val index = match[-1]
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
index + 1,
|
||||||
|
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->adjustMiniplayerOpacity(Landroid/view/View;)V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod()
|
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod()
|
||||||
.addInstruction(
|
.addInstruction(
|
||||||
0,
|
0,
|
||||||
|
|||||||
+7
@@ -15,6 +15,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
@@ -93,6 +94,11 @@ val shortsAutoplayPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YouTube 21.29+ replaced the diagnostics string and reworked the reel playback repeat
|
||||||
|
// method (its signature changed from "V(L)" to "bL(JLbetu;J)V"), so the parent fingerprint
|
||||||
|
// below no longer resolves on 21.34+. The autoplay restore logic thus only applies to
|
||||||
|
// versions below 21.34.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
val reelPlaybackRepeatMethod =
|
val reelPlaybackRepeatMethod =
|
||||||
reelPlaybackRepeatParentMethod.immutableClassDef.getReelPlaybackRepeatMethod()
|
reelPlaybackRepeatParentMethod.immutableClassDef.getReelPlaybackRepeatMethod()
|
||||||
|
|
||||||
@@ -193,4 +199,5 @@ val shortsAutoplayPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@ val changeStartPagePatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -29,7 +29,8 @@ val announcementsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_29_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_29_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
@@ -103,7 +104,10 @@ val removeBackgroundPlaybackRestrictionsPatch = bytecodePatch(
|
|||||||
kidsBackgroundPlaybackPolicyControllerMethod.returnEarly()
|
kidsBackgroundPlaybackPolicyControllerMethod.returnEarly()
|
||||||
|
|
||||||
// Fix PiP buttons not working after locking/unlocking device screen.
|
// Fix PiP buttons not working after locking/unlocking device screen.
|
||||||
if (is_19_34_or_greater) {
|
// YouTube 21.34+ removed the flag this anchors on (the remaining case is an
|
||||||
|
// unfixable Google-side issue, see RVX #2764). Skipping it avoids the
|
||||||
|
// fingerprint being unresolved and crashing the whole patch.
|
||||||
|
if (is_19_34_or_greater && !is_21_34_or_greater) {
|
||||||
pipInputConsumerFeatureFlagMethodMatch.let {
|
pipInputConsumerFeatureFlagMethodMatch.let {
|
||||||
it.method.insertLiteralOverride(
|
it.method.insertLiteralOverride(
|
||||||
it[0],
|
it[0],
|
||||||
|
|||||||
+14
@@ -15,6 +15,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
|||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.returnType
|
import app.revanced.patcher.returnType
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||||
import app.revanced.util.cloneMutableAndPreserveParameters
|
import app.revanced.util.cloneMutableAndPreserveParameters
|
||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
@@ -73,6 +74,10 @@ val hookClientContextPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Authentication changed listener method (where the message lite builder method was
|
||||||
|
// sourced from) was removed in YouTube 21.34+. Derive the builder method from the dummy
|
||||||
|
// client context body method instead, which still resolves and builds the same body.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
authenticationChangeListenerMethod.apply {
|
authenticationChangeListenerMethod.apply {
|
||||||
val messageLiteBuilderIndex =
|
val messageLiteBuilderIndex =
|
||||||
indexOfMessageLiteBuilderReference(this, messageLiteBuilderField.definingClass)
|
indexOfMessageLiteBuilderReference(this, messageLiteBuilderField.definingClass)
|
||||||
@@ -80,6 +85,15 @@ val hookClientContextPatch = bytecodePatch(
|
|||||||
messageLiteBuilderMethod =
|
messageLiteBuilderMethod =
|
||||||
getInstruction<ReferenceInstruction>(messageLiteBuilderIndex).methodReference!!
|
getInstruction<ReferenceInstruction>(messageLiteBuilderIndex).methodReference!!
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
buildDummyClientContextBodyMethodMatch.method.apply {
|
||||||
|
val messageLiteBuilderIndex =
|
||||||
|
indexOfMessageLiteBuilderReference(this, messageLiteBuilderField.definingClass)
|
||||||
|
|
||||||
|
messageLiteBuilderMethod =
|
||||||
|
getInstruction<ReferenceInstruction>(messageLiteBuilderIndex).methodReference!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildClientContextBodyConstructorMethod.immutableClassDef.buildClientContextBodyMethodMatch.let {
|
buildClientContextBodyConstructorMethod.immutableClassDef.buildClientContextBodyMethodMatch.let {
|
||||||
it.method.apply {
|
it.method.apply {
|
||||||
|
|||||||
+2
-1
@@ -24,7 +24,8 @@ val enableDebuggingPatch = enableDebuggingPatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ internal val BytecodePatchContext.specificNetworkErrorViewControllerMethodMatch
|
|||||||
returnType("V")
|
returnType("V")
|
||||||
parameterTypes()
|
parameterTypes()
|
||||||
instructions(
|
instructions(
|
||||||
ResourceType.DRAWABLE("ic_offline_no_content_upside_down"),
|
|
||||||
ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"),
|
ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"),
|
||||||
method { name == "getString" && returnType == "Ljava/lang/String;" },
|
method { name == "getString" && returnType == "Ljava/lang/String;" },
|
||||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||||
@@ -20,12 +19,13 @@ internal val BytecodePatchContext.specificNetworkErrorViewControllerMethodMatch
|
|||||||
|
|
||||||
// It's not clear if this second class is ever used, and it may be dead code,
|
// It's not clear if this second class is ever used, and it may be dead code,
|
||||||
// but it is the layout image/text is identical to the network error match above.
|
// but it is the layout image/text is identical to the network error match above.
|
||||||
|
// The offline image drawable (ic_offline_no_content_upside_down) was removed in 21.34,
|
||||||
|
// so only the still-present offline_no_content string is matched.
|
||||||
internal val BytecodePatchContext.loadingFrameLayoutControllerMethodMatch by composingFirstMethod {
|
internal val BytecodePatchContext.loadingFrameLayoutControllerMethodMatch by composingFirstMethod {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returnType("V")
|
returnType("V")
|
||||||
parameterTypes("L")
|
parameterTypes("Z")
|
||||||
instructions(
|
instructions(
|
||||||
ResourceType.DRAWABLE("ic_offline_no_content_upside_down"),
|
|
||||||
ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"),
|
ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"),
|
||||||
method { name == "getString" && returnType == "Ljava/lang/String;" },
|
method { name == "getString" && returnType == "Ljava/lang/String;" },
|
||||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ val bypassURLRedirectsPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -47,7 +47,8 @@ val openLinksExternallyPatch = bytecodePatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+5
@@ -59,6 +59,10 @@ val lithoFilterPatch = lithoFilterPatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf).
|
// Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf).
|
||||||
|
// The flag (45419603) was removed in 21.34. In 21.34 the native UPB buffer path is the
|
||||||
|
// default (and required) path for the setProtoBuffer([B) hook, so nothing needs to be
|
||||||
|
// forced off.
|
||||||
|
if (!is_21_34_or_greater) {
|
||||||
lithoConverterBufferUpbFeatureFlagMethodMatch.let {
|
lithoConverterBufferUpbFeatureFlagMethodMatch.let {
|
||||||
// 20.22 the flag is still enabled in one location, but what it does is not known.
|
// 20.22 the flag is still enabled in one location, but what it does is not known.
|
||||||
// Disable it anyway.
|
// Disable it anyway.
|
||||||
@@ -67,6 +71,7 @@ val lithoFilterPatch = lithoFilterPatch(
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|||||||
+21
@@ -34,6 +34,27 @@ internal val ClassDef.motionEventMethodMatch by ClassDefComposing.composingFirst
|
|||||||
instructions(method("setTranslationY"))
|
instructions(method("setTranslationY"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The player controls overlay was rebuilt in 21.34 and no longer calls `setTranslationY`.
|
||||||
|
// Instead the tap-to-dismiss handler is `nwo.b(Landroid/view/MotionEvent;)V`, which triggers the
|
||||||
|
// visibility change via `nwo.U()V`. These two fingerprints resolve that new hook and keep the
|
||||||
|
// `visibilityNegatedImmediate` injection working for the newer layout.
|
||||||
|
internal val BytecodePatchContext.youtubeControlsOverlayMethod21_34 by gettingFirstImmutableMethodDeclaratively {
|
||||||
|
returnType("V")
|
||||||
|
parameterTypes("Landroid/view/MotionEvent;")
|
||||||
|
instructions(
|
||||||
|
field { definingClass == "Lnwo;" && name == "br" },
|
||||||
|
method { definingClass == "Lnwo;" && name == "U" },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val ClassDef.motionEventMethodMatch21_34 by ClassDefComposing.composingFirstMethod {
|
||||||
|
returnType("V")
|
||||||
|
parameterTypes("Landroid/view/MotionEvent;")
|
||||||
|
instructions(
|
||||||
|
method { definingClass == "Lnwo;" && name == "U" },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
internal val BytecodePatchContext.playerControlsExtensionHookListenersExistMethod by gettingFirstMethodDeclaratively {
|
internal val BytecodePatchContext.playerControlsExtensionHookListenersExistMethod by gettingFirstMethodDeclaratively {
|
||||||
name("fullscreenButtonVisibilityCallbacksExist")
|
name("fullscreenButtonVisibilityCallbacksExist")
|
||||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||||
|
|||||||
+7
-1
@@ -268,7 +268,13 @@ val playerControlsPatch = bytecodePatch(
|
|||||||
visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistMethod
|
visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistMethod
|
||||||
visibilityImmediateMethod = playerControlsExtensionHookMethod
|
visibilityImmediateMethod = playerControlsExtensionHookMethod
|
||||||
|
|
||||||
youtubeControlsOverlayMethod.immutableClassDef.motionEventMethodMatch.let {
|
val motionEventMethodMatch = if (is_21_34_or_greater) {
|
||||||
|
youtubeControlsOverlayMethod21_34.immutableClassDef.motionEventMethodMatch21_34
|
||||||
|
} else {
|
||||||
|
youtubeControlsOverlayMethod.immutableClassDef.motionEventMethodMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
motionEventMethodMatch.let {
|
||||||
visibilityNegatedImmediateMethod = it.method
|
visibilityNegatedImmediateMethod = it.method
|
||||||
visibilityNegatedImmediateInsertIndex = it[0] + 1
|
visibilityNegatedImmediateInsertIndex = it[0] + 1
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -127,6 +127,8 @@ var is_21_07_or_greater : Boolean by Delegates.notNull()
|
|||||||
private set
|
private set
|
||||||
var is_21_08_or_greater : Boolean by Delegates.notNull()
|
var is_21_08_or_greater : Boolean by Delegates.notNull()
|
||||||
private set
|
private set
|
||||||
|
var is_21_34_or_greater: Boolean by Delegates.notNull()
|
||||||
|
private set
|
||||||
|
|
||||||
val versionCheckPatch = resourcePatch(
|
val versionCheckPatch = resourcePatch(
|
||||||
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
|
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
|
||||||
@@ -184,5 +186,6 @@ val versionCheckPatch = resourcePatch(
|
|||||||
is_21_06_or_greater = 260705000 <= playStoreServicesVersion
|
is_21_06_or_greater = 260705000 <= playStoreServicesVersion
|
||||||
is_21_07_or_greater = 260805000 <= playStoreServicesVersion
|
is_21_07_or_greater = 260805000 <= playStoreServicesVersion
|
||||||
is_21_08_or_greater = 260905000 <= playStoreServicesVersion
|
is_21_08_or_greater = 260905000 <= playStoreServicesVersion
|
||||||
|
is_21_34_or_greater = 263410000 <= playStoreServicesVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -20,7 +20,8 @@ val sanitizeSharingLinksPatch = sanitizeSharingLinksPatch(
|
|||||||
"20.26.46",
|
"20.26.46",
|
||||||
"20.31.42",
|
"20.31.42",
|
||||||
"20.37.48",
|
"20.37.48",
|
||||||
"20.40.45"
|
"20.40.45",
|
||||||
|
"21.34.248"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,6 +21,27 @@ internal val BytecodePatchContext.setThemeMethod by gettingFirstMethodDeclarativ
|
|||||||
instructions(ResourceType.STRING("app_theme_appearance_dark"))
|
instructions(ResourceType.STRING("app_theme_appearance_dark"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YouTube 21.34+ appearance resolver `Latyq;->t()Lkll;`.
|
||||||
|
*
|
||||||
|
* Returns the theme appearance enum (whose ordinal 1 is dark), resolving between the
|
||||||
|
* `app_theme_appearance_*` prefs, the dark-mode flag and the system UI mode. Among all
|
||||||
|
* `public final ()Lkll` methods it is uniquely identified by reading `Lklm;->f` (the stored
|
||||||
|
* appearance value string): the sibling helpers `u()`/`v()` read `Lklm;->e`/the system UI mode
|
||||||
|
* instead, while `qyn` reads `Lklm;->f` but returns `V`.
|
||||||
|
*/
|
||||||
|
internal val BytecodePatchContext.setThemeMethod21_34 by gettingFirstMethodDeclaratively {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returnType("Lkll;")
|
||||||
|
parameterTypes()
|
||||||
|
instructions(
|
||||||
|
allOf(
|
||||||
|
Opcode.IGET_OBJECT(),
|
||||||
|
field { name == "f" && type == "Ljava/lang/String;" && definingClass == "Lklm;" },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
internal val BytecodePatchContext.cairoFragmentConfigMethodMatch by composingFirstMethod {
|
internal val BytecodePatchContext.cairoFragmentConfigMethodMatch by composingFirstMethod {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returnType("Z")
|
returnType("Z")
|
||||||
@@ -29,3 +50,22 @@ internal val BytecodePatchContext.cairoFragmentConfigMethodMatch by composingFir
|
|||||||
afterAtMost(10, Opcode.MOVE_RESULT()),
|
afterAtMost(10, Opcode.MOVE_RESULT()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YouTube 21.34+ Cairo settings gate `Loqy;->run()V`.
|
||||||
|
*
|
||||||
|
* The legacy `public final ()Z` feature-flag gate no longer exists. In 21.34 the Cairo settings
|
||||||
|
* screen is selected by feature flag `0x2b6c3c4` inside `Loqy;->run()V`, which chooses between the
|
||||||
|
* legacy (`xml/settings_fragment`, 0x7f180027) and Cairo (`xml/settings_fragment_cairo`,
|
||||||
|
* 0x7f180028) settings screens. The `0x7f180028` literal only appears in this method, which keeps
|
||||||
|
* it unambiguous among the other `0x2b6c3c4` references.
|
||||||
|
*/
|
||||||
|
internal val BytecodePatchContext.cairoFragmentConfigMethodMatch21_34 by composingFirstMethod {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returnType("V")
|
||||||
|
instructions(
|
||||||
|
45532100L(),
|
||||||
|
afterAtMost(10, Opcode.MOVE_RESULT()),
|
||||||
|
afterAtMost(10, 2132279336L()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
+64
-3
@@ -23,6 +23,7 @@ import app.revanced.patches.youtube.misc.fix.contentprovider.fixContentProviderP
|
|||||||
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
|
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_31_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_31_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.util.*
|
import app.revanced.util.*
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
@@ -214,6 +215,64 @@ val settingsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
PreferenceScreen.MISC.addPreferences(
|
||||||
|
PreferenceCategory(
|
||||||
|
titleKey = "revanced_custom_title",
|
||||||
|
sorting = Sorting.UNSORTED,
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
|
||||||
|
preferences = setOf(
|
||||||
|
NonInteractivePreference(
|
||||||
|
key = "revanced_custom_reset_title",
|
||||||
|
titleKey = "revanced_custom_reset_title",
|
||||||
|
summaryKey = "revanced_custom_reset_summary",
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.CustomSettingsResetPreference",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_force_amoled",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_pure_black_player",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_disable_ambient_mode",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_hide_shorts",
|
||||||
|
),
|
||||||
|
ListPreference(
|
||||||
|
key = "revanced_custom_default_playback_speed",
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_remember_playback_speed",
|
||||||
|
),
|
||||||
|
ListPreference(
|
||||||
|
key = "revanced_custom_default_video_quality",
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_remember_video_quality",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_force_highest_quality",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_debug_video_info",
|
||||||
|
),
|
||||||
|
TextPreference(
|
||||||
|
key = "revanced_custom_app_name",
|
||||||
|
titleKey = "revanced_custom_app_name_title",
|
||||||
|
inputType = InputType.TEXT_CAP_CHARACTERS,
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.TextInputPreference",
|
||||||
|
),
|
||||||
|
ListPreference(
|
||||||
|
key = "revanced_custom_startup_tab",
|
||||||
|
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference",
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_custom_clean_urls",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
TextPreference(
|
TextPreference(
|
||||||
key = null,
|
key = null,
|
||||||
titleKey = "revanced_pref_import_export_title",
|
titleKey = "revanced_pref_import_export_title",
|
||||||
@@ -230,7 +289,7 @@ val settingsPatch = bytecodePatch(
|
|||||||
// Update shared dark mode status based on YT theme.
|
// Update shared dark mode status based on YT theme.
|
||||||
// This is needed because YT allows forcing light/dark mode
|
// This is needed because YT allows forcing light/dark mode
|
||||||
// which then differs from the system dark mode status.
|
// which then differs from the system dark mode status.
|
||||||
setThemeMethod.apply {
|
(if (is_21_34_or_greater) setThemeMethod21_34 else setThemeMethod).apply {
|
||||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
|
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
@@ -241,10 +300,12 @@ val settingsPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add setting to force Cairo settings fragment on/off.
|
// Add setting to force Cairo settings fragment on/off.
|
||||||
cairoFragmentConfigMethodMatch.method.insertLiteralOverride(
|
(if (is_21_34_or_greater) cairoFragmentConfigMethodMatch21_34 else cairoFragmentConfigMethodMatch).let {
|
||||||
cairoFragmentConfigMethodMatch[0],
|
it.method.insertLiteralOverride(
|
||||||
|
it[0],
|
||||||
"$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useCairoSettingsFragment(Z)Z",
|
"$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useCairoSettingsFragment(Z)Z",
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Bold icon resources are found starting in 20.23, but many YT icons are not bold.
|
// Bold icon resources are found starting in 20.23, but many YT icons are not bold.
|
||||||
// 20.31 is the first version that seems to have all the bold icons.
|
// 20.31 is the first version that seems to have all the bold icons.
|
||||||
|
|||||||
+26
@@ -183,6 +183,32 @@ internal val BytecodePatchContext.playbackSpeedClassMethod by gettingFirstMethod
|
|||||||
opcodes(Opcode.RETURN_OBJECT)
|
opcodes(Opcode.RETURN_OBJECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YouTube 21.34+ playback-speed bottom sheet fragment factory `Lpzy;->aW(Lce;Ljava/lang/String;)Lpzy;`.
|
||||||
|
*
|
||||||
|
* In 21.34 the `PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT` string moved into the large state-holder
|
||||||
|
* factory `Lhxz;->p()Ljava/lang/Object;` (which returns `Ljava/lang/Object;`, not the fragment type),
|
||||||
|
* so the legacy `playbackSpeedClassMethod` fingerprint no longer resolves. The fragment is instead
|
||||||
|
* obtained from its static factory, lookup by tag with fallback creation, which returns the exact
|
||||||
|
* fragment instance (`Lpzy;`). It is uniquely identified by the exact return type `Lpzy;` together
|
||||||
|
* with the `getSupportFragmentManager` -> `f(Ljava/lang/String;)L` lookup sequence.
|
||||||
|
*/
|
||||||
|
internal val BytecodePatchContext.playbackSpeedClassMethod21_34 by gettingFirstMethodDeclaratively {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||||
|
returnType("Lpzy;")
|
||||||
|
parameterTypes("L", "Ljava/lang/String;")
|
||||||
|
instructions(
|
||||||
|
method("getSupportFragmentManager"),
|
||||||
|
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||||
|
after(method {
|
||||||
|
returnType.startsWith("L") && parameterTypes.size == 1 && parameterTypes.first() == "Ljava/lang/String;"
|
||||||
|
}),
|
||||||
|
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||||
|
after(Opcode.IF_EQZ()),
|
||||||
|
after(Opcode.CHECK_CAST()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* YouTube 20.19 and lower.
|
* YouTube 20.19 and lower.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_49_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_49_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_21_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.shared.videoQualityChangedMethodMatch
|
import app.revanced.patches.youtube.shared.videoQualityChangedMethodMatch
|
||||||
import app.revanced.patches.youtube.video.playerresponse.Hook
|
import app.revanced.patches.youtube.video.playerresponse.Hook
|
||||||
@@ -283,7 +284,7 @@ val videoInformationPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackSpeedClassMethod.apply {
|
(if (is_21_34_or_greater) playbackSpeedClassMethod21_34 else playbackSpeedClassMethod).apply {
|
||||||
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
|
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
val playbackSpeedClass = this.returnType
|
val playbackSpeedClass = this.returnType
|
||||||
|
|||||||
@@ -1516,6 +1516,7 @@ Limitation: Using the back button on the toolbar may not work"</string>
|
|||||||
<string name="revanced_miniplayer_type_entry_5">Modern 2</string>
|
<string name="revanced_miniplayer_type_entry_5">Modern 2</string>
|
||||||
<string name="revanced_miniplayer_type_entry_6">Modern 3</string>
|
<string name="revanced_miniplayer_type_entry_6">Modern 3</string>
|
||||||
<string name="revanced_miniplayer_type_entry_7">Modern 4</string>
|
<string name="revanced_miniplayer_type_entry_7">Modern 4</string>
|
||||||
|
<string name="revanced_miniplayer_type_not_available_summary">Selecting a miniplayer type is no longer available on your YouTube version.</string>
|
||||||
<string name="revanced_miniplayer_disable_rounded_corners_title">Disable rounded corners</string>
|
<string name="revanced_miniplayer_disable_rounded_corners_title">Disable rounded corners</string>
|
||||||
<string name="revanced_miniplayer_disable_rounded_corners_summary_on">Corners are square</string>
|
<string name="revanced_miniplayer_disable_rounded_corners_summary_on">Corners are square</string>
|
||||||
<string name="revanced_miniplayer_disable_rounded_corners_summary_off">Corners are rounded</string>
|
<string name="revanced_miniplayer_disable_rounded_corners_summary_off">Corners are rounded</string>
|
||||||
@@ -1944,6 +1945,36 @@ Video playback with AV1 may stutter or drop frames."</string>
|
|||||||
<string name="revanced_twitch_debug_title">Debug logging</string>
|
<string name="revanced_twitch_debug_title">Debug logging</string>
|
||||||
<string name="revanced_twitch_debug_summary_on">Debug logs are enabled</string>
|
<string name="revanced_twitch_debug_summary_on">Debug logs are enabled</string>
|
||||||
<string name="revanced_twitch_debug_summary_off">Debug logs are disabled</string>
|
<string name="revanced_twitch_debug_summary_off">Debug logs are disabled</string>
|
||||||
|
<!-- Custom settings -->
|
||||||
|
<string name="revanced_custom_title">Custom</string>
|
||||||
|
<string name="revanced_custom_reset_title">Reset Custom Settings</string>
|
||||||
|
<string name="revanced_custom_reset_summary">Restore custom preferences to defaults</string>
|
||||||
|
<string name="revanced_custom_force_amoled_title">Force AMOLED</string>
|
||||||
|
<string name="revanced_custom_force_amoled_summary">Use true black in the YouTube UI</string>
|
||||||
|
<string name="revanced_custom_pure_black_player_title">Pure Black Player</string>
|
||||||
|
<string name="revanced_custom_pure_black_player_summary">Make player background pure black</string>
|
||||||
|
<string name="revanced_custom_disable_ambient_mode_title">Disable Ambient Mode</string>
|
||||||
|
<string name="revanced_custom_disable_ambient_mode_summary">Prevent Ambient Mode from activating</string>
|
||||||
|
<string name="revanced_custom_hide_shorts_title">Hide Shorts</string>
|
||||||
|
<string name="revanced_custom_hide_shorts_summary">Remove Shorts UI entries</string>
|
||||||
|
<string name="revanced_custom_default_playback_speed_title">Default Playback Speed</string>
|
||||||
|
<string name="revanced_custom_default_playback_speed_summary">Select default playback speed</string>
|
||||||
|
<string name="revanced_custom_remember_playback_speed_title">Remember Playback Speed</string>
|
||||||
|
<string name="revanced_custom_remember_playback_speed_summary">Remember speed across videos</string>
|
||||||
|
<string name="revanced_custom_default_video_quality_title">Default Video Quality</string>
|
||||||
|
<string name="revanced_custom_default_video_quality_summary">Select default video quality</string>
|
||||||
|
<string name="revanced_custom_remember_video_quality_title">Remember Video Quality</string>
|
||||||
|
<string name="revanced_custom_remember_video_quality_summary">Remember quality across videos</string>
|
||||||
|
<string name="revanced_custom_force_highest_quality_title">Force Highest Available Quality</string>
|
||||||
|
<string name="revanced_custom_force_highest_quality_summary">Prefer highest quality option</string>
|
||||||
|
<string name="revanced_custom_debug_video_info_title">Debug Video Information</string>
|
||||||
|
<string name="revanced_custom_debug_video_info_summary">Show technical video details</string>
|
||||||
|
<string name="revanced_custom_app_name_title">Custom App Name</string>
|
||||||
|
<string name="revanced_custom_app_name_summary">Change the visible application name</string>
|
||||||
|
<string name="revanced_custom_startup_tab_title">Default Startup Tab</string>
|
||||||
|
<string name="revanced_custom_startup_tab_summary">Select startup tab</string>
|
||||||
|
<string name="revanced_custom_clean_urls_title">Clean YouTube URLs</string>
|
||||||
|
<string name="revanced_custom_clean_urls_summary">Remove tracking parameters from shared links</string>
|
||||||
</patch>
|
</patch>
|
||||||
</app>
|
</app>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user