feat(kakaotalk): add layout handling for deleted and hidden messages in chat log

This is the fourth commit to make deleted, hidden messages visible.
This commit is contained in:
2025-05-28 01:29:08 +09:00
parent 1e91a5cb52
commit 11bf82640d

View File

@ -3,10 +3,15 @@ package app.revanced.patches.kakaotalk.chatlog
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel 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.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.ImmutableField
import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
@ -23,6 +28,9 @@ val addDeletedOrHiddenLayoutPatch = bytecodePatch(
val makeLayoutMethod = chatInfoViewClass.methods.firstOrNull { val makeLayoutMethod = chatInfoViewClass.methods.firstOrNull {
it.name == "makeLayout" it.name == "makeLayout"
} ?: error("makeLayout method not found in ChatInfoView class") } ?: 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("ChatInfoView class: $chatInfoViewClass")
println("makeLayout method: $makeLayoutMethod") println("makeLayout method: $makeLayoutMethod")
@ -172,48 +180,160 @@ val addDeletedOrHiddenLayoutPatch = bytecodePatch(
).toMutable() ).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;-><init>(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;-><init>(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, 0,
""" deletedMakeLayoutBlock + "\n\n" + hiddenMakeLayoutBlock,
iget-boolean v0, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->isDeleted:Z )
if-eqz v0, :not_deleted
const-string v0, "[Deleted]" val superIndex = onDrawMethod.instructions.indexOfFirst {
iget-object v1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; it.opcode == Opcode.INVOKE_SUPER
invoke-static {v0, v1}, Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;)Landroid/text/BoringLayout${'$'}Metrics; }
move-result-object v8
val deletedOnDrawBlock = """
if-nez v8, :boring_deleted iget-object v0, p0, $cls->deletedLayout:Landroid/text/Layout;
new-instance v1, Landroid/text/StaticLayout; if-eqz v0, :skip_deleted_draw
iget-object v4, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; iget-boolean v0, p0, $cls->isDeleted:Z
invoke-virtual { v4, v0 }, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F if-eqz v0, :skip_deleted_draw
move-result v2 invoke-virtual {p1}, Landroid/graphics/Canvas;->save()I
float-to-int v5, v2 iget-object v0, p0, $cls->deletedRect:Landroid/graphics/Rect;
sget-object v6, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; invoke-virtual {p1, v0}, Landroid/graphics/Canvas;->clipRect(Landroid/graphics/Rect;)Z
const/4 v9, 0x1 iget-object v0, p0, $cls->deletedRect:Landroid/graphics/Rect;
const/high16 v7, 0x3f800000 # 1.0f iget v0, v0, Landroid/graphics/Rect;->left:I
const/4 v8, 0x0 int-to-float v0, v0
move-object v2, v1 iget-object v1, p0, $cls->deletedRect:Landroid/graphics/Rect;
move-object v3, v0 iget v1, v1, Landroid/graphics/Rect;->top:I
invoke-direct/range {v2 .. v9}, Landroid/text/StaticLayout;-><init>(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout${'$'}Alignment;FFZ)V int-to-float v1, v1
goto :set_deleted_layout invoke-virtual {p1, v0, v1}, Landroid/graphics/Canvas;->translate(FF)V
:boring_deleted iget-object v0, p0, $cls->deletedLayout:Landroid/text/Layout;
iget-object v3, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint; .line 1
invoke-virtual {v3, v0}, Landroid/graphics/Paint;->measureText(Ljava/lang/String;)F if-eqz v0, :fuck
move-result v1 .line 2
float-to-int v4, v1 invoke-virtual {v0, p1}, Landroid/text/Layout;->draw(Landroid/graphics/Canvas;)V
sget-object v5, Landroid/text/Layout${'$'}Alignment;->ALIGN_NORMAL:Landroid/text/Layout${'$'}Alignment; .line 3
const/4 v9, 0x0 :fuck
const/high16 v6, 0x3f800000 # 1.0f invoke-virtual {p1}, Landroid/graphics/Canvas;->restore()V
const/4 v7, 0x0 """.trimIndent()
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; val hiddenOnDrawBlock = """
move-result-object v1 iget-object v0, p0, $cls->hiddenLayout:Landroid/text/Layout;
:set_deleted_layout if-eqz v0, :skip_hidden_draw
iput-object v1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedLayout:Landroid/text/Layout; iget-boolean v0, p0, $cls->isHidden:Z
""".trimIndent(), if-eqz v0, :skip_hidden_draw
ExternalLabel("not_deleted", makeLayoutMethod.getInstruction(0)) 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
) )
} }
} }