diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java index cccf47d41..f1dd20b99 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java @@ -197,6 +197,8 @@ public class GeneralPatch { // region [Hide navigation bar components] patch + private static final int fillBellCairoBlack = ResourceUtils.getDrawableIdentifier("yt_fill_bell_cairo_black_24"); + private static final Map shouldHideMap = new EnumMap<>(NavigationButton.class) { { put(NavigationButton.HOME, Settings.HIDE_NAVIGATION_HOME_BUTTON.get()); @@ -216,6 +218,18 @@ public class GeneralPatch { return Settings.ENABLE_TRANSLUCENT_NAVIGATION_BAR.get(); } + /** + * @noinspection ALL + */ + public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) { + if (fillBellCairoBlack != 0) { + // It's very unlikely, but Google might fix this issue someday. + // If so, [fillBellCairoBlack] might already be in enumMap. + // That's why 'EnumMap.putIfAbsent()' is used instead of 'EnumMap.put()'. + enumMap.putIfAbsent(tabActivityCairo, Integer.valueOf(fillBellCairoBlack)); + } + } + public static boolean switchCreateWithNotificationButton(boolean original) { return Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get() || original; } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/Fingerprints.kt index c1fbb878f..adc587729 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/Fingerprints.kt @@ -1,10 +1,14 @@ package app.revanced.patches.youtube.general.navigation +import app.revanced.patches.youtube.utils.resourceid.ytFillBell 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 const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive" +internal const val TAB_ACTIVITY_CAIRO_STRING = "TAB_ACTIVITY_CAIRO" + internal val autoMotiveFingerprint = legacyFingerprint( name = "autoMotiveFingerprint", opcodes = listOf( @@ -13,7 +17,13 @@ internal val autoMotiveFingerprint = legacyFingerprint( Opcode.MOVE_RESULT, Opcode.IF_EQZ ), - strings = listOf("Android Automotive") + strings = listOf(ANDROID_AUTOMOTIVE_STRING) +) + +internal val imageEnumConstructorFingerprint = legacyFingerprint( + name = "imageEnumConstructorFingerprint", + returnType = "V", + strings = listOf(TAB_ACTIVITY_CAIRO_STRING) ) internal val pivotBarChangedFingerprint = legacyFingerprint( @@ -59,6 +69,11 @@ internal val pivotBarStyleFingerprint = legacyFingerprint( } ) +internal val setEnumMapFingerprint = legacyFingerprint( + name = "setEnumMapFingerprint", + literals = listOf(ytFillBell), +) + internal val translucentNavigationBarFingerprint = legacyFingerprint( name = "translucentNavigationBarFingerprint", literals = listOf(45630927L), diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/NavigationBarComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/NavigationBarComponentsPatch.kt index 8a41f9bd8..3b0e48a3e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/NavigationBarComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/navigation/NavigationBarComponentsPatch.kt @@ -4,6 +4,7 @@ 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.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch 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.navigation.addBottomBarContainerHook @@ -11,20 +12,54 @@ import app.revanced.patches.youtube.utils.navigation.hookNavigationButtonCreated import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch import app.revanced.patches.youtube.utils.patch.PatchList.NAVIGATION_BAR_COMPONENTS import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater +import app.revanced.patches.youtube.utils.playservice.is_19_28_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch +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 +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall 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.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 +private val navigationBarComponentsResourcePatch = resourcePatch( + description = "navigationBarComponentsResourcePatch" +) { + dependsOn(versionCheckPatch) + + execute { + if (is_19_28_or_greater) { + // Since I couldn't get the Cairo notification filled icon anywhere, + // I just made it as close as possible. + arrayOf( + "xxxhdpi", + "xxhdpi", + "xhdpi", + "hdpi", + "mdpi" + ).forEach { dpi -> + copyResources( + "youtube/navigationbuttons", + ResourceGroup( + "drawable-$dpi", + "yt_fill_bell_cairo_black_24.png" + ) + ) + } + } + } +} + @Suppress("unused") val navigationBarComponentsPatch = bytecodePatch( NAVIGATION_BAR_COMPONENTS.title, @@ -33,7 +68,9 @@ val navigationBarComponentsPatch = bytecodePatch( compatibleWith(COMPATIBLE_PACKAGE) dependsOn( + navigationBarComponentsResourcePatch, settingsPatch, + sharedResourceIdPatch, navigationBarHookPatch, versionCheckPatch, ) @@ -90,7 +127,7 @@ val navigationBarComponentsPatch = bytecodePatch( // region patch for hide navigation buttons autoMotiveFingerprint.methodOrThrow().apply { - val insertIndex = indexOfFirstStringInstructionOrThrow("Android Automotive") - 1 + val insertIndex = indexOfFirstStringInstructionOrThrow(ANDROID_AUTOMOTIVE_STRING) - 1 val insertRegister = getInstruction(insertIndex).registerA addInstructions( @@ -122,6 +159,51 @@ val navigationBarComponentsPatch = bytecodePatch( // endregion + // region fix for cairo notification icon + + /** + * The Cairo navigation bar was widely rolled out in YouTube 19.28.42. + * + * Unlike Home, Shorts, and Subscriptions, which have Cairo icons, + * Notifications does not have a Cairo icon. + * + * This led to an issue revanced-patches#4046, + * Which was closed as not planned because it was a YouTube issue and not a ReVanced issue. + * + * It was not too hard to fix, so it was implemented as a patch. + */ + if (is_19_28_or_greater) { + val cairoNotificationEnumReference = with (imageEnumConstructorFingerprint.methodOrThrow()) { + val stringIndex = indexOfFirstStringInstructionOrThrow(TAB_ACTIVITY_CAIRO_STRING) + val cairoNotificationEnumIndex = indexOfFirstInstructionOrThrow(stringIndex) { + opcode == Opcode.SPUT_OBJECT + } + getInstruction(cairoNotificationEnumIndex).reference + } + + setEnumMapFingerprint.methodOrThrow().apply { + val enumMapIndex = indexOfFirstInstructionReversedOrThrow { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.definingClass == "Ljava/util/EnumMap;" && + reference.name == "put" && + reference.parameterTypes.firstOrNull() == "Ljava/lang/Enum;" + } + val (enumMapRegister, enumRegister) = getInstruction(enumMapIndex).let { + Pair(it.registerC, it.registerD) + } + + addInstructions( + enumMapIndex + 1, """ + sget-object v$enumRegister, $cairoNotificationEnumReference + invoke-static {v$enumMapRegister, v$enumRegister}, $GENERAL_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V + """ + ) + } + } + + // endregion + // Hook navigation button created, in order to hide them. hookNavigationButtonCreated(GENERAL_CLASS_DESCRIPTOR) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt index 17b8bba16..31bb20af6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt @@ -216,6 +216,8 @@ var youTubeControlsOverlaySubtitleButton = -1L private set var youTubeLogo = -1L private set +var ytFillBell = -1L + private set var ytOutlinePictureInPictureWhite = -1L private set var ytOutlineVideoCamera = -1L @@ -638,6 +640,10 @@ internal val sharedResourceIdPatch = resourcePatch( ID, "youtube_logo" ] + ytFillBell = resourceMappings[ + DRAWABLE, + "yt_fill_bell_black_24" + ] ytOutlinePictureInPictureWhite = resourceMappings[ DRAWABLE, "yt_outline_picture_in_picture_white_24" diff --git a/patches/src/main/resources/youtube/navigationbuttons/drawable-hdpi/yt_fill_bell_cairo_black_24.png b/patches/src/main/resources/youtube/navigationbuttons/drawable-hdpi/yt_fill_bell_cairo_black_24.png new file mode 100644 index 000000000..bec5bd78b Binary files /dev/null and b/patches/src/main/resources/youtube/navigationbuttons/drawable-hdpi/yt_fill_bell_cairo_black_24.png differ diff --git a/patches/src/main/resources/youtube/navigationbuttons/drawable-mdpi/yt_fill_bell_cairo_black_24.png b/patches/src/main/resources/youtube/navigationbuttons/drawable-mdpi/yt_fill_bell_cairo_black_24.png new file mode 100644 index 000000000..415a6ccca Binary files /dev/null and b/patches/src/main/resources/youtube/navigationbuttons/drawable-mdpi/yt_fill_bell_cairo_black_24.png differ diff --git a/patches/src/main/resources/youtube/navigationbuttons/drawable-xhdpi/yt_fill_bell_cairo_black_24.png b/patches/src/main/resources/youtube/navigationbuttons/drawable-xhdpi/yt_fill_bell_cairo_black_24.png new file mode 100644 index 000000000..4c837aabc Binary files /dev/null and b/patches/src/main/resources/youtube/navigationbuttons/drawable-xhdpi/yt_fill_bell_cairo_black_24.png differ diff --git a/patches/src/main/resources/youtube/navigationbuttons/drawable-xxhdpi/yt_fill_bell_cairo_black_24.png b/patches/src/main/resources/youtube/navigationbuttons/drawable-xxhdpi/yt_fill_bell_cairo_black_24.png new file mode 100644 index 000000000..4ee5124c2 Binary files /dev/null and b/patches/src/main/resources/youtube/navigationbuttons/drawable-xxhdpi/yt_fill_bell_cairo_black_24.png differ diff --git a/patches/src/main/resources/youtube/navigationbuttons/drawable-xxxhdpi/yt_fill_bell_cairo_black_24.png b/patches/src/main/resources/youtube/navigationbuttons/drawable-xxxhdpi/yt_fill_bell_cairo_black_24.png new file mode 100644 index 000000000..b31f6adc9 Binary files /dev/null and b/patches/src/main/resources/youtube/navigationbuttons/drawable-xxxhdpi/yt_fill_bell_cairo_black_24.png differ