fix(ComponentsV2): refactor, prevent viewraw crash, and add cv2 tag
This commit is contained in:
parent
e7dd212cd1
commit
0dc4bf4286
3 changed files with 141 additions and 75 deletions
|
|
@ -0,0 +1,137 @@
|
|||
package com.aliucord.coreplugins
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.aliucord.Constants
|
||||
import com.aliucord.Utils
|
||||
import com.aliucord.api.PatcherAPI
|
||||
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
||||
import com.aliucord.patcher.*
|
||||
import com.aliucord.utils.GsonUtils
|
||||
import com.aliucord.utils.GsonUtils.toJson
|
||||
import com.aliucord.utils.ReflectUtils
|
||||
import com.discord.api.botuikit.ComponentType
|
||||
import com.discord.api.botuikit.gson.ComponentRuntimeTypeAdapter
|
||||
import com.discord.api.botuikit.gson.ComponentTypeTypeAdapter
|
||||
import com.discord.api.message.attachment.MessageAttachment
|
||||
import com.discord.models.domain.Model
|
||||
import com.discord.models.message.Message
|
||||
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemMessage
|
||||
import com.google.gson.stream.JsonReader
|
||||
import java.io.File
|
||||
import b.a.b.a as TypeAdapterRegistrar
|
||||
import b.i.d.c as FieldNamingPolicy
|
||||
import b.i.d.e as GsonBuilder
|
||||
|
||||
fun ComponentsV2.compat(patcher: PatcherAPI) {
|
||||
// check for old cursed plugin, probably not needed anymore
|
||||
val oldFile = File("${Constants.PLUGINS_PATH}/ComponentsV2-Beta.zip")
|
||||
if (oldFile.exists()) {
|
||||
logger.info("old plugin found, deleting and prompting restart")
|
||||
oldFile.delete()
|
||||
Utils.promptRestart()
|
||||
return
|
||||
}
|
||||
|
||||
// I'm sorry
|
||||
// ViewRaw crashes without this
|
||||
val cuteGson = GsonBuilder().run {
|
||||
c = FieldNamingPolicy.m // LOWER_CASE_WITH_UNDERSCORES
|
||||
TypeAdapterRegistrar.a(this)
|
||||
e.add(Model.TypeAdapterFactory())
|
||||
a().apply {
|
||||
ReflectUtils.setField(this, "k", true)
|
||||
}
|
||||
}
|
||||
patcher.patch(GsonUtils::class.java.getDeclaredMethod("toJsonPretty", Object::class.java))
|
||||
{ (param, obj: Any) ->
|
||||
if (obj is Message && obj.isComponentV2)
|
||||
param.result = cuteGson.toJson(obj)
|
||||
}
|
||||
|
||||
// add cv2 tag
|
||||
patcher.after<WidgetChatListAdapterItemMessage>("configureItemTag", Message::class.java, Boolean::class.javaPrimitiveType!!)
|
||||
{ (_, msg: Message) ->
|
||||
val textView = ReflectUtils.getField(this, "itemTag") as TextView?
|
||||
?: return@after
|
||||
|
||||
if (!msg.isComponentV2)
|
||||
return@after
|
||||
|
||||
if (textView.text.isEmpty()) {
|
||||
// this code path shouldn't really ever run (only bots can send cv2, and bots have the tag already)
|
||||
// but idk maybe someone self-bots or something
|
||||
textView.visibility = View.VISIBLE
|
||||
@SuppressLint("SetTextI18n")
|
||||
textView.text = "CV2"
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
|
||||
} else {
|
||||
@SuppressLint("SetTextI18n")
|
||||
textView.text = textView.text.toString() + " | CV2"
|
||||
}
|
||||
}
|
||||
|
||||
ComponentV2Type.make()
|
||||
patchGson(patcher)
|
||||
}
|
||||
|
||||
fun ComponentsV2.stopCompat() {
|
||||
unpatchGson()
|
||||
ComponentV2Type.unmake(logger)
|
||||
}
|
||||
|
||||
private fun patchGson(patcher: PatcherAPI) {
|
||||
val factory = ComponentRuntimeTypeAdapter.INSTANCE.a()
|
||||
val typeToClass = factory.l
|
||||
val classToType = factory.m
|
||||
ComponentV2Type.newValues?.forEach {
|
||||
typeToClass[it.type.toString()] = it.clazz
|
||||
classToType[it.clazz] = it.type.toString()
|
||||
}
|
||||
|
||||
patcher.instead<ComponentTypeTypeAdapter>("read", JsonReader::class.java)
|
||||
{ (_, jsonReader: JsonReader) ->
|
||||
val type: Int = b.c.a.a0.d.n1(jsonReader)
|
||||
ComponentType.values().find { it.type == type } ?: ComponentType.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun unpatchGson() {
|
||||
val factory = ComponentRuntimeTypeAdapter.INSTANCE.a()
|
||||
val typeToClass = factory.l
|
||||
val classToType = factory.m
|
||||
ComponentV2Type.newValues?.forEach {
|
||||
typeToClass.remove(it.type.toString())
|
||||
classToType.remove(it.clazz)
|
||||
}
|
||||
}
|
||||
|
||||
object CV2Compat {
|
||||
/** Creates a new [MessageAttachment] */
|
||||
fun createAttachment(
|
||||
filename: String,
|
||||
filesize: Long,
|
||||
proxyUrl: String,
|
||||
url: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
): MessageAttachment {
|
||||
val inst = ReflectUtils.allocateInstance(clazz)
|
||||
filenameField.set(inst, filename)
|
||||
filesizeField.set(inst, filesize)
|
||||
proxyUrlField.set(inst, proxyUrl)
|
||||
urlField.set(inst, url)
|
||||
widthField.set(inst, width)
|
||||
heightField.set(inst, height)
|
||||
return inst
|
||||
}
|
||||
}
|
||||
|
||||
private val clazz = MessageAttachment::class.java
|
||||
private val filenameField = clazz.getDeclaredField("filename").apply { isAccessible = true }
|
||||
private val filesizeField = clazz.getDeclaredField("size").apply { isAccessible = true }
|
||||
private val proxyUrlField = clazz.getDeclaredField("proxyUrl").apply { isAccessible = true }
|
||||
private val urlField = clazz.getDeclaredField("url").apply { isAccessible = true }
|
||||
private val widthField = clazz.getDeclaredField("width").apply { isAccessible = true }
|
||||
private val heightField = clazz.getDeclaredField("height").apply { isAccessible = true }
|
||||
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.aliucord.Constants
|
||||
import com.aliucord.Utils
|
||||
import com.aliucord.annotations.AliucordPlugin
|
||||
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
||||
|
|
@ -13,11 +12,7 @@ import com.aliucord.coreplugins.componentsv2.patchMessageItems
|
|||
import com.aliucord.coreplugins.componentsv2.views.*
|
||||
import com.aliucord.entities.Plugin
|
||||
import com.aliucord.patcher.*
|
||||
import com.aliucord.utils.ReflectUtils
|
||||
import com.discord.api.botuikit.*
|
||||
import com.discord.api.botuikit.gson.ComponentRuntimeTypeAdapter
|
||||
import com.discord.api.botuikit.gson.ComponentTypeTypeAdapter
|
||||
import com.discord.api.message.attachment.MessageAttachment
|
||||
import com.discord.models.botuikit.*
|
||||
import com.discord.models.message.Message
|
||||
import com.discord.stores.StoreApplicationInteractions.InteractionSendState
|
||||
|
|
@ -29,57 +24,17 @@ import com.discord.widgets.botuikit.views.select.SelectComponentView
|
|||
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapter
|
||||
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemBotComponentRow
|
||||
import com.discord.widgets.chat.list.entries.BotUiComponentEntry
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.lytefast.flexinput.R
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import java.io.File
|
||||
|
||||
val Message.isComponentV2 get() = (flags shr 15) and 1 == 1L
|
||||
|
||||
@AliucordPlugin(requiresRestart = true)
|
||||
@Suppress("unused")
|
||||
class ComponentsV2 : Plugin() {
|
||||
companion object {
|
||||
/** Creates a new [MessageAttachment] */
|
||||
fun createAttachment(
|
||||
filename: String,
|
||||
filesize: Long,
|
||||
proxyUrl: String,
|
||||
url: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
): MessageAttachment {
|
||||
val inst = ReflectUtils.allocateInstance(clazz)
|
||||
filenameField.set(inst, filename)
|
||||
filesizeField.set(inst, filesize)
|
||||
proxyUrlField.set(inst, proxyUrl)
|
||||
urlField.set(inst, url)
|
||||
widthField.set(inst, width)
|
||||
heightField.set(inst, height)
|
||||
return inst
|
||||
}
|
||||
|
||||
private val clazz = MessageAttachment::class.java
|
||||
private val filenameField = clazz.getDeclaredField("filename").apply { isAccessible = true }
|
||||
private val filesizeField = clazz.getDeclaredField("size").apply { isAccessible = true }
|
||||
private val proxyUrlField = clazz.getDeclaredField("proxyUrl").apply { isAccessible = true }
|
||||
private val urlField = clazz.getDeclaredField("url").apply { isAccessible = true }
|
||||
private val widthField = clazz.getDeclaredField("width").apply { isAccessible = true }
|
||||
private val heightField = clazz.getDeclaredField("height").apply { isAccessible = true }
|
||||
}
|
||||
|
||||
override fun start(context: Context) {
|
||||
val oldFile = File("${Constants.PLUGINS_PATH}/ComponentsV2-Beta.zip")
|
||||
if (oldFile.exists()) {
|
||||
logger.info("old plugin found, deleting and prompting restart")
|
||||
oldFile.delete()
|
||||
Utils.promptRestart()
|
||||
return
|
||||
}
|
||||
|
||||
compat(patcher)
|
||||
XposedBridge.makeClassInheritable(BotUiComponentEntry::class.java)
|
||||
ComponentV2Type.make()
|
||||
patchGson()
|
||||
// https://github.com/LSPosed/LSPlant/issues/41
|
||||
patchMessageItems(patcher)
|
||||
|
||||
|
|
@ -205,32 +160,6 @@ class ComponentsV2 : Plugin() {
|
|||
|
||||
override fun stop(context: Context) {
|
||||
patcher.unpatchAll()
|
||||
unpatchGson()
|
||||
ComponentV2Type.unmake(logger)
|
||||
}
|
||||
|
||||
private fun patchGson() {
|
||||
val factory = ComponentRuntimeTypeAdapter.INSTANCE.a()
|
||||
val typeToClass = factory.l
|
||||
val classToType = factory.m
|
||||
ComponentV2Type.newValues?.forEach {
|
||||
typeToClass[it.type.toString()] = it.clazz
|
||||
classToType[it.clazz] = it.type.toString()
|
||||
}
|
||||
|
||||
patcher.instead<ComponentTypeTypeAdapter>("read", JsonReader::class.java)
|
||||
{ (_, jsonReader: JsonReader) ->
|
||||
val type: Int = b.c.a.a0.d.n1(jsonReader)
|
||||
ComponentType.values().find { it.type == type } ?: ComponentType.UNKNOWN
|
||||
}
|
||||
}
|
||||
private fun unpatchGson() {
|
||||
val factory = ComponentRuntimeTypeAdapter.INSTANCE.a()
|
||||
val typeToClass = factory.l
|
||||
val classToType = factory.m
|
||||
ComponentV2Type.newValues?.forEach {
|
||||
typeToClass.remove(it.type.toString())
|
||||
classToType.remove(it.clazz)
|
||||
}
|
||||
stopCompat()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import android.widget.FrameLayout
|
|||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
||||
import com.aliucord.Logger
|
||||
import com.aliucord.coreplugins.ComponentsV2
|
||||
import com.aliucord.coreplugins.CV2Compat
|
||||
import com.aliucord.coreplugins.componentsv2.BotUiComponentV2Entry
|
||||
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
||||
import com.aliucord.coreplugins.componentsv2.models.MediaGalleryMessageComponent
|
||||
|
|
@ -77,7 +77,7 @@ class MediaGalleryComponentView(ctx: Context) : ConstraintLayout(ctx), Component
|
|||
val media = it.media
|
||||
// TODO: there's probably a utility to extract filename from url
|
||||
val name = media.url.split("/").last().split("?").first()
|
||||
val attachment = ComponentsV2.createAttachment(
|
||||
val attachment = CV2Compat.createAttachment(
|
||||
name,
|
||||
0,
|
||||
media.proxyUrl,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue