Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(YouTube - Miniplayer): Rename Tablet mini player and allow selecting the style of the in-app miniplayer #3302

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3c46f7b
fix(YouTube - Tablet mini player): Correctly show mini player buttons
LisoUseInAIKyrios Jun 4, 2024
0576089
fix(YouTube - Spoof client): Add support for 18.x
LisoUseInAIKyrios Jun 4, 2024
e99e880
feat(YouTube - Enable tablet layout): Modern tablet mini player
LisoUseInAIKyrios Jun 4, 2024
173fa91
refactor: Consolidate tablet settings in new submenu, Merge `EnableTa…
LisoUseInAIKyrios Jun 4, 2024
b086b08
refactor: Include the resource name in the exception if not found
LisoUseInAIKyrios Jun 4, 2024
a8a13b5
restrict to only working versions
LisoUseInAIKyrios Jun 4, 2024
9460336
feat: Adjust mini player overlay opacity
LisoUseInAIKyrios Jun 4, 2024
907f239
feat: Hide mini player back / forward buttons
LisoUseInAIKyrios Jun 4, 2024
4731c3a
refactor: Use throws method
LisoUseInAIKyrios Jun 4, 2024
b1e6c0a
fix: Fix icons of tablet modern mini player
LisoUseInAIKyrios Jun 5, 2024
508e3ee
feat: Allow selecting which mini player to use
LisoUseInAIKyrios Jun 5, 2024
f8076f5
refactor: removed fingerprint that patches code twice
LisoUseInAIKyrios Jun 5, 2024
36e4faf
Hide sub titles and 'continue watching'
LisoUseInAIKyrios Jun 5, 2024
93202b6
refactor: Mini Player -> Miniplayer
LisoUseInAIKyrios Jun 5, 2024
e83527b
fix: Allow patching older versions without modern miniplayer options
LisoUseInAIKyrios Jun 6, 2024
a98f668
fix: Rename back to `Enable tablet layout`
LisoUseInAIKyrios Jun 6, 2024
63fe654
refactor: Extract helper method
LisoUseInAIKyrios Jun 6, 2024
d5ca630
Comments
LisoUseInAIKyrios Jun 6, 2024
7730e2c
Refactor
LisoUseInAIKyrios Jun 6, 2024
e406117
refactor: Cleanup
LisoUseInAIKyrios Jun 6, 2024
ad8173f
refactor
LisoUseInAIKyrios Jun 6, 2024
2870b69
refactor
LisoUseInAIKyrios Jun 6, 2024
4a894f8
fix: Hide broken subtitle of modern 2 miniplayer
LisoUseInAIKyrios Jun 7, 2024
5810841
feat: Allow mini player 2
LisoUseInAIKyrios Jun 7, 2024
f09938c
fix: legacy tablet hook can still be required for new targets
LisoUseInAIKyrios Jun 7, 2024
74262a3
cleanup
LisoUseInAIKyrios Jun 7, 2024
91410b4
Update src/main/kotlin/app/revanced/util/BytecodeUtils.kt
LisoUseInAIKyrios Jun 7, 2024
47f8558
Update src/main/kotlin/app/revanced/util/BytecodeUtils.kt
LisoUseInAIKyrios Jun 7, 2024
bac4394
fixing build
LisoUseInAIKyrios Jun 7, 2024
2f93fdd
refactor
LisoUseInAIKyrios Jun 7, 2024
55e76ff
adjust text
LisoUseInAIKyrios Jun 7, 2024
06dd01d
Remove literal supplier property
LisoUseInAIKyrios Jun 7, 2024
e7cafe3
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
dcb078e
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
f488e10
refactor
LisoUseInAIKyrios Jun 7, 2024
70fec0e
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
0e1971c
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
06bf850
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
fe7c719
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
81531ae
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
bc062f9
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
8fa532d
refactor
LisoUseInAIKyrios Jun 7, 2024
2f0d4b9
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
LisoUseInAIKyrios Jun 7, 2024
e9abe8e
Update src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer…
oSumAtrIX Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,12 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down Expand Up @@ -1602,6 +1608,12 @@ public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayout
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/tablet/TabletLayoutPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tablet/TabletLayoutPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down Expand Up @@ -1901,6 +1913,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
Expand Down Expand Up @@ -1930,6 +1943,7 @@ public final class app/revanced/util/ResourceUtilsKt {
public abstract class app/revanced/util/patch/LiteralValueFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getLiteralSupplier ()Lkotlin/jvm/functions/Function0;
}

public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.mapping

import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import org.w3c.dom.Element
import java.util.*
Expand Down Expand Up @@ -51,9 +52,11 @@ object ResourceMappingPatch : ResourcePatch() {
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
}

operator fun get(type: String, name: String) = resourceMappings.first {
it.type == type && it.name == name
}.id
operator fun get(type: String, name: String) : Long {
return resourceMappings.firstOrNull {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
it.type == type && it.name == name
}?.id ?: throw PatchException("Could not find resource type: $type name: $name")
}

data class ResourceElement(val type: String, val name: String, val id: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
package app.revanced.patches.youtube.layout.miniplayer

import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandCloseDrawablesFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernForwardButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernOverlayViewFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernRewindButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernViewParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideNoContextFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerResponseModelSizeCheckFingerprint
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.patch.LiteralValueFingerprint
import app.revanced.util.resultOrThrow
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.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

// YT uses "Miniplayer" without a space between 'mini' and 'player.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
@Patch(
name = "Miniplayer",
description = "Adds options to change the in app minimized player",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
MiniplayerResourcePatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
// Hide modern miniplayer is only present in 19.15+
// If a robust way of detecting the target app version is added,
// then all changes except modern miniplayer could be applied to older versions.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
"19.15.36",
"19.16.39"
]
)
]
)
@Suppress("unused")
object MiniplayerPatch : BytecodePatch(
setOf(GetFormFactorFingerprint,
MiniplayerDimensionsCalculatorParentFingerprint,
MiniplayerResponseModelSizeCheckFingerprint,
MiniplayerOverrideFingerprint,
MiniplayerModernConstructorFingerprint,
MiniplayerModernViewParentFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/MiniplayerPatch;"

override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)

SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = "revanced_miniplayer_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
ListPreference(key = "revanced_miniplayer_type", summaryKey = null,),
SwitchPreference("revanced_miniplayer_hide_expand_close"),
SwitchPreference("revanced_miniplayer_hide_sub_text"),
SwitchPreference("revanced_miniplayer_hide_rewind_forward"),
TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
)
)
)

// region Enable tablet miniplayer.

MiniplayerOverrideNoContextFingerprint.resolve(
context,
MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef
)
MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply {
findReturnIndexes().forEach { index -> insertTabletOverride(index) }
}

// endregion

LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

// region Pre 19.15 patches.
// These are not required for 19.15+.

MiniplayerOverrideFingerprint.resultOrThrow().let {
val appNameStringIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2

it.mutableMethod.apply {
val walkerMethod = context.toMethodWalker(this)
.nextMethod(appNameStringIndex, true)
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
.getMethod() as MutableMethod

walkerMethod.apply {
findReturnIndexes().forEach { index -> insertTabletOverride(index) }
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

MiniplayerResponseModelSizeCheckFingerprint.resultOrThrow().let {
it.mutableMethod.insertTabletOverride(it.scanResult.patternScanResult!!.endIndex)
}

// endregion


// region Enable modern miniplayer.

MiniplayerModernConstructorFingerprint.resultOrThrow().mutableClass.methods.forEach {
it.apply {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
val iPutIndex = indexOfFirstInstructionOrThrow {
this.opcode == Opcode.IPUT && this.getReference<FieldReference>()?.type == "I"
}

insertModernOverrideInt(iPutIndex)
} else {
findReturnIndexes().forEach { index -> insertModernOverride(index) }
}
}
}

// endregion


// region Fix YT 19.15 and 19.16 using mixed up drawables for tablet modern.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

MiniplayerModernExpandCloseDrawablesFingerprint.let {
it.resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

it.resultOrThrow().mutableMethod.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
).forEach { (originalResource, replacementResource) ->
val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource)
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA

replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
}
}
}

// endregion


// region Hide tablet modern miniplayer buttons.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

MiniplayerModernExpandButtonFingerprint.addModernMiniplayerImageViewHook(
context,
"hideMiniplayerExpandClose"
)

MiniplayerModernCloseButtonFingerprint.addModernMiniplayerImageViewHook(
context,
"hideMiniplayerExpandClose"
)

MiniplayerModernRewindButtonFingerprint.addModernMiniplayerImageViewHook(
context,
"hideMiniplayerRewindForward"
)

MiniplayerModernForwardButtonFingerprint.addModernMiniplayerImageViewHook(
context,
"hideMiniplayerRewindForward"
)

MiniplayerModernOverlayViewFingerprint.addModernMiniplayerImageViewHook(
context,
"adjustMiniplayerOpacity"
)

MiniplayerModernAddViewListenerFingerprint.let {
it.resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)

it.resultOrThrow().mutableMethod.apply {
addInstruction(
0,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
"hideMiniplayerSubTexts(Landroid/view/View;)V"
)
}
}

// endregion
}

private fun Method.findReturnIndexes(): List<Int> {
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
val indexes = implementation!!.instructions
.withIndex()
.filter { (_, instruction) -> instruction.opcode == Opcode.RETURN }
.map { (index, _) -> index }
.reversed()
if (indexes.isEmpty()) throw PatchException("No return instructions found in: $this")

return indexes
}

private fun MutableMethod.insertTabletOverride(index: Int) {
insertModernTabletOverride(index, "getTabletOverride")
}

private fun MutableMethod.insertModernOverride(index: Int) {
insertModernTabletOverride(index, "getModernOverride")
}

private fun MutableMethod.insertModernTabletOverride(index: Int, methodName: String) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
this.addInstructions(
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved
index,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register
"""
)
}

private fun MutableMethod.insertModernOverrideInt(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
val targetReference = (targetInstruction as ReferenceInstruction).reference

addInstructions(
iPutIndex + 1, """
invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernOverrideType(I)I
move-result v${targetInstruction.registerA}
# Original instruction
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
"""
)
removeInstruction(iPutIndex)
}

private fun LiteralValueFingerprint.addModernMiniplayerImageViewHook(
context: BytecodeContext,
integrationsMethodName: String
) {
resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)

resultOrThrow().mutableMethod.apply {
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstWideLiteralInstructionValueOrThrow(literalSupplier.invoke())
) {
opcode == Opcode.CHECK_CAST
}

val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->$integrationsMethodName(Landroid/widget/ImageView;)V"
)
}
}
}
Loading
Loading