feat(YouTube): Support version 19.44.39

This commit is contained in:
inotia00 2024-12-21 14:54:08 +09:00
parent 2b77e46f5e
commit dc0a5973ad
24 changed files with 294 additions and 181 deletions

View File

@ -18,6 +18,7 @@ public class SponsorBlockSettings {
public void settingsImported(@Nullable Context context) {
SegmentCategory.loadAllCategoriesFromSettings();
}
@Override
public void settingsExported(@Nullable Context context) {
}

View File

@ -2,7 +2,6 @@ package app.revanced.extension.music.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.FrameLayout;

View File

@ -64,14 +64,14 @@ public class GmsCoreSupport {
// Use a delay to allow the activity to finish initializing.
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
Utils.runOnMainThreadDelayed(() ->
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the battery change can never be satisfied.
new AlertDialog.Builder(context)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
.show(),
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the battery change can never be satisfied.
new AlertDialog.Builder(context)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
.show(),
100
);
}

View File

@ -74,6 +74,11 @@ public class PlayerPatch {
// region [Ambient mode control] patch
/**
* Constant found in: androidx.window.embedding.DividerAttributes
*/
private static final int DIVIDER_ATTRIBUTES_COLOR_SYSTEM_DEFAULT = -16777216;
public static boolean bypassAmbientModeRestrictions(boolean original) {
return (!Settings.BYPASS_AMBIENT_MODE_RESTRICTIONS.get() && original) || Settings.DISABLE_AMBIENT_MODE.get();
}
@ -82,6 +87,14 @@ public class PlayerPatch {
return !Settings.DISABLE_AMBIENT_MODE_IN_FULLSCREEN.get();
}
public static int getFullScreenBackgroundColor(int originalColor) {
if (Settings.DISABLE_AMBIENT_MODE_IN_FULLSCREEN.get()) {
return DIVIDER_ATTRIBUTES_COLOR_SYSTEM_DEFAULT;
}
return originalColor;
}
// endregion
// region [Change player flyout menu toggles] patch
@ -424,6 +437,35 @@ public class PlayerPatch {
return !Settings.HIDE_PLAYER_PREVIOUS_NEXT_BUTTON.get() && previousOrNextButtonVisible;
}
private static final int playerControlPreviousButtonTouchAreaId =
ResourceUtils.getIdIdentifier("player_control_previous_button_touch_area");
private static final int playerControlNextButtonTouchAreaId =
ResourceUtils.getIdIdentifier("player_control_next_button_touch_area");
public static void hidePreviousNextButtons(View parentView) {
if (!Settings.HIDE_PLAYER_PREVIOUS_NEXT_BUTTON.get()) {
return;
}
// Must use a deferred call to main thread to hide the button.
// Otherwise the layout crashes if set to hidden now.
Utils.runOnMainThread(() -> {
hideView(parentView, playerControlPreviousButtonTouchAreaId);
hideView(parentView, playerControlNextButtonTouchAreaId);
});
}
private static void hideView(View parentView, int resourceId) {
View nextPreviousButton = parentView.findViewById(resourceId);
if (nextPreviousButton == null) {
Logger.printException(() -> "Could not find player previous / next button");
return;
}
Utils.hideViewByRemovingFromParentUnderCondition(true, nextPreviousButton);
}
public static boolean hideMusicButton() {
return Settings.HIDE_PLAYER_YOUTUBE_MUSIC_BUTTON.get();
}

View File

@ -1,5 +1,10 @@
package app.revanced.extension.youtube.patches.shorts;
import static app.revanced.extension.shared.utils.ResourceUtils.getString;
import static app.revanced.extension.shared.utils.Utils.dpToPx;
import static app.revanced.extension.youtube.patches.components.ShortsCustomActionsFilter.isShortsFlyoutMenuVisible;
import static app.revanced.extension.youtube.utils.ExtendedUtils.isSpoofingToLessThan;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.ColorFilter;
@ -10,13 +15,26 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.support.v7.widget.RecyclerView;
import android.view.*;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils;
@ -26,17 +44,6 @@ import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
import app.revanced.extension.youtube.utils.ThemeUtils;
import app.revanced.extension.youtube.utils.VideoUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import static app.revanced.extension.shared.utils.ResourceUtils.getString;
import static app.revanced.extension.shared.utils.Utils.dpToPx;
import static app.revanced.extension.youtube.patches.components.ShortsCustomActionsFilter.isShortsFlyoutMenuVisible;
import static app.revanced.extension.youtube.utils.ExtendedUtils.isSpoofingToLessThan;
@SuppressWarnings("unused")
public final class CustomActionsPatch {

View File

@ -34,6 +34,7 @@ public class SponsorBlockSettings {
SegmentCategory.loadAllCategoriesFromSettings();
SponsorBlockSettingsPreference.updateSegmentCategories();
}
@Override
public void settingsExported(@Nullable Context context) {
showExportWarningIfNeeded(context);

View File

@ -22,11 +22,11 @@ internal val actionBarRingoBackgroundFingerprint = legacyFingerprint(
returnType = "Landroid/view/View;",
literals = listOf(actionBarRingoBackground),
customFingerprint = { method, _ ->
indexOfStaticInstruction(method) >= 0
indexOfActionBarRingoBackgroundTabletInstruction(method) >= 0
}
)
internal fun indexOfStaticInstruction(method: Method) =
internal fun indexOfActionBarRingoBackgroundTabletInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
@ -54,7 +54,7 @@ internal val actionBarRingoTextFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
customFingerprint = { method, _ ->
indexOfStartDelayInstruction(method) >= 0 &&
indexOfStaticInstructions(method) >= 0
indexOfActionBarRingoTextTabletInstructions(method) >= 0
}
)
@ -64,7 +64,7 @@ internal fun indexOfStartDelayInstruction(method: Method) =
getReference<MethodReference>()?.name == "setStartDelay"
}
internal fun indexOfStaticInstructions(method: Method) =
internal fun indexOfActionBarRingoTextTabletInstructions(method: Method) =
method.indexOfFirstInstructionReversed(indexOfStartDelayInstruction(method)) {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
@ -175,24 +175,6 @@ internal val setActionBarRingoFingerprint = legacyFingerprint(
literals = listOf(actionBarRingo),
)
internal val setWordMarkHeaderFingerprint = legacyFingerprint(
name = "setWordMarkHeaderFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/widget/ImageView;"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.CONST,
Opcode.INVOKE_STATIC,
)
)
@Suppress("SpellCheckingInspection")
internal val yoodlesImageViewFingerprint = legacyFingerprint(
name = "yoodlesImageViewFingerprint",

View File

@ -169,7 +169,7 @@ val toolBarComponentsPatch = bytecodePatch(
"invoke-static {v$viewRegister}, $GENERAL_CLASS_DESCRIPTOR->setWideSearchBarLayout(Landroid/view/View;)V"
)
val targetIndex = indexOfStaticInstruction(this) + 1
val targetIndex = indexOfActionBarRingoBackgroundTabletInstruction(this) + 1
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
injectSearchBarHook(
@ -180,7 +180,7 @@ val toolBarComponentsPatch = bytecodePatch(
}
actionBarRingoTextFingerprint.methodOrThrow(actionBarRingoBackgroundFingerprint).apply {
val targetIndex = indexOfStaticInstruction(this) + 1
val targetIndex = indexOfActionBarRingoTextTabletInstructions(this) + 1
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
injectSearchBarHook(

View File

@ -6,15 +6,20 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.AMBIENT_MODE_CONTROL
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_41_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstStringInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -26,7 +31,10 @@ val ambientModeSwitchPatch = bytecodePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
versionCheckPatch,
)
execute {
// region patch for bypass ambient mode restrictions
@ -83,10 +91,29 @@ val ambientModeSwitchPatch = bytecodePatch(
// region patch for disable ambient mode in fullscreen
ambientModeInFullscreenFingerprint.injectLiteralInstructionBooleanCall(
45389368L,
"$PLAYER_CLASS_DESCRIPTOR->disableAmbientModeInFullscreen()Z"
)
if (!is_19_41_or_greater) {
ambientModeInFullscreenFingerprint.injectLiteralInstructionBooleanCall(
AMBIENT_MODE_IN_FULLSCREEN_FEATURE_FLAG,
"$PLAYER_CLASS_DESCRIPTOR->disableAmbientModeInFullscreen()Z"
)
}
if (is_19_34_or_greater) {
setFullScreenBackgroundColorFingerprint.methodOrThrow().apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow {
getReference<MethodReference>()?.name == "setBackgroundColor"
}
val register = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
addInstructions(
insertIndex,
"""
invoke-static { v$register }, $PLAYER_CLASS_DESCRIPTOR->getFullScreenBackgroundColor(I)I
move-result v$register
""",
)
}
}
// endregion

View File

@ -4,10 +4,12 @@ import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal const val AMBIENT_MODE_IN_FULLSCREEN_FEATURE_FLAG = 45389368L
internal val ambientModeInFullscreenFingerprint = legacyFingerprint(
name = "ambientModeInFullscreenFingerprint",
returnType = "V",
literals = listOf(45389368L),
literals = listOf(AMBIENT_MODE_IN_FULLSCREEN_FEATURE_FLAG),
)
internal val powerSaveModeBroadcastReceiverFingerprint = legacyFingerprint(
@ -30,4 +32,15 @@ internal val powerSaveModeSyntheticFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"),
strings = listOf("android.os.action.POWER_SAVE_MODE_CHANGED")
)
internal val setFullScreenBackgroundColorFingerprint = legacyFingerprint(
name = "setFullScreenBackgroundColorFingerprint",
returnType = "V",
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
parameters = listOf("Z", "I", "I", "I", "I"),
customFingerprint = { method, classDef ->
classDef.type.endsWith("/YouTubePlayerViewNotForReflection;")
&& method.name == "onLayout"
},
)

View File

@ -14,22 +14,27 @@ import app.revanced.patches.youtube.utils.fix.bottomui.cfBottomUIPatch
import app.revanced.patches.youtube.utils.layoutConstructorFingerprint
import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_PLAYER_BUTTONS
import app.revanced.patches.youtube.utils.playservice.is_18_31_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.autoNavToggle
import app.revanced.patches.youtube.utils.resourceid.fullScreenButton
import app.revanced.patches.youtube.utils.resourceid.playerCollapseButton
import app.revanced.patches.youtube.utils.resourceid.playerControlPreviousButtonTouchArea
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.resourceid.titleAnchor
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.RegisterRangeInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val HAS_NEXT = 5
private const val HAS_PREVIOUS = 6
@ -162,21 +167,40 @@ val playerButtonsPatch = bytecodePatch(
// region patch for hide previous and next button
playerControlsVisibilityModelFingerprint.methodOrThrow().apply {
val callIndex = indexOfFirstInstructionOrThrow(Opcode.INVOKE_DIRECT_RANGE)
val callInstruction = getInstruction<RegisterRangeInstruction>(callIndex)
if (is_19_34_or_greater) {
layoutConstructorFingerprint.methodOrThrow().apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(playerControlPreviousButtonTouchArea)
val hasNextParameterRegister = callInstruction.startRegister + HAS_NEXT
val hasPreviousParameterRegister = callInstruction.startRegister + HAS_PREVIOUS
val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Landroid/view/View;"
}
addInstructions(
callIndex, """
invoke-static { v$hasNextParameterRegister }, $PLAYER_CLASS_DESCRIPTOR->hidePreviousNextButton(Z)Z
move-result v$hasNextParameterRegister
invoke-static { v$hasPreviousParameterRegister }, $PLAYER_CLASS_DESCRIPTOR->hidePreviousNextButton(Z)Z
move-result v$hasPreviousParameterRegister
"""
)
val viewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $PLAYER_CLASS_DESCRIPTOR" +
"->hidePreviousNextButtons(Landroid/view/View;)V",
)
}
} else {
playerControlsVisibilityModelFingerprint.methodOrThrow().apply {
val callIndex = indexOfFirstInstructionOrThrow(Opcode.INVOKE_DIRECT_RANGE)
val callInstruction = getInstruction<RegisterRangeInstruction>(callIndex)
val hasNextParameterRegister = callInstruction.startRegister + HAS_NEXT
val hasPreviousParameterRegister = callInstruction.startRegister + HAS_PREVIOUS
addInstructions(
callIndex, """
invoke-static { v$hasNextParameterRegister }, $PLAYER_CLASS_DESCRIPTOR->hidePreviousNextButton(Z)Z
move-result v$hasNextParameterRegister
invoke-static { v$hasPreviousParameterRegister }, $PLAYER_CLASS_DESCRIPTOR->hidePreviousNextButton(Z)Z
move-result v$hasPreviousParameterRegister
"""
)
}
}
// endregion

View File

@ -9,7 +9,6 @@ import app.revanced.patches.youtube.utils.resourceid.easySeekEduContainer
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircle
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo
import app.revanced.patches.youtube.utils.resourceid.notice
import app.revanced.patches.youtube.utils.resourceid.offlineActionsVideoDeletedUndoSnackbarText
import app.revanced.patches.youtube.utils.resourceid.scrubbing
import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing
@ -207,11 +206,12 @@ internal val lithoComponentOnClickListenerFingerprint = legacyFingerprint(
literals = listOf(componentLongClickListener),
)
internal val noticeOnClickListenerFingerprint = legacyFingerprint(
name = "noticeOnClickListenerFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literals = listOf(notice),
internal val engagementPanelPlaylistSyntheticFingerprint = legacyFingerprint(
name = "engagementPanelPlaylistSyntheticFingerprint",
strings = listOf("engagement-panel-playlist"),
customFingerprint = { _, classDef ->
classDef.interfaces.contains("Landroid/view/View${'$'}OnClickListener;")
}
)
internal val offlineActionsOnClickListenerFingerprint = legacyFingerprint(

View File

@ -369,30 +369,32 @@ val playerComponentsPatch = bytecodePatch(
arrayOf(
lithoComponentOnClickListenerFingerprint,
noticeOnClickListenerFingerprint,
offlineActionsOnClickListenerFingerprint,
startVideoInformerFingerprint,
).forEach { fingerprint ->
fingerprint.methodOrThrow().apply {
if (fingerprint == startVideoInformerFingerprint) {
hookInitVideoPanel(1)
} else {
val syntheticIndex =
indexOfFirstInstruction(Opcode.NEW_INSTANCE)
if (syntheticIndex >= 0) {
val syntheticReference =
getInstruction<ReferenceInstruction>(syntheticIndex).reference.toString()
val syntheticIndex =
indexOfFirstInstruction(Opcode.NEW_INSTANCE)
if (syntheticIndex >= 0) {
val syntheticReference =
getInstruction<ReferenceInstruction>(syntheticIndex).reference.toString()
findMethodOrThrow(syntheticReference) {
name == "onClick"
}.hookInitVideoPanel(0)
} else {
println("WARNING: target Opcode not found in ${fingerprint.first}")
}
findMethodOrThrow(syntheticReference) {
name == "onClick"
}.hookInitVideoPanel(0)
} else {
println("WARNING: target Opcode not found in ${fingerprint.first}")
}
}
}
findMethodOrThrow(
engagementPanelPlaylistSyntheticFingerprint.methodOrThrow().definingClass
) {
name == "onClick"
}.hookInitVideoPanel(0)
startVideoInformerFingerprint.methodOrThrow().hookInitVideoPanel(1)
engagementPanelBuilderFingerprint.methodOrThrow().apply {
addInstructionsWithLabels(
0, """

View File

@ -16,8 +16,8 @@ import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewHook
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverHook
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.rollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.utils.rollingNumberTextViewFingerprint
@ -43,9 +43,9 @@ val descriptionComponentsPatch = bytecodePatch(
dependsOn(
settingsPatch,
bottomSheetRecyclerViewPatch,
lithoFilterPatch,
playerTypeHookPatch,
recyclerViewTreeObserverPatch,
sharedResourceIdPatch,
versionCheckPatch,
)
@ -117,7 +117,7 @@ val descriptionComponentsPatch = bytecodePatch(
)
}
bottomSheetRecyclerViewHook("$PLAYER_CLASS_DESCRIPTOR->onVideoDescriptionCreate(Landroid/support/v7/widget/RecyclerView;)V")
recyclerViewTreeObserverHook("$PLAYER_CLASS_DESCRIPTOR->onVideoDescriptionCreate(Landroid/support/v7/widget/RecyclerView;)V")
settingArray += "SETTINGS: DESCRIPTION_INTERACTION"
}

View File

@ -19,6 +19,7 @@ import app.revanced.patches.youtube.utils.layoutConstructorFingerprint
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.patch.PatchList.FULLSCREEN_COMPONENTS
import app.revanced.patches.youtube.utils.playservice.is_18_42_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_41_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.autoNavPreviewStub
import app.revanced.patches.youtube.utils.resourceid.fullScreenEngagementPanel
@ -293,7 +294,7 @@ val fullscreenComponentsPatch = bytecodePatch(
// region patch for keep landscape mode
if (is_18_42_or_greater) {
if (is_18_42_or_greater && !is_19_41_or_greater) {
landScapeModeConfigFingerprint.methodOrThrow().apply {
val insertIndex = implementation!!.instructions.lastIndex
val insertRegister =
@ -319,6 +320,8 @@ val fullscreenComponentsPatch = bytecodePatch(
}
settingArray += "SETTINGS: KEEP_LANDSCAPE_MODE"
} else {
println("WARNING: \"Keep landscape mode\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
}
// endregion

View File

@ -201,64 +201,59 @@ val overlayButtonsPatch = resourcePatch(
"android.support.constraint.ConstraintLayout"
)
// Modify the layout of fullscreen button for newer YouTube versions (19.09.xx+)
arrayOf(
"youtube_controls_bottom_ui_container.xml",
"youtube_controls_fullscreen_button.xml",
"youtube_controls_cf_fullscreen_button.xml",
).forEach { xmlFile ->
val targetXml = get("res").resolve("layout").resolve(xmlFile)
if (targetXml.exists()) {
document("res/layout/$xmlFile").use { document ->
document.doRecursively loop@{ node ->
if (node !is Element) return@loop
// Note: Do not modify fullscreen button and multiview button
document("res/layout/youtube_controls_bottom_ui_container.xml").use { document ->
document.doRecursively loop@{ node ->
if (node !is Element) return@loop
// Change the relationship between buttons
node.getAttributeNode("yt:layout_constraintRight_toLeftOf")
?.let { attribute ->
if (attribute.textContent == "@id/fullscreen_button") {
attribute.textContent = "@+id/speed_dialog_button"
}
}
val (id, height, width) = Triple(
node.getAttribute("android:id"),
node.getAttribute("android:layout_height"),
node.getAttribute("android:layout_width")
)
val (heightIsNotZero, widthIsNotZero, isButton) = Triple(
height != "0.0dip",
width != "0.0dip",
id.endsWith("_button") || id == "@id/youtube_controls_fullscreen_button_stub"
)
// Adjust TimeBar and Chapter bottom padding
val timBarItem = mutableMapOf(
"@id/time_bar_chapter_title" to "16.0dip",
"@id/timestamps_container" to "14.0dip"
)
val layoutHeightWidth = if (widerButtonsSpace == true)
"56.0dip"
else
"48.0dip"
if (isButton) {
node.setAttribute("android:layout_marginBottom", marginBottom)
node.setAttribute("android:paddingLeft", "0.0dip")
node.setAttribute("android:paddingRight", "0.0dip")
node.setAttribute("android:paddingBottom", "22.0dip")
if (heightIsNotZero && widthIsNotZero) {
node.setAttribute("android:layout_height", layoutHeightWidth)
node.setAttribute("android:layout_width", layoutHeightWidth)
}
} else if (timBarItem.containsKey(id)) {
node.setAttribute("android:layout_marginBottom", marginBottom)
if (widerButtonsSpace != true) {
node.setAttribute("android:paddingBottom", timBarItem.getValue(id))
}
// Change the relationship between buttons
node.getAttributeNode("yt:layout_constraintRight_toLeftOf")
?.let { attribute ->
if (attribute.textContent == "@id/fullscreen_button") {
attribute.textContent = "@+id/speed_dialog_button"
}
}
val (id, height, width) = Triple(
node.getAttribute("android:id"),
node.getAttribute("android:layout_height"),
node.getAttribute("android:layout_width")
)
val (heightIsNotZero, widthIsNotZero, isButton) = Triple(
height != "0.0dip",
width != "0.0dip",
id.endsWith("_button") && id != "@id/multiview_button"
)
// Adjust TimeBar and Chapter bottom padding
val timBarItem = mutableMapOf(
"@id/time_bar_chapter_title" to "16.0dip",
"@id/timestamps_container" to "14.0dip"
)
val layoutHeightWidth = if (widerButtonsSpace == true)
"56.0dip"
else
"48.0dip"
if (isButton) {
node.setAttribute("android:layout_marginBottom", marginBottom)
node.setAttribute("android:paddingLeft", "0.0dip")
node.setAttribute("android:paddingRight", "0.0dip")
node.setAttribute("android:paddingBottom", "22.0dip")
if (heightIsNotZero && widthIsNotZero) {
node.setAttribute("android:layout_height", layoutHeightWidth)
node.setAttribute("android:layout_width", layoutHeightWidth)
}
} else if (timBarItem.containsKey(id)) {
node.setAttribute("android:layout_marginBottom", marginBottom)
if (widerButtonsSpace != true) {
node.setAttribute("android:paddingBottom", timBarItem.getValue(id))
}
}
if (id.equals("@id/youtube_controls_fullscreen_button_stub")) {
node.setAttribute("android:layout_width", layoutHeightWidth)
}
}
}

View File

@ -37,8 +37,8 @@ import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_28_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewHook
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverHook
import app.revanced.patches.youtube.utils.resourceid.bottomBarContainer
import app.revanced.patches.youtube.utils.resourceid.metaPanel
import app.revanced.patches.youtube.utils.resourceid.reelDynRemix
@ -158,9 +158,9 @@ private val shortsCustomActionsPatch = bytecodePatch(
description = "shortsCustomActionsPatch"
) {
dependsOn(
bottomSheetRecyclerViewPatch,
lithoFilterPatch,
playerTypeHookPatch,
recyclerViewTreeObserverPatch,
toolBarHookPatch,
videoIdPatch,
videoInformationPatch,
@ -332,7 +332,7 @@ private val shortsCustomActionsPatch = bytecodePatch(
}
}
bottomSheetRecyclerViewHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
recyclerViewTreeObserverHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
// endregion

View File

@ -1,11 +1,15 @@
package app.revanced.patches.youtube.utils
import app.revanced.patches.youtube.player.components.playerComponentsPatch
import app.revanced.patches.youtube.utils.resourceid.autoNavPreviewStub
import app.revanced.patches.youtube.utils.resourceid.autoNavToggle
import app.revanced.patches.youtube.utils.resourceid.fadeDurationFast
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor
import app.revanced.patches.youtube.utils.resourceid.insetOverlayViewLayout
import app.revanced.patches.youtube.utils.resourceid.menuItemView
import app.revanced.patches.youtube.utils.resourceid.playerControlNextButtonTouchArea
import app.revanced.patches.youtube.utils.resourceid.playerControlPreviousButtonTouchArea
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
import app.revanced.patches.youtube.utils.resourceid.seekUndoEduOverlayStub
import app.revanced.patches.youtube.utils.resourceid.totalTime
@ -62,7 +66,8 @@ internal val layoutConstructorFingerprint = legacyFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
strings = listOf("1.0x")
strings = listOf("1.0x"),
literals = listOf(autoNavToggle, autoNavPreviewStub, playerControlPreviousButtonTouchArea, playerControlNextButtonTouchArea),
)
internal val playbackRateBottomSheetBuilderFingerprint = legacyFingerprint(

View File

@ -15,7 +15,7 @@ internal object Constants {
"18.48.39", // This is the last version that do not use Rolling Number.
"19.05.36", // This is the last version with the least YouTube experimental flag.
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
"19.38.41", // This is the latest version supported by the RVX patch.
"19.44.39", // This is the latest version supported by the RVX patch.
)
)
}

View File

@ -10,9 +10,11 @@ import com.android.tools.smali.dexlib2.AccessFlags
* When this value is TRUE, Cairo Fragment is used.
* In this case, some of patches may be broken, so set this value to FALSE.
*/
internal const val CAIRO_FRAGMENT_FEATURE_FLAG = 45532100L
internal val carioFragmentConfigFingerprint = legacyFingerprint(
name = "carioFragmentConfigFingerprint",
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
literals = listOf(45532100L),
literals = listOf(CAIRO_FRAGMENT_FEATURE_FLAG),
)

View File

@ -4,14 +4,21 @@ import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal val bottomSheetRecyclerViewBuilderFingerprint = legacyFingerprint(
name = "bottomSheetRecyclerViewBuilderFingerprint",
literals = listOf(45382015L),
internal const val RECYCLER_VIEW_BUILDER_FEATURE_FLAG = 45382015L
internal val recyclerViewBuilderFingerprint = legacyFingerprint(
name = "recyclerViewBuilderFingerprint",
literals = listOf(RECYCLER_VIEW_BUILDER_FEATURE_FLAG),
)
internal val recyclerViewTreeObserverFingerprint = legacyFingerprint(
name = "recyclerViewTreeObserverFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
strings = listOf("LithoRVSLCBinder")
strings = listOf("LithoRVSLCBinder"),
customFingerprint = { method, _ ->
val parameterTypes = method.parameterTypes
parameterTypes.size > 2 &&
parameterTypes[1] == "Landroid/support/v7/widget/RecyclerView;"
}
)

View File

@ -5,7 +5,6 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.resolvable
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
@ -15,29 +14,27 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private lateinit var recyclerViewTreeObserverMutableMethod: MutableMethod
private var recyclerViewTreeObserverInsertIndex = 0
val bottomSheetRecyclerViewPatch = bytecodePatch(
description = "bottomSheetRecyclerViewPatch"
val recyclerViewTreeObserverPatch = bytecodePatch(
description = "recyclerViewTreeObserverPatch"
) {
execute {
/**
* If this value is false, OldQualityLayoutPatch and OldSpeedLayoutPatch will not work.
* If this value is false, RecyclerViewTreeObserver is not initialized.
* This value is usually true so this patch is not strictly necessary,
* But in very rare cases this value may be false.
* Therefore, we need to force this to be true.
*/
if (bottomSheetRecyclerViewBuilderFingerprint.resolvable()) {
bottomSheetRecyclerViewBuilderFingerprint.injectLiteralInstructionBooleanCall(
45382015L,
"0x1"
)
}
recyclerViewBuilderFingerprint.injectLiteralInstructionBooleanCall(
RECYCLER_VIEW_BUILDER_FEATURE_FLAG,
"0x1"
)
recyclerViewTreeObserverFingerprint.methodOrThrow().apply {
recyclerViewTreeObserverMutableMethod = this
val onDrawListenerIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT
&& getReference<FieldReference>()?.type == "Landroid/view/ViewTreeObserver${'$'}OnDrawListener;"
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/view/ViewTreeObserver${'$'}OnDrawListener;"
}
recyclerViewTreeObserverInsertIndex =
indexOfFirstInstructionReversedOrThrow(onDrawListenerIndex, Opcode.CHECK_CAST) + 1
@ -45,7 +42,7 @@ val bottomSheetRecyclerViewPatch = bytecodePatch(
}
}
fun bottomSheetRecyclerViewHook(descriptor: String) =
fun recyclerViewTreeObserverHook(descriptor: String) =
recyclerViewTreeObserverMutableMethod.addInstruction(
recyclerViewTreeObserverInsertIndex++,
"invoke-static/range { p2 .. p2 }, $descriptor"

View File

@ -134,14 +134,16 @@ var modernMiniPlayerRewindButton = -1L
private set
var musicAppDeeplinkButtonView = -1L
private set
var notice = -1L
private set
var notificationBigPictureIconWidth = -1L
private set
var offlineActionsVideoDeletedUndoSnackbarText = -1L
private set
var playerCollapseButton = -1L
private set
var playerControlPreviousButtonTouchArea = -1L
private set
var playerControlNextButtonTouchArea = -1L
private set
var playerVideoTitleView = -1L
private set
var posterArtWidthDefault = -1L
@ -478,10 +480,6 @@ internal val sharedResourceIdPatch = resourcePatch(
ID,
"music_app_deeplink_button_view"
]
notice = resourceMappings[
ID,
"notice"
]
notificationBigPictureIconWidth = resourceMappings[
DIMEN,
"notification_big_picture_icon_width"
@ -494,6 +492,14 @@ internal val sharedResourceIdPatch = resourcePatch(
ID,
"player_collapse_button"
]
playerControlPreviousButtonTouchArea = resourceMappings[
ID,
"player_control_previous_button_touch_area"
]
playerControlNextButtonTouchArea = resourceMappings[
ID,
"player_control_next_button_touch_area"
]
playerVideoTitleView = resourceMappings[
ID,
"player_video_title_view"

View File

@ -19,8 +19,8 @@ import app.revanced.patches.youtube.utils.flyoutmenu.flyoutMenuHookPatch
import app.revanced.patches.youtube.utils.patch.PatchList.VIDEO_PLAYBACK
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.qualityMenuViewInflateFingerprint
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewHook
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverPatch
import app.revanced.patches.youtube.utils.recyclerview.recyclerViewTreeObserverHook
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
@ -80,7 +80,6 @@ val videoPlaybackPatch = bytecodePatch(
dependsOn(
settingsPatch,
bottomSheetRecyclerViewPatch,
customPlaybackSpeedPatch(
"$VIDEO_PATH/CustomPlaybackSpeedPatch;",
8.0f
@ -88,6 +87,7 @@ val videoPlaybackPatch = bytecodePatch(
flyoutMenuHookPatch,
lithoFilterPatch,
playerTypeHookPatch,
recyclerViewTreeObserverPatch,
shortsPlaybackPatch,
videoIdPatch,
videoInformationPatch,
@ -102,7 +102,7 @@ val videoPlaybackPatch = bytecodePatch(
// region patch for custom playback speed
bottomSheetRecyclerViewHook("$EXTENSION_CUSTOM_PLAYBACK_SPEED_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
recyclerViewTreeObserverHook("$EXTENSION_CUSTOM_PLAYBACK_SPEED_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
addLithoFilter(PLAYBACK_SPEED_MENU_FILTER_CLASS_DESCRIPTOR)
// endregion
@ -249,7 +249,7 @@ val videoPlaybackPatch = bytecodePatch(
} ?: throw PatchException("Failed to find onItemClick method")
}
bottomSheetRecyclerViewHook("$EXTENSION_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
recyclerViewTreeObserverHook("$EXTENSION_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
addLithoFilter(VIDEO_QUALITY_MENU_FILTER_CLASS_DESCRIPTOR)
// endregion