diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/AddDeletedOrHiddenLayoutPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/AddDeletedOrHiddenLayoutPatch.kt index 09adaad8f..dacf9f578 100644 --- a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/AddDeletedOrHiddenLayoutPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/AddDeletedOrHiddenLayoutPatch.kt @@ -3,10 +3,15 @@ package app.revanced.patches.kakaotalk.chatlog 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.instructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.util.getReference import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c +import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue @@ -23,6 +28,9 @@ val addDeletedOrHiddenLayoutPatch = bytecodePatch( val makeLayoutMethod = chatInfoViewClass.methods.firstOrNull { it.name == "makeLayout" } ?: error("makeLayout method not found in ChatInfoView class") + val onDrawMethod = chatInfoViewClass.methods.firstOrNull { + it.name == "onDraw" + } ?: error("onDraw method not found in ChatInfoView class") println("ChatInfoView class: $chatInfoViewClass") println("makeLayout method: $makeLayoutMethod") @@ -172,48 +180,160 @@ val addDeletedOrHiddenLayoutPatch = bytecodePatch( ).toMutable() ) - // TODO: Add logic to initialize these fields in the makeLayout method + val cls = chatInfoViewClass.type - makeLayoutMethod.addInstructionsWithLabels( + val deletedMakeLayoutBlock = """ + iget-boolean v0, p0, $cls->isDeleted:Z + if-eqz v0, :skip_deleted + const-string v0, "[Deleted]" + iget-object v1, p0, $cls->deletedPaint:Landroid/text/TextPaint; + invoke-static {v0, v1}, Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;)Landroid/text/BoringLayout${'$'}Metrics; + move-result-object v8 + if-nez v8, :boring_deleted + new-instance v1, Landroid/text/StaticLayout; + iget-object v4, p0, $cls->deletedPaint:Landroid/text/TextPaint; + invoke-virtual {v4, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F + move-result v2 + float-to-int v5, v2 + sget-object v6, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; + const/4 v9, 0x1 + const/high16 v7, 0x3f800000 # 1.0f + const/4 v8, 0x0 + move-object v2, v1 + move-object v3, v0 + invoke-direct/range {v2 .. v9}, Landroid/text/StaticLayout;->(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFZ)V + goto :set_deleted + :boring_deleted + iget-object v3, p0, $cls->deletedPaint:Landroid/text/TextPaint; + invoke-virtual {v3, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F + move-result v1 + float-to-int v4, v1 + sget-object v5, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; + const/4 v9, 0x0 + const/high16 v6, 0x3f800000 # 1.0f + const/4 v7, 0x0 + move-object v2, v0 + invoke-static/range {v2 .. v9}, Landroid/text/BoringLayout;->make(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFLandroid/text/BoringLayout${'$'}Metrics;Z)Landroid/text/BoringLayout; + move-result-object v1 + :set_deleted + iput-object v1, p0, $cls->deletedLayout:Landroid/text/Layout; + :skip_deleted + """.trimIndent() + + val hiddenMakeLayoutBlock = """ + iget-boolean v0, p0, $cls->isHidden:Z + if-eqz v0, :skip_hidden + const-string v0, "[Hidden]" + iget-object v1, p0, $cls->hiddenPaint:Landroid/text/TextPaint; + invoke-static {v0, v1}, Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;)Landroid/text/BoringLayout${'$'}Metrics; + move-result-object v8 + if-nez v8, :boring_hidden + new-instance v1, Landroid/text/StaticLayout; + iget-object v4, p0, $cls->hiddenPaint:Landroid/text/TextPaint; + invoke-virtual {v4, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F + move-result v2 + float-to-int v5, v2 + sget-object v6, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; + const/4 v9, 0x1 + const/high16 v7, 0x3f800000 # 1.0f + const/4 v8, 0x0 + move-object v2, v1 + move-object v3, v0 + invoke-direct/range {v2 .. v9}, Landroid/text/StaticLayout;->(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFZ)V + goto :set_hidden + :boring_hidden + iget-object v3, p0, $cls->hiddenPaint:Landroid/text/TextPaint; + invoke-virtual {v3, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F + move-result v1 + float-to-int v4, v1 + sget-object v5, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; + const/4 v9, 0x0 + const/high16 v6, 0x3f800000 # 1.0f + const/4 v7, 0x0 + move-object v2, v0 + invoke-static/range {v2 .. v9}, Landroid/text/BoringLayout;->make(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFLandroid/text/BoringLayout${'$'}Metrics;Z)Landroid/text/BoringLayout; + move-result-object v1 + :set_hidden + iput-object v1, p0, $cls->hiddenLayout:Landroid/text/Layout; + :skip_hidden + """.trimIndent() + + makeLayoutMethod.addInstructions( 0, - """ - iget-boolean v0, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->isDeleted:Z - if-eqz v0, :not_deleted - const-string v0, "[Deleted]" - iget-object v1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; - invoke-static {v0, v1}, Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;)Landroid/text/BoringLayout${'$'}Metrics; - move-result-object v8 - - if-nez v8, :boring_deleted - new-instance v1, Landroid/text/StaticLayout; - iget-object v4, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; - invoke-virtual { v4, v0 }, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F - move-result v2 - float-to-int v5, v2 - sget-object v6, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; - const/4 v9, 0x1 - const/high16 v7, 0x3f800000 # 1.0f - const/4 v8, 0x0 - move-object v2, v1 - move-object v3, v0 - invoke-direct/range {v2 .. v9}, Landroid/text/StaticLayout;->(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFZ)V - goto :set_deleted_layout - :boring_deleted - iget-object v3, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; - invoke-virtual {v3, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F - move-result v1 - float-to-int v4, v1 - sget-object v5, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; - const/4 v9, 0x0 - const/high16 v6, 0x3f800000 # 1.0f - const/4 v7, 0x0 - move-object v2, v0 - invoke-static/range {v2 .. v9}, Landroid/text/BoringLayout;->make(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFLandroid/text/BoringLayout${'$'}Metrics;Z)Landroid/text/BoringLayout; - move-result-object v1 - :set_deleted_layout - iput-object v1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedLayout:Landroid/text/Layout; - """.trimIndent(), - ExternalLabel("not_deleted", makeLayoutMethod.getInstruction(0)) + deletedMakeLayoutBlock + "\n\n" + hiddenMakeLayoutBlock, + ) + + val superIndex = onDrawMethod.instructions.indexOfFirst { + it.opcode == Opcode.INVOKE_SUPER + } + + val deletedOnDrawBlock = """ + iget-object v0, p0, $cls->deletedLayout:Landroid/text/Layout; + if-eqz v0, :skip_deleted_draw + iget-boolean v0, p0, $cls->isDeleted:Z + if-eqz v0, :skip_deleted_draw + invoke-virtual {p1}, Landroid/graphics/Canvas;->save()I + iget-object v0, p0, $cls->deletedRect:Landroid/graphics/Rect; + invoke-virtual {p1, v0}, Landroid/graphics/Canvas;->clipRect(Landroid/graphics/Rect;)Z + iget-object v0, p0, $cls->deletedRect:Landroid/graphics/Rect; + iget v0, v0, Landroid/graphics/Rect;->left:I + int-to-float v0, v0 + iget-object v1, p0, $cls->deletedRect:Landroid/graphics/Rect; + iget v1, v1, Landroid/graphics/Rect;->top:I + int-to-float v1, v1 + invoke-virtual {p1, v0, v1}, Landroid/graphics/Canvas;->translate(FF)V + iget-object v0, p0, $cls->deletedLayout:Landroid/text/Layout; + .line 1 + if-eqz v0, :fuck + .line 2 + invoke-virtual {v0, p1}, Landroid/text/Layout;->draw(Landroid/graphics/Canvas;)V + .line 3 + :fuck + invoke-virtual {p1}, Landroid/graphics/Canvas;->restore()V + """.trimIndent() + + val hiddenOnDrawBlock = """ + iget-object v0, p0, $cls->hiddenLayout:Landroid/text/Layout; + if-eqz v0, :skip_hidden_draw + iget-boolean v0, p0, $cls->isHidden:Z + if-eqz v0, :skip_hidden_draw + invoke-virtual {p1}, Landroid/graphics/Canvas;->save()I + iget-object v0, p0, $cls->hiddenRect:Landroid/graphics/Rect; + invoke-virtual {p1, v0}, Landroid/graphics/Canvas;->clipRect(Landroid/graphics/Rect;)Z + iget-object v0, p0, $cls->hiddenRect:Landroid/graphics/Rect; + iget v0, v0, Landroid/graphics/Rect;->left:I + int-to-float v0, v0 + iget-object v1, p0, $cls->hiddenRect:Landroid/graphics/Rect; + iget v1, v1, Landroid/graphics/Rect;->top:I + int-to-float v1, v1 + invoke-virtual {p1, v0, v1}, Landroid/graphics/Canvas;->translate(FF)V + iget-object v0, p0, $cls->hiddenLayout:Landroid/text/Layout; + .line 4 + if-eqz v0, :nullCheck_2 + .line 5 + invoke-virtual {v0, p1}, Landroid/text/Layout;->draw(Landroid/graphics/Canvas;)V + .line 6 + :nullCheck_2 + invoke-virtual {p1}, Landroid/graphics/Canvas;->restore()V + """.trimIndent() + + val instructionFollowingBothBlocks = onDrawMethod.getInstruction(superIndex + 1) + + val skipHiddenLabel = ExternalLabel("skip_hidden_draw", instructionFollowingBothBlocks) + + onDrawMethod.addInstructionsWithLabels( + superIndex + 1, + hiddenOnDrawBlock, + skipHiddenLabel + ) + + val firstInstructionOfHiddenBlock = onDrawMethod.getInstruction(superIndex + 1) + val skipDeletedLabel = ExternalLabel("skip_deleted_draw", firstInstructionOfHiddenBlock) + + onDrawMethod.addInstructionsWithLabels( + superIndex + 1, + deletedOnDrawBlock, + skipDeletedLabel ) } }