feat(YouTube): Support version 19.34.42

This commit is contained in:
inotia00
2024-12-09 22:50:53 +09:00
parent 977b7f481b
commit 53c64552dd
31 changed files with 744 additions and 400 deletions

View File

@ -14,6 +14,7 @@ import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyResources
import app.revanced.util.getAdaptiveIconResourceFile
import app.revanced.util.getResourceGroup
import app.revanced.util.underBarOrThrow
import org.w3c.dom.Element
@ -248,38 +249,23 @@ val customBrandingIconPatch = resourcePatch(
return@execute
}
fun getAdaptiveIconResourceFile(tag: String): String {
document("res/mipmap-anydpi/ic_launcher_release.xml").use { document ->
val adaptiveIcon = document
.getElementsByTagName("adaptive-icon")
.item(0) as Element
val childNodes = adaptiveIcon.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i)
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
return node.getAttribute("android:drawable").split("/")[1]
}
}
throw PatchException("Element not found: $tag")
}
}
mapOf(
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("background"),
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("foreground")
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "background"),
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "foreground")
).forEach { (oldIconResourceFile, newIconResourceFile) ->
mipmapDirectories.forEach {
val mipmapDirectory = resourceDirectory.resolve(it)
Files.move(
mipmapDirectory
.resolve("$oldIconResourceFile.png")
.toPath(),
mipmapDirectory
.resolve("$newIconResourceFile.png")
.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
if (oldIconResourceFile != newIconResourceFile) {
mipmapDirectories.forEach {
val mipmapDirectory = resourceDirectory.resolve(it)
Files.move(
mipmapDirectory
.resolve("$oldIconResourceFile.png")
.toPath(),
mipmapDirectory
.resolve("$newIconResourceFile.png")
.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
}

View File

@ -112,10 +112,6 @@ private val settingsBytecodePatch = bytecodePatch(
// endregion
injectOnCreateMethodCall(
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
"setDeviceInformation"
)
injectOnCreateMethodCall(
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
"onCreate"

View File

@ -147,18 +147,22 @@ val feedComponentsPatch = bytecodePatch(
// region patch for hide floating button
onCreateMethod.apply {
val fabIndex = indexOfFirstInstructionOrThrow {
val stringIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_STRING &&
getReference<StringReference>()?.string == "fab"
}
val fabRegister = getInstruction<OneRegisterInstruction>(fabIndex).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(fabIndex + 1, Opcode.CONST_STRING)
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
val insertIndex = indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.name == "<init>"
}
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex, Opcode.CONST_STRING)
addInstructionsWithLabels(
fabIndex, """
invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton()Z
move-result v$fabRegister
if-nez v$fabRegister, :hide
insertIndex, """
invoke-static {v$stringRegister}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$stringRegister
if-eqz v$stringRegister, :hide
""", ExternalLabel("hide", getInstruction(jumpIndex))
)
}

View File

@ -2,20 +2,18 @@
package app.revanced.patches.youtube.general.miniplayer
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.resourceid.floatyBarTopMargin
import app.revanced.patches.youtube.utils.resourceid.miniplayerMaxSize
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerForwardButton
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerRewindButton
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWhite
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.util.MethodUtil
internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint(
name = "miniplayerDimensionsCalculatorParentFingerprint",
@ -25,6 +23,9 @@ internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint
literals = listOf(floatyBarTopMargin),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
name = "miniplayerModernAddViewListenerFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
@ -32,6 +33,9 @@ internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
parameters = listOf("Landroid/view/View;")
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
name = "miniplayerModernCloseButtonFingerprint",
returnType = "Landroid/widget/ImageView;",
@ -40,41 +44,33 @@ internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerClose),
)
private var constructorMethodCount = 0
internal fun isMultiConstructorMethod() = constructorMethodCount > 1
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
// In later targets this feature flag does nothing and is dead code.
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L
internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L
internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L
internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L
internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L
internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L
internal val miniplayerModernConstructorFingerprint = legacyFingerprint(
name = "miniplayerModernConstructorFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
literals = listOf(45623000L),
customFingerprint = custom@{ method, classDef ->
classDef.methods.forEach {
if (MethodUtil.isConstructor(it)) constructorMethodCount += 1
}
if (!is_19_25_or_greater)
return@custom true
// Double tap action (Used in YouTube 19.25.39+).
method.containsLiteralInstruction(45628823L)
&& method.containsLiteralInstruction(45630429L)
}
)
internal val miniplayerModernDragAndDropFingerprint = legacyFingerprint(
name = "miniplayerModernDragAndDropFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
literals = listOf(45628752L),
)
internal val miniplayerModernEnabledFingerprint = legacyFingerprint(
name = "miniplayerModernEnabledFingerprint",
literals = listOf(45622882L),
internal val miniplayerOnCloseHandlerFingerprint = legacyFingerprint(
name = "miniplayerOnCloseHandlerFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
literals = listOf(MINIPLAYER_DISABLED_FEATURE_KEY),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
name = "miniplayerModernExpandButtonFingerprint",
returnType = "Landroid/widget/ImageView;",
@ -83,6 +79,9 @@ internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerExpand),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint(
name = "miniplayerModernExpandCloseDrawablesFingerprint",
returnType = "V",
@ -91,6 +90,9 @@ internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint
literals = listOf(ytOutlinePictureInPictureWhite),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
name = "miniplayerModernForwardButtonFingerprint",
returnType = "Landroid/widget/ImageView;",
@ -99,6 +101,9 @@ internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
literals = listOf(modernMiniPlayerForwardButton),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
name = "miniplayerModernOverlayViewFingerprint",
returnType = "V",
@ -107,6 +112,9 @@ internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
literals = listOf(scrimOverlay),
)
/**
* Matches using the class found in [miniplayerModernViewParentFingerprint].
*/
internal val miniplayerModernRewindButtonFingerprint = legacyFingerprint(
name = "miniplayerModernRewindButtonFingerprint",
returnType = "Landroid/widget/ImageView;",
@ -123,11 +131,16 @@ internal val miniplayerModernViewParentFingerprint = legacyFingerprint(
strings = listOf("player_overlay_modern_mini_player_controls")
)
internal val miniplayerMinimumSizeFingerprint = legacyFingerprint(
name = "miniplayerMinimumSizeFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literals = listOf(192L, 128L, miniplayerMaxSize),
)
internal val miniplayerOverrideFingerprint = legacyFingerprint(
name = "miniplayerOverrideFingerprint",
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("appName")
)
@ -144,6 +157,7 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
returnType = "L",
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST,
Opcode.CHECK_CAST,
Opcode.INVOKE_STATIC,
@ -152,12 +166,12 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
)
)
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
internal val youTubePlayerOverlaysLayoutFingerprint = legacyFingerprint(
name = "youTubePlayerOverlaysLayoutFingerprint",
customFingerprint = { _, classDef ->
classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
}
)
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"

View File

@ -1,22 +1,22 @@
package app.revanced.patches.youtube.general.miniplayer
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
import app.revanced.patches.youtube.utils.patch.PatchList.MINIPLAYER
import app.revanced.patches.youtube.utils.playservice.is_19_15_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_26_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_29_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_36_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_43_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWh
import app.revanced.patches.youtube.utils.resourceid.ytOutlineXWhite
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
@ -41,14 +42,13 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
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.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR =
"$GENERAL_PATH/MiniplayerPatch;"
@ -83,7 +83,7 @@ val miniplayerPatch = bytecodePatch(
"""
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register
"""
"""
)
}
@ -105,39 +105,34 @@ val miniplayerPatch = bytecodePatch(
* Adds an override to specify which modern miniplayer is used.
*/
fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
val targetReference = (targetInstruction as ReferenceInstruction).reference
val register = getInstruction<TwoRegisterInstruction>(iPutIndex).registerA
addInstructions(
iPutIndex + 1, """
invoke-static { v${targetInstruction.registerA} }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v${targetInstruction.registerA}
# Original instruction
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
"""
addInstructionsAtControlFlowLabel(
iPutIndex,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v$register
""",
)
removeInstruction(iPutIndex)
}
fun Pair<String, Fingerprint>.hookInflatedView(
fun MutableMethod.hookInflatedView(
literalValue: Long,
hookedClassType: String,
extensionMethodName: String,
) {
methodOrThrow(miniplayerModernViewParentFingerprint).apply {
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstLiteralInstructionOrThrow(literalValue)
) {
opcode == Opcode.CHECK_CAST &&
getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $extensionMethodName"
)
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstLiteralInstructionOrThrow(literalValue)
) {
opcode == Opcode.CHECK_CAST &&
getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $extensionMethodName"
)
}
// Modern mini player is only present and functional in 19.15+.
@ -184,7 +179,7 @@ val miniplayerPatch = bytecodePatch(
}
if (isPatchingOldVersion) {
settingArray += "SETTINGS: MINIPLAYER_TYPE_LEGACY"
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_14"
addPreference(settingArray, MINIPLAYER)
// Return here, as patch below is only intended for new versions of the app.
@ -197,14 +192,14 @@ val miniplayerPatch = bytecodePatch(
miniplayerModernConstructorFingerprint.mutableClassOrThrow().methods.forEach {
it.apply {
if (MethodUtil.isConstructor(it)) {
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
val iPutIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT &&
getReference<FieldReference>()?.type == "I"
this.opcode == Opcode.IPUT &&
this.getReference<FieldReference>()?.type == "I"
}
insertModernMiniplayerTypeOverride(iPutIndex)
} else if (isMultiConstructorMethod()) {
} else {
findReturnIndicesReversed().forEach { index ->
insertModernMiniplayerOverride(
index
@ -214,27 +209,96 @@ val miniplayerPatch = bytecodePatch(
}
}
if (is_19_25_or_greater) {
miniplayerModernEnabledFingerprint.injectLiteralInstructionBooleanCall(
45622882L,
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
if (is_19_23_or_greater) {
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
}
// endregion
// region Enable double tap action.
if (is_19_25_or_greater) {
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
45628823L,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction()Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
45630429L,
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_MODERN_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getModernFeatureFlagsActiveOverride(Z)Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction(Z)Z"
)
if (!is_19_29_or_greater) {
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
}
}
if (is_19_26_or_greater) {
miniplayerModernConstructorFingerprint.methodOrThrow().apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
MINIPLAYER_INITIAL_SIZE_FEATURE_KEY,
)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I
move-result v$register
""",
)
}
// Override a minimum size constant.
miniplayerMinimumSizeFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_16 &&
(this as NarrowLiteralInstruction).narrowLiteral == 192
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
// Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller.
// The 170 initial limit probably could be patched to allow even smaller initial sizes,
// but 170 is already half the horizontal space and smaller does not seem useful.
replaceInstruction(index, "const/16 v$register, 170")
}
settingArray += "SETTINGS: MINIPLAYER_WIDTH_DIP"
} else {
settingArray += "SETTINGS: MINIPLAYER_REWIND_FORWARD"
}
if (is_19_36_or_greater) {
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->setRoundedCorners(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_ROUNDED_CONERS"
}
if (is_19_43_or_greater) {
miniplayerOnCloseHandlerFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_DISABLED_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->getMiniplayerOnCloseHandler(Z)Z"
)
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
"$EXTENSION_CLASS_DESCRIPTOR->setHorizontalDrag(Z)Z"
)
settingArray += "SETTINGS: MINIPLAYER_HORIZONTAL_DRAG"
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_43"
} else {
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_16"
}
// endregion
@ -291,7 +355,7 @@ val miniplayerPatch = bytecodePatch(
"adjustMiniplayerOpacity"
)
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.hookInflatedView(
fingerprint.methodOrThrow(miniplayerModernViewParentFingerprint).hookInflatedView(
literalValue,
"Landroid/widget/ImageView;",
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
@ -300,23 +364,19 @@ val miniplayerPatch = bytecodePatch(
miniplayerModernAddViewListenerFingerprint.methodOrThrow(
miniplayerModernViewParentFingerprint
).apply {
addInstructionsWithLabels(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideMiniplayerSubTexts(Landroid/view/View;)Z
move-result v0
if-nez v0, :hidden
""",
ExternalLabel("hidden", getInstruction(implementation!!.instructions.lastIndex))
)
}
).addInstruction(
0,
"invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" +
"hideMiniplayerSubTexts(Landroid/view/View;)V",
)
// Modern 2 has a broken overlay subtitle view that is always present.
// Modern 2 uses the same overlay controls as the regular video player,
// and the overlay views are added at runtime.
// Add a hook to the overlay class, and pass the added views to extension.
//
// NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller.
// This patch code could be used to hide other player overlays that do not use Litho.
youTubePlayerOverlaysLayoutFingerprint.matchOrThrow().let {
it.method.apply {
it.classDef.methods.add(
@ -352,18 +412,6 @@ val miniplayerPatch = bytecodePatch(
// endregion
// region Enable drag and drop.
if (is_19_23_or_greater) {
miniplayerModernDragAndDropFingerprint.injectLiteralInstructionBooleanCall(
45628752L,
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop()Z"
)
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
}
// endregion
settingArray += "SETTINGS: MINIPLAYER_TYPE_MODERN"
// region add settings

View File

@ -1,17 +1,69 @@
package app.revanced.patches.youtube.general.spoofappversion
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_APP_VERSION
import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_39_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_23_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.patches.youtube.utils.toolBarButtonFingerprint
import app.revanced.util.appendAppVersion
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private val spoofAppVersionBytecodePatch = bytecodePatch(
description = "spoofAppVersionBytecodePatch"
) {
dependsOn(versionCheckPatch)
execute {
if (!is_19_23_or_greater) {
return@execute
}
/**
* When spoofing the app version to YouTube 19.20.xx or earlier via Spoof app version on YouTube 19.23.xx+, the Library tab will crash.
* As a temporary workaround, do not set an image in the toolbar when the enum name is UNKNOWN.
*/
toolBarButtonFingerprint.methodOrThrow().apply {
val getDrawableIndex = indexOfGetDrawableInstruction(this)
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.returnType == "I"
}
val insertIndex = enumOrdinalIndex + 2
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setImageDrawable"
} + 1
addInstructionsWithLabels(
insertIndex, """
if-eqz v$insertRegister, :ignore
""", ExternalLabel("ignore", getInstruction(jumpIndex))
)
}
}
}
@Suppress("unused")
val spoofAppVersionPatch = resourcePatch(
@ -22,6 +74,7 @@ val spoofAppVersionPatch = resourcePatch(
dependsOn(
baseSpoofAppVersionPatch("$GENERAL_CLASS_DESCRIPTOR->getVersionOverride(Ljava/lang/String;)Ljava/lang/String;"),
spoofAppVersionBytecodePatch,
settingsPatch,
versionCheckPatch,
)

View File

@ -297,6 +297,7 @@ val toolBarComponentsPatch = bytecodePatch(
// endregion
/*
// region patch for hide voice search button
if (is_19_28_or_greater) {
@ -311,6 +312,7 @@ val toolBarComponentsPatch = bytecodePatch(
}
// endregion
*/
// region patch for hide voice search button

View File

@ -5,6 +5,8 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE
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.settings.ResourceUtils.updatePatchStatusIcon
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
@ -12,9 +14,16 @@ import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyFile
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
import app.revanced.util.getAdaptiveIconResourceFile
import app.revanced.util.getResourceGroup
import app.revanced.util.underBarOrThrow
import java.nio.file.Files
import java.nio.file.StandardCopyOption
private const val ADAPTIVE_ICON_BACKGROUND_FILE_NAME =
"adaptiveproduct_youtube_background_color_108"
private const val ADAPTIVE_ICON_FOREGROUND_FILE_NAME =
"adaptiveproduct_youtube_foreground_color_108"
private const val DEFAULT_ICON = "revancify_blue"
private val availableIcon = mapOf(
@ -39,8 +48,8 @@ private val drawableDirectories = sizeArray.map { "drawable-$it" }
private val mipmapDirectories = sizeArray.map { "mipmap-$it" }
private val launcherIconResourceFileNames = arrayOf(
"adaptiveproduct_youtube_background_color_108",
"adaptiveproduct_youtube_foreground_color_108",
ADAPTIVE_ICON_BACKGROUND_FILE_NAME,
ADAPTIVE_ICON_FOREGROUND_FILE_NAME,
"ic_launcher",
"ic_launcher_round"
).map { "$it.png" }.toTypedArray()
@ -83,7 +92,10 @@ val customBrandingIconPatch = resourcePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
versionCheckPatch,
)
val appIconOption = stringOption(
@ -181,5 +193,32 @@ val customBrandingIconPatch = resourcePatch(
updatePatchStatusIcon(appIcon)
}
if (!is_19_34_or_greater) {
return@execute
}
if (appIcon == "youtube") {
return@execute
}
mapOf(
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "background"),
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "foreground")
).forEach { (oldIconResourceFile, newIconResourceFile) ->
if (oldIconResourceFile != newIconResourceFile) {
mipmapDirectories.forEach {
val mipmapDirectory = get("res").resolve(it)
Files.copy(
mipmapDirectory
.resolve("$oldIconResourceFile.png")
.toPath(),
mipmapDirectory
.resolve("$newIconResourceFile.png")
.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
}
}
}

View File

@ -386,6 +386,8 @@ val playerComponentsPatch = bytecodePatch(
findMethodOrThrow(syntheticReference) {
name == "onClick"
}.hookInitVideoPanel(0)
} else {
println("WARNING: target Opcode not found in ${fingerprint.first}")
}
}
}

View File

@ -5,6 +5,7 @@ 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.scrimOverlay
import app.revanced.patches.youtube.utils.resourceid.seekUndoEduOverlayStub
import app.revanced.patches.youtube.utils.resourceid.totalTime
@ -12,9 +13,13 @@ import app.revanced.patches.youtube.utils.resourceid.varispeedUnavailableTitle
import app.revanced.patches.youtube.utils.resourceid.videoQualityBottomSheet
import app.revanced.patches.youtube.utils.sponsorblock.sponsorBlockBytecodePatch
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val engagementPanelBuilderFingerprint = legacyFingerprint(
name = "engagementPanelBuilderFingerprint",
@ -164,6 +169,23 @@ internal val seekbarOnDrawFingerprint = legacyFingerprint(
customFingerprint = { method, _ -> method.name == "onDraw" }
)
internal fun indexOfGetDrawableInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() == "Landroid/content/res/Resources;->getDrawable(I)Landroid/graphics/drawable/Drawable;"
}
internal val toolBarButtonFingerprint = legacyFingerprint(
name = "toolBarButtonFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/MenuItem;"),
literals = listOf(menuItemView),
customFingerprint = { method, _ ->
indexOfGetDrawableInstruction(method) >= 0
}
)
internal val totalTimeFingerprint = legacyFingerprint(
name = "totalTimeFingerprint",
returnType = "V",

View File

@ -14,7 +14,8 @@ internal object Constants {
"18.38.44", // This is the last version with no delay in applying video quality on the server side.
"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 latest version supported by the RVX patch.
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
"19.34.42", // This is the latest version supported by the RVX patch.
)
)
}

View File

@ -134,8 +134,8 @@ private val playerControlsBytecodePatch = bytecodePatch(
changeVisibilityMethod =
findMethodOrThrow(EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR) {
name == "changeVisibility"
&& parameters == listOf("Z", "Z")
name == "changeVisibility" &&
parameters == listOf("Z", "Z")
}
changeVisibilityNegatedImmediatelyMethod =

View File

@ -10,12 +10,6 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal fun indexOfLayoutDirectionInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Landroid/view/View;->setLayoutDirection(I)V"
}
internal val browseIdClassFingerprint = legacyFingerprint(
name = "browseIdClassFingerprint",
returnType = "Ljava/lang/Object;",
@ -45,18 +39,26 @@ internal val reelWatchPagerFingerprint = legacyFingerprint(
literals = listOf(reelWatchPlayer),
)
internal fun indexOfStringIsEmptyInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Ljava/lang/String;->isEmpty()Z"
}
internal val searchQueryClassFingerprint = legacyFingerprint(
name = "searchQueryClassFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "Ljava/util/Map;"),
opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
),
strings = listOf("force_enable_sticky_browsy_bars"),
customFingerprint = { method, _ ->
indexOfStringIsEmptyInstruction(method) >= 0
}
)
internal val videoStateFingerprint = legacyFingerprint(

View File

@ -135,32 +135,30 @@ val playerTypeHookPatch = bytecodePatch(
// region patch for hook search bar
searchQueryClassFingerprint.matchOrThrow().let {
it.method.apply {
val searchQueryIndex = it.patternMatch!!.startIndex + 1
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
searchQueryClassFingerprint.methodOrThrow().apply {
val searchQueryIndex = indexOfStringIsEmptyInstruction(this) - 1
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
findMethodOrThrow(searchQueryClass).apply {
val smaliInstructions =
findMethodOrThrow(searchQueryClass).apply {
val smaliInstructions =
"""
if-eqz v0, :ignore
iget-object v0, v0, $searchQueryFieldReference
if-eqz v0, :ignore
return-object v0
:ignore
const-string v0, ""
return-object v0
"""
if-eqz v0, :ignore
iget-object v0, v0, $searchQueryFieldReference
if-eqz v0, :ignore
return-object v0
:ignore
const-string v0, ""
return-object v0
"""
addStaticFieldToExtension(
EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR,
"getSearchQuery",
"searchQueryClass",
definingClass,
smaliInstructions
)
}
addStaticFieldToExtension(
EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR,
"getSearchQuery",
"searchQueryClass",
definingClass,
smaliInstructions
)
}
}

View File

@ -23,10 +23,22 @@ var is_19_23_or_greater = false
private set
var is_19_25_or_greater = false
private set
var is_19_26_or_greater = false
private set
var is_19_28_or_greater = false
private set
var is_19_29_or_greater = false
private set
var is_19_32_or_greater = false
private set
var is_19_34_or_greater = false
private set
var is_19_36_or_greater = false
private set
var is_19_41_or_greater = false
private set
var is_19_43_or_greater = false
private set
var is_19_44_or_greater = false
private set
@ -53,8 +65,14 @@ val versionCheckPatch = resourcePatch(
is_19_15_or_greater = 241602000 <= playStoreServicesVersion
is_19_23_or_greater = 242402000 <= playStoreServicesVersion
is_19_25_or_greater = 242599000 <= playStoreServicesVersion
is_19_26_or_greater = 242705000 <= playStoreServicesVersion
is_19_28_or_greater = 242905000 <= playStoreServicesVersion
is_19_29_or_greater = 243005000 <= playStoreServicesVersion
is_19_32_or_greater = 243305000 <= playStoreServicesVersion
is_19_34_or_greater = 243499000 <= playStoreServicesVersion
is_19_36_or_greater = 243705000 <= playStoreServicesVersion
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
is_19_44_or_greater = 244505000 <= playStoreServicesVersion
}
}

View File

@ -120,6 +120,8 @@ var menuItemView = -1L
private set
var metaPanel = -1L
private set
var miniplayerMaxSize = -1L
private set
var modernMiniPlayerClose = -1L
private set
var modernMiniPlayerExpand = -1L
@ -444,6 +446,10 @@ internal val sharedResourceIdPatch = resourcePatch(
ID,
"metapanel"
]
miniplayerMaxSize = resourceMappings[
DIMEN,
"miniplayer_max_size",
]
modernMiniPlayerClose = resourceMappings[
ID,
"modern_miniplayer_close"

View File

@ -1,26 +1,10 @@
package app.revanced.patches.youtube.utils.toolbar
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.resourceid.menuItemView
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val toolBarButtonFingerprint = legacyFingerprint(
name = "toolBarButtonFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/MenuItem;"),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL
),
literals = listOf(menuItemView),
)
internal val toolBarPatchFingerprint = legacyFingerprint(
name = "toolBarPatchFingerprint",
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,

View File

@ -6,12 +6,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"$UTILS_PATH/ToolBarPatch;"
@ -24,30 +30,35 @@ val toolBarHookPatch = bytecodePatch(
dependsOn(sharedResourceIdPatch)
execute {
toolBarButtonFingerprint.matchOrThrow().let {
it.method.apply {
val replaceIndex = it.patternMatch!!.startIndex
val freeIndex = it.patternMatch!!.endIndex - 1
val replaceReference = getInstruction<ReferenceInstruction>(replaceIndex).reference
val replaceRegister =
getInstruction<FiveRegisterInstruction>(replaceIndex).registerC
val enumRegister = getInstruction<FiveRegisterInstruction>(replaceIndex).registerD
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
val imageViewIndex = replaceIndex + 2
val imageViewReference =
getInstruction<ReferenceInstruction>(imageViewIndex).reference
addInstructions(
replaceIndex + 1, """
iget-object v$freeRegister, p0, $imageViewReference
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
"""
)
removeInstruction(replaceIndex)
toolBarButtonFingerprint.methodOrThrow().apply {
val getDrawableIndex = indexOfGetDrawableInstruction(this)
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.returnType == "I"
}
val freeIndex = getDrawableIndex - 1
val replaceReference = getInstruction<ReferenceInstruction>(enumOrdinalIndex).reference
val replaceRegister =
getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerC
val enumRegister = getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerD
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
val imageViewIndex = indexOfFirstInstructionReversedOrThrow(enumOrdinalIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/widget/ImageView;"
}
val imageViewReference =
getInstruction<ReferenceInstruction>(imageViewIndex).reference
addInstructions(
enumOrdinalIndex + 1, """
iget-object v$freeRegister, p0, $imageViewReference
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
"""
)
removeInstruction(enumOrdinalIndex)
}
toolbarMethod = toolBarPatchFingerprint.methodOrThrow()

View File

@ -5,7 +5,6 @@ import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatchContext
import app.revanced.patcher.util.Document
import org.w3c.dom.Attr
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
@ -44,26 +43,6 @@ fun Node.cloneNodes(parent: Node) {
parent.removeChild(this)
}
/**
* Returns a sequence for all child nodes.
*/
fun NodeList.asSequence() = (0 until this.length).asSequence().map { this.item(it) }
/**
* Returns a sequence for all child nodes.
*/
@Suppress("UNCHECKED_CAST")
fun Node.childElementsSequence() =
this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } as Sequence<Element>
/**
* Performs the given [action] on each child element.
*/
inline fun Node.forEachChildElement(action: (Element) -> Unit) =
childElementsSequence().forEach {
action(it)
}
/**
* Recursively traverse the DOM tree starting from the given root node.
*
@ -74,20 +53,29 @@ fun Node.doRecursively(action: (Node) -> Unit) {
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
}
fun String.startsWithAny(vararg prefixes: String): Boolean {
for (prefix in prefixes)
if (this.startsWith(prefix))
return true
return false
}
fun List<String>.getResourceGroup(fileNames: Array<String>) = map { directory ->
ResourceGroup(
directory, *fileNames
)
}
fun ResourcePatchContext.getAdaptiveIconResourceFile(path: String, tag: String): String {
document(path).use { document ->
val adaptiveIcon = document
.getElementsByTagName("adaptive-icon")
.item(0) as Element
val childNodes = adaptiveIcon.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i)
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
return node.getAttribute("android:drawable").split("/")[1]
}
}
throw PatchException("Element not found: $tag")
}
}
fun ResourcePatchContext.appendAppVersion(appVersion: String) {
addEntryValues(
"revanced_spoof_app_version_target_entries",
@ -227,14 +215,6 @@ fun ResourcePatchContext.removeStringsElements(
}
}
fun Node.insertFirst(node: Node) {
if (hasChildNodes()) {
insertBefore(node, firstChild)
} else {
appendChild(node)
}
}
fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName)
child.block()
@ -294,21 +274,6 @@ internal fun inputStreamFromBundledResource(
*/
class ResourceGroup(val resourceDirectoryName: String, vararg val resources: String)
/**
* Iterate through the children of a node by its tag.
* @param resource The xml resource.
* @param targetTag The target xml node.
* @param callback The callback to call when iterating over the nodes.
*/
fun ResourcePatchContext.iterateXmlNodeChildren(
resource: String,
targetTag: String,
callback: (node: Node) -> Unit,
) = document(classLoader.getResourceAsStream(resource)!!).use { document ->
val stringsNode = document.getElementsByTagName(targetTag).item(0).childNodes
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
}
/**
* Copy resources from the current class loader to the resource directory.
* @param resourceDirectory The directory of the resource.
@ -383,12 +348,3 @@ internal fun NodeList.findElementByAttributeValue(attributeName: String, value:
internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String) =
findElementByAttributeValue(attributeName, value)
?: throw PatchException("Could not find: $attributeName $value")
internal fun Element.copyAttributesFrom(oldContainer: Element) {
// Copy attributes from the old element to the new element
val attributes = oldContainer.attributes
for (i in 0 until attributes.length) {
val attr = attributes.item(i) as Attr
setAttribute(attr.name, attr.value)
}
}