feat(kakaotalk): update compatibility to version 25.4.3 and add show deleted messages patch
This commit is contained in:
@ -272,20 +272,12 @@ public final class app/revanced/patches/kakaotalk/changemodel/ChangeModelPatchKt
|
||||
public static final fun getChangeModelPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/kakaotalk/chatlog/AddDeletedOrHiddenLayoutPatchKt {
|
||||
public static final fun getAddDeletedOrHiddenLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/kakaotalk/chatlog/AddRealDeletedOrHiddenFlagPatchKt {
|
||||
public static final fun getAddRealDeletedOrHiddenFlagPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/kakaotalk/chatlog/Remove99ClampPatchKt {
|
||||
public static final fun getRemove99ClampPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/kakaotalk/chatlog/ShowDeletedOrHiddenMessagePatchKt {
|
||||
public static final fun getShowDeletedOrHiddenMessagePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public final class app/revanced/patches/kakaotalk/chatlog/ShowDeletedMessagePatchKt {
|
||||
public static final fun getShowDeletedMessagePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/kakaotalk/chatroom/Remove300PlusLimitPatchKt {
|
||||
|
@ -9,7 +9,7 @@ val removeBizBoardPatch = bytecodePatch(
|
||||
name = "Remove BizBoard ads",
|
||||
description = "Removes the BizBoard ad by forcing its dimensions to 0x0 and visibility to GONE in onMeasure.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = measuringBizBoardFingerprint.method
|
||||
|
@ -8,7 +8,7 @@ val removeFocusAdPatch = bytecodePatch(
|
||||
name = "Remove focus ad",
|
||||
description = "Removes the focus ad from the app.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
loadFocusAdFingerprint.method.addInstructions(
|
||||
|
@ -14,7 +14,7 @@ val removeMoreTabAdPatch = bytecodePatch(
|
||||
name = "Remove More tab ad",
|
||||
description = "Removes the ad from the More tab.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val addSectionToMoreTabUIMethod = addSectionToMoreTabUIFingerprint.method
|
||||
|
@ -8,7 +8,7 @@ val removeNativeAdPatch = bytecodePatch(
|
||||
name = "Remove native ad",
|
||||
description = "Removes the native ad from the app.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = loadNativeAdFingerprint.method
|
||||
|
@ -10,7 +10,7 @@ val removeOlkChatRoomListAdPatch = bytecodePatch(
|
||||
name = "Remove OpenLink chat room list ad",
|
||||
description = "Removes the OpenLink chat room list ad.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val findUnit = kotlinUnitInstanceFingerprint.method
|
||||
|
@ -14,7 +14,7 @@ val changeModelPatch = bytecodePatch(
|
||||
"model", "SM-X926N"
|
||||
)
|
||||
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
changeModelFingerprint.method.addInstructions(
|
||||
|
@ -1,504 +0,0 @@
|
||||
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.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.chatInfoViewConstructorFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
|
||||
import com.google.common.collect.ImmutableList
|
||||
|
||||
val addDeletedOrHiddenLayoutPatch = bytecodePatch(
|
||||
name = "Add deleted or hidden layout",
|
||||
description = "Adds a layout for deleted or hidden messages in chat logs",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
|
||||
execute {
|
||||
val chatInfoViewClass = proxy(classes.first {
|
||||
it.type == "Lcom/kakao/talk/widget/chatlog/ChatInfoView;"
|
||||
}).mutableClass
|
||||
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")
|
||||
val constructor = chatInfoViewConstructorFingerprint.method
|
||||
|
||||
println("ChatInfoView class: $chatInfoViewClass")
|
||||
println("makeLayout method: $makeLayoutMethod")
|
||||
println("onDraw method: $onDrawMethod")
|
||||
println("constructor: $constructor")
|
||||
|
||||
chatInfoViewClass.apply {
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"isDeleted",
|
||||
"Z",
|
||||
AccessFlags.PUBLIC.value,
|
||||
ImmutableBooleanEncodedValue.FALSE_VALUE,
|
||||
null,
|
||||
null
|
||||
).toMutable(),
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"deletedLayout",
|
||||
"Landroid/text/Layout;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"deletedPaint",
|
||||
"Landroid/text/TextPaint;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"deletedRect",
|
||||
"Landroid/graphics/Rect;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"deletedTextColor",
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"deletedTextSize",
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"isHidden",
|
||||
"Z",
|
||||
AccessFlags.PUBLIC.value,
|
||||
ImmutableBooleanEncodedValue.FALSE_VALUE,
|
||||
null,
|
||||
null
|
||||
).toMutable(),
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"hiddenLayout",
|
||||
"Landroid/text/Layout;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"hiddenPaint",
|
||||
"Landroid/text/TextPaint;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"hiddenRect",
|
||||
"Landroid/graphics/Rect;",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"hiddenTextColor",
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"hiddenTextSize",
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"setDeleted",
|
||||
ImmutableList.of(
|
||||
ImmutableMethodParameter(
|
||||
"Z",
|
||||
null,
|
||||
null
|
||||
)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iput-boolean p1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->isDeleted:Z
|
||||
invoke-virtual {p0}, Landroid/view/View;->requestLayout()V
|
||||
invoke-virtual {p0}, Landroid/view/View;->invalidate()V
|
||||
return-void
|
||||
""".trimIndent()
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"setHidden",
|
||||
ImmutableList.of(
|
||||
ImmutableMethodParameter(
|
||||
"Z",
|
||||
null,
|
||||
null
|
||||
)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iput-boolean p1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->isHidden:Z
|
||||
invoke-virtual {p0}, Landroid/view/View;->requestLayout()V
|
||||
invoke-virtual {p0}, Landroid/view/View;->invalidate()V
|
||||
return-void
|
||||
""".trimIndent()
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"setDeletedTextColor",
|
||||
ImmutableList.of(
|
||||
ImmutableMethodParameter(
|
||||
"I",
|
||||
null,
|
||||
null
|
||||
)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iput p1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedTextColor:I
|
||||
iget-object v0, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->deletedPaint:Landroid/text/TextPaint;
|
||||
if-nez v0, :cond_0
|
||||
goto :goto_0
|
||||
:cond_0
|
||||
invoke-virtual {v0, p1}, Landroid/graphics/Paint;->setColor(I)V
|
||||
:goto_0
|
||||
return-void
|
||||
""".trimIndent()
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"setHiddenTextColor",
|
||||
ImmutableList.of(
|
||||
ImmutableMethodParameter(
|
||||
"I",
|
||||
null,
|
||||
null
|
||||
)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iput p1, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->hiddenTextColor:I
|
||||
iget-object v0, p0, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->hiddenPaint:Landroid/text/TextPaint;
|
||||
if-nez v0, :cond_0
|
||||
goto :goto_0
|
||||
:cond_0
|
||||
invoke-virtual {v0, p1}, Landroid/graphics/Paint;->setColor(I)V
|
||||
:goto_0
|
||||
return-void
|
||||
""".trimIndent()
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
val cls = chatInfoViewClass.type
|
||||
|
||||
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,
|
||||
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
|
||||
)
|
||||
|
||||
val indices = constructor.instructions
|
||||
.withIndex()
|
||||
.filter { it.value.opcode == Opcode.MOVE_RESULT_OBJECT }
|
||||
.map { it.index }
|
||||
|
||||
val secondIndex = indices.getOrNull(1) ?: error("Second MOVE_RESULT_OBJECT instruction not found in constructor")
|
||||
|
||||
constructor.addInstructions(
|
||||
secondIndex + 1,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
|
||||
new-instance p3, Landroid/text/TextPaint;
|
||||
invoke-direct {p3, v0}, Landroid/text/TextPaint;-><init>(I)V
|
||||
iget v1, p0, $cls->deletedTextSize:I
|
||||
int-to-float v1, v1
|
||||
invoke-virtual {p3, v1}, Landroid/graphics/Paint;->setTextSize(F)V
|
||||
iget v1, p0, $cls->deletedTextColor:I
|
||||
invoke-virtual {p3, v1}, Landroid/graphics/Paint;->setColor(I)V
|
||||
invoke-virtual {p3, p2}, Landroid/graphics/Paint;->setTypeface(Landroid/graphics/Typeface;)Landroid/graphics/Typeface;
|
||||
iput-object p3, p0, $cls->deletedPaint:Landroid/text/TextPaint;
|
||||
|
||||
new-instance p3, Landroid/text/TextPaint;
|
||||
invoke-direct {p3, v0}, Landroid/text/TextPaint;-><init>(I)V
|
||||
iget v1, p0, $cls->hiddenTextSize:I
|
||||
int-to-float v1, v1
|
||||
invoke-virtual {p3, v1}, Landroid/graphics/Paint;->setTextSize(F)V
|
||||
iget v1, p0, $cls->hiddenTextColor:I
|
||||
invoke-virtual {p3, v1}, Landroid/graphics/Paint;->setColor(I)V
|
||||
invoke-virtual {p3, p2}, Landroid/graphics/Paint;->setTypeface(Landroid/graphics/Typeface;)Landroid/graphics/Typeface;
|
||||
iput-object p3, p0, $cls->hiddenPaint:Landroid/text/TextPaint;
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package app.revanced.patches.kakaotalk.chatlog
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.chatLogFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
|
||||
|
||||
val addRealDeletedOrHiddenFlagPatch = bytecodePatch(
|
||||
name = "Add real deleted or hidden flag",
|
||||
description = "Add real deleted or hidden flag to chat log",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
|
||||
execute {
|
||||
val chatLogClass = proxy(classes.first {
|
||||
it.type == chatLogFingerprint.method.definingClass
|
||||
}).mutableClass
|
||||
|
||||
chatLogClass.apply {
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"isRealDeleted",
|
||||
"Z",
|
||||
AccessFlags.PUBLIC.value,
|
||||
ImmutableBooleanEncodedValue.FALSE_VALUE,
|
||||
null,
|
||||
null
|
||||
).toMutable(),
|
||||
)
|
||||
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"isHidden",
|
||||
"Z",
|
||||
AccessFlags.PUBLIC.value,
|
||||
ImmutableBooleanEncodedValue.FALSE_VALUE,
|
||||
null,
|
||||
null
|
||||
).toMutable(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ val remove99ClampPatch = bytecodePatch(
|
||||
name = "Disable 99 unread limit",
|
||||
description = "Skip the 99-cap so unread count shows full value"
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = processWatermarkCountFingerprint.method
|
||||
|
@ -0,0 +1,64 @@
|
||||
package app.revanced.patches.kakaotalk.chatlog
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.chatLogGetTextFingerprint
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.chatLogSetTextFingerprint
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.replaceToFeedFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
val showDeletedMessagePatch = bytecodePatch(
|
||||
name = "Show deleted messages",
|
||||
description = "Allows you to see deleted messages in chat logs.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = replaceToFeedFingerprint.method
|
||||
val insns = method.instructions
|
||||
|
||||
val orInsnsIdx = insns
|
||||
.indexOfFirst { it.opcode == Opcode.OR_INT_LIT16 }
|
||||
|
||||
if (orInsnsIdx == -1) error("could not find or built message $orInsnsIdx")
|
||||
|
||||
method.addInstructions(
|
||||
orInsnsIdx + 1,
|
||||
"""
|
||||
const/16 p1, 0x1
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val setTextMethod = chatLogSetTextFingerprint.method.name
|
||||
val getTextMethod = chatLogGetTextFingerprint.method.name
|
||||
val chatLogClass = chatLogSetTextFingerprint.method.definingClass
|
||||
|
||||
method.replaceInstruction(
|
||||
orInsnsIdx + 2,
|
||||
"nop"
|
||||
)
|
||||
method.addInstructions(
|
||||
orInsnsIdx + 3,
|
||||
"""
|
||||
invoke-virtual {p0}, $chatLogClass->$getTextMethod()Ljava/lang/String;
|
||||
move-result-object v2
|
||||
|
||||
const-string v1, "[Deleted] "
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V
|
||||
invoke-virtual {v3, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual {p0, v1}, $chatLogClass->$setTextMethod(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {p0}, $chatLogClass->getChatRoomId()J
|
||||
move-result-wide v2
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
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.instructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.chatLogFingerprint
|
||||
import app.revanced.patches.kakaotalk.chatlog.fingerprints.createChatLogViewHolderFromRealFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
// TODO: 레지스터 관련 패치해야함, 자동으로 찾게
|
||||
val showDeletedOrHiddenMessagePatch = bytecodePatch(
|
||||
name = "Show deleted or hidden message",
|
||||
description = "Shows deleted & hidden messages in chat logs",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
|
||||
dependsOn(addRealDeletedOrHiddenFlagPatch)
|
||||
|
||||
execute {
|
||||
val method = createChatLogViewHolderFromRealFingerprint.method
|
||||
val insns = method.instructions
|
||||
|
||||
val chatLogClass = chatLogFingerprint.classDef
|
||||
|
||||
println("ChatLog class: ${chatLogClass.type}")
|
||||
|
||||
val dateInst = insns
|
||||
.filterIsInstance<BuilderInstruction35c>()
|
||||
.filter { it.opcode == Opcode.INVOKE_VIRTUAL }
|
||||
.find {
|
||||
it.getReference<MethodReference>()?.name == "setDate"
|
||||
}
|
||||
val dateIndex = insns.indexOf(dateInst ?: return@execute)
|
||||
|
||||
val chatLogRegister = insns
|
||||
.filterIsInstance<BuilderInstruction21c>()
|
||||
.filter { it.opcode == Opcode.CHECK_CAST }
|
||||
.find {
|
||||
it.reference == chatLogClass
|
||||
}?.registerA ?: error("ChatLog class ${chatLogClass.type} not found")
|
||||
|
||||
println(chatLogRegister)
|
||||
|
||||
println("Date index: $dateIndex")
|
||||
|
||||
method.addInstructionsWithLabels(
|
||||
dateIndex + 1,
|
||||
"""
|
||||
invoke-virtual/range {p0 .. p0}, LOe/f2;->u0()LKe/S;
|
||||
move-result-object v14
|
||||
instance-of v15, v14, ${chatLogClass.type}
|
||||
if-eqz v15, :is_not_chat_log
|
||||
check-cast v14, ${chatLogClass.type}
|
||||
goto :set_flags
|
||||
:is_not_chat_log
|
||||
const/4 v14, 0x0
|
||||
:set_flags
|
||||
if-eqz v14, :skip_deleted_check
|
||||
iget-boolean v15, v14, ${chatLogClass.type}->isRealDeleted:Z
|
||||
invoke-virtual {v4, v15}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setDeleted(Z)V
|
||||
goto :check_hidden
|
||||
:skip_deleted_check
|
||||
const/4 v15, 0x0
|
||||
invoke-virtual {v4, v15}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setDeleted(Z)V
|
||||
:check_hidden
|
||||
if-eqz v14, :skip_hidden_check
|
||||
iget-boolean v15, v14, ${chatLogClass.type}->isHidden:Z
|
||||
invoke-virtual {v4, v15}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setHidden(Z)V
|
||||
goto :end_patch
|
||||
:skip_hidden_check
|
||||
const/4 v15, 0x0
|
||||
invoke-virtual {v4, v15}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setHidden(Z)V
|
||||
:end_patch
|
||||
nop
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
val setModifyTextColorInst = insns
|
||||
.filterIsInstance<BuilderInstruction35c>()
|
||||
.filter { it.opcode == Opcode.INVOKE_VIRTUAL }
|
||||
.find {
|
||||
it.getReference<MethodReference>()?.name == "setModifyTextColor"
|
||||
} ?: error("setModifyTextColor method not found")
|
||||
val setModifyTextColorInstIndex = insns.indexOf(setModifyTextColorInst)
|
||||
|
||||
println("setModifyTextColorInstIndex: $setModifyTextColorInstIndex")
|
||||
|
||||
method.addInstructions(
|
||||
setModifyTextColorInstIndex + 1,
|
||||
"""
|
||||
invoke-virtual {v4, v6}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setDeletedTextColor(I)V
|
||||
invoke-virtual {v4, v6}, Lcom/kakao/talk/widget/chatlog/ChatInfoView;->setHiddenTextColor(I)V
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package app.revanced.patches.kakaotalk.chatlog.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val chatInfoViewConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
returns("V")
|
||||
parameters("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I")
|
||||
strings(
|
||||
"context"
|
||||
)
|
||||
custom { method, classDef -> classDef.type == "Lcom/kakao/talk/widget/chatlog/ChatInfoView;" && method.name == "<init>" }
|
||||
}
|
@ -2,6 +2,7 @@ package app.revanced.patches.kakaotalk.chatlog.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val chatLogFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
@ -11,4 +12,41 @@ internal val chatLogFingerprint = fingerprint {
|
||||
"[class:",
|
||||
"] ChatLog [id=",
|
||||
)
|
||||
}
|
||||
|
||||
internal val chatLogSetTextFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returns("V")
|
||||
parameters("Ljava/lang/String;")
|
||||
opcodes(
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_STRING,
|
||||
Opcode.CONST_STRING,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.CONST_16,
|
||||
Opcode.IF_LE,
|
||||
)
|
||||
custom { method, classDef -> classDef.sourceFile == "ChatLog.kt" }
|
||||
}
|
||||
|
||||
internal val chatLogGetTextFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters()
|
||||
opcodes(
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.CONST_STRING,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.GOTO,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.GOTO,
|
||||
Opcode.MOVE_OBJECT,
|
||||
)
|
||||
custom { method, classDef -> classDef.sourceFile == "ChatLog.kt" }
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package app.revanced.patches.kakaotalk.chatlog.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val createChatLogViewHolderFromRealFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters()
|
||||
opcodes(
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_16,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4,
|
||||
)
|
||||
custom { method, classDef -> classDef.sourceFile == "ChatLogViewHolder.kt" }
|
||||
}
|
@ -14,7 +14,7 @@ val remove300PlusLimitBaseChatRoomPatch = bytecodePatch(
|
||||
name = "Disable 300+ unread limit (BaseChatRoom)",
|
||||
description = "Always show the real unread count instead of '300+' in base chatroom list"
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = limit300PlusBaseChatRoomFingerprint.method
|
||||
@ -40,7 +40,7 @@ val remove300PlusLimitOpenChatRoomPatch = bytecodePatch(
|
||||
name = "Disable 300+ unread limit (OpenChatRoom)",
|
||||
description = "Always show the real unread count instead of '300+' in open chatroom list"
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = limit300PlusOpenChatRoomFingerprint.method
|
||||
|
@ -9,7 +9,7 @@ val forceEnableEmoticonPlusPatch = bytecodePatch(
|
||||
name = "Force enable emoticon plus feature",
|
||||
description = "Force enable emoticon plus feature (Unpurchased emoticon can be sent once per day)",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
isEnableEmoticonPlusFingerprint.method.addInstructions(
|
||||
|
@ -10,7 +10,7 @@ val ghostMode = bytecodePatch(
|
||||
name = "Ghost Mode",
|
||||
description = "Don't expose your typing status to the other party.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val findUnit = kotlinUnitInstanceFingerprint.method
|
||||
|
@ -21,7 +21,7 @@ val bypassMoatCheckPatch = bytecodePatch(
|
||||
name = "Bypass Moat check",
|
||||
description = "Bypass Moat check that prevents the app from running.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val patch: (Fingerprint) -> Unit = {
|
||||
|
@ -10,7 +10,7 @@ val bypassRequestChecksumPatch = bytecodePatch(
|
||||
name = "Bypass requestChecksums",
|
||||
description = "Prevents the execution of checksum verification logic by making it return early."
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val findUnit = kotlinUnitInstanceFingerprint.method
|
||||
|
@ -9,7 +9,7 @@ val verifyingSignaturePatch = bytecodePatch(
|
||||
name = "Disable verifying signature",
|
||||
description = "Disables the signature verification check that prevents the app from running.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
verifyingSignatureFingerprint.method.addInstructions(
|
||||
|
@ -11,7 +11,7 @@ val forceEnableDebugModePatch = bytecodePatch(
|
||||
name = "Force enable debug mode",
|
||||
description = "Enables debug mode in the app.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = configConstructorFingerprint.method
|
||||
|
@ -15,7 +15,7 @@ val removeShopTabPatch = bytecodePatch(
|
||||
name = "Remove shop tab",
|
||||
description = "Removes the shop tab from the bottom navigation bar.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val method = addNavigationTabFingerprint.method
|
||||
|
@ -15,7 +15,7 @@ val allowReplyToFeedPatch = app.revanced.patcher.patch.bytecodePatch(
|
||||
name = "Allow reply to feed",
|
||||
description = "Allows replying to feed messages",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val patch: (Fingerprint) -> Unit = { fp ->
|
||||
|
@ -17,7 +17,7 @@ val versionInfoPatch = bytecodePatch(
|
||||
name = "Version info patch",
|
||||
description = "Patches the version info to include '(ReVanced)' in the version string.",
|
||||
) {
|
||||
compatibleWith("com.kakao.talk"("25.4.2"))
|
||||
compatibleWith("com.kakao.talk"("25.4.3"))
|
||||
|
||||
execute {
|
||||
val runPatch: (Fingerprint, Boolean) -> Unit = { fp, inDetail ->
|
||||
|
Reference in New Issue
Block a user