diff --git a/extensions/instagram/src/main/java/app/revanced/extension/instagram/hide/navigation/HideNavigationButtonsPatch.java b/extensions/instagram/src/main/java/app/revanced/extension/instagram/hide/navigation/HideNavigationButtonsPatch.java new file mode 100644 index 000000000..1dd99e204 --- /dev/null +++ b/extensions/instagram/src/main/java/app/revanced/extension/instagram/hide/navigation/HideNavigationButtonsPatch.java @@ -0,0 +1,33 @@ +package app.revanced.extension.instagram.hide.navigation; + +import java.lang.reflect.Field; +import java.util.List; + +@SuppressWarnings("unused") +public class HideNavigationButtonsPatch { + + /** + * Injection point. + * @param navigationButtonsList the list of navigation buttons, as an (obfuscated) Enum type + * @param buttonNameToRemove the name of the button we want to remove + * @param enumNameField the field in the nav button enum class which contains the name of the button + * @return the patched list of navigation buttons + */ + public static List removeNavigationButtonByName( + List navigationButtonsList, + String buttonNameToRemove, + String enumNameField + ) + throws IllegalAccessException, NoSuchFieldException { + for (Object button : navigationButtonsList) { + Field f = button.getClass().getDeclaredField(enumNameField); + String currentButtonEnumName = (String) f.get(button); + + if (buttonNameToRemove.equals(currentButtonEnumName)) { + navigationButtonsList.remove(button); + break; + } + } + return navigationButtonsList; + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt index 07933f70f..efbdaf597 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt @@ -2,28 +2,22 @@ package app.revanced.patches.instagram.hide.navigation import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.patch.BytecodePatchContext -internal val tabCreateButtonsLoopStartFingerprint = fingerprint { - returns("V") - strings("InstagramMainActivity.createTabButtons") - opcodes( - //Loop Start - Opcode.IF_GE, // Check if index is finished (index, size) - //Injection - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT - ) +internal val initializeNavigationButtonsListFingerprint = fingerprint { + strings("Nav3") + parameters("Lcom/instagram/common/session/UserSession;", "Z") + returns("Ljava/util/List;") } -internal val tabCreateButtonsLoopEndFingerprint = fingerprint { - returns("V") - strings("InstagramMainActivity.createTabButtons") - opcodes( - Opcode.IPUT_OBJECT, - // Injection Jump - Opcode.ADD_INT_LIT8, //Increase Index - Opcode.GOTO // Jump to loopStart - // LoopEnd - ) +private val navigationButtonsEnumClassDef = fingerprint { + strings("FEED", "fragment_feed", "SEARCH", "fragment_search") +} + +context(BytecodePatchContext) +internal val navigationButtonsEnumInitFingerprint get() = fingerprint { + custom { method, classDef -> + method.name == "" + && classDef == navigationButtonsEnumClassDef.classDef + } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt index cac9c35c3..4ada34d27 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt @@ -1,21 +1,28 @@ package app.revanced.patches.instagram.hide.navigation -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference import java.util.logging.Logger +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/instagram/hide/navigation/HideNavigationButtonsPatch;" + @Suppress("unused") val hideNavigationButtonsPatch = bytecodePatch( name = "Hide navigation buttons", description = "Hides navigation bar buttons, such as the Reels and Create button.", use = false ) { - compatibleWith("com.instagram.android"("397.1.0.52.81")) + compatibleWith("com.instagram.android") val hideReels by booleanOption( key = "hideReels", @@ -38,43 +45,44 @@ val hideNavigationButtonsPatch = bytecodePatch( ) } - tabCreateButtonsLoopStartFingerprint.method.apply { - // Check the current loop index, and skip over adding the - // navigation button view if the index matches a given button. + val enumNameField: String - val startIndex = tabCreateButtonsLoopStartFingerprint.patternMatch!!.startIndex - val endIndex = tabCreateButtonsLoopEndFingerprint.patternMatch!!.endIndex - val insertIndex = startIndex + 1 - val loopIndexRegister = getInstruction(startIndex).registerA - val freeRegister = findFreeRegister(insertIndex, loopIndexRegister) - val instruction = getInstruction(endIndex - 1) - - val instructions = buildString { - if (hideCreate!!) { - appendLine( - """ - const v$freeRegister, 0x2 - if-eq v$freeRegister, v$loopIndexRegister, :skipAddView - """ - ) - } - - if (hideReels!!) { - appendLine( - """ - const v$freeRegister, 0x3 - if-eq v$freeRegister, v$loopIndexRegister, :skipAddView - """ - ) - } - } - - addInstructionsWithLabels( - insertIndex, - instructions, - ExternalLabel("skipAddView", instruction) - ) + // Get the field name which contains the name of the enum for the navigation button ("fragment_clips", "fragment_share", ...) + with(navigationButtonsEnumInitFingerprint.method) { + enumNameField = indexOfFirstInstructionOrThrow { + opcode == Opcode.IPUT_OBJECT && + (this as TwoRegisterInstruction).registerA == 2 // The p2 register + }.let { + getInstruction(it).getReference()!!.name } } - } + initializeNavigationButtonsListFingerprint.method.apply { + val returnIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) + val buttonsListRegister = getInstruction(returnIndex).registerA + val freeRegister = findFreeRegister(returnIndex) + val freeRegister2 = findFreeRegister(returnIndex, freeRegister) + + fun instructionsRemoveButtonByName(buttonEnumName: String): String { + return """ + const-string v$freeRegister, "$buttonEnumName" + const-string v$freeRegister2, "$enumNameField" + invoke-static { v$buttonsListRegister, v$freeRegister, v$freeRegister2 }, $EXTENSION_CLASS_DESCRIPTOR->removeNavigationButtonByName(Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; + move-result-object v$buttonsListRegister + """ + } + + if (hideReels!!) + addInstructionsAtControlFlowLabel( + returnIndex, + instructionsRemoveButtonByName("fragment_clips") + ) + + if (hideCreate!!) + addInstructionsAtControlFlowLabel( + returnIndex, + instructionsRemoveButtonByName("fragment_share") + ) + } + } +}