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.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import com.aliucord.Constants
|
|
||||||
import com.aliucord.Utils
|
import com.aliucord.Utils
|
||||||
import com.aliucord.annotations.AliucordPlugin
|
import com.aliucord.annotations.AliucordPlugin
|
||||||
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
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.coreplugins.componentsv2.views.*
|
||||||
import com.aliucord.entities.Plugin
|
import com.aliucord.entities.Plugin
|
||||||
import com.aliucord.patcher.*
|
import com.aliucord.patcher.*
|
||||||
import com.aliucord.utils.ReflectUtils
|
|
||||||
import com.discord.api.botuikit.*
|
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.botuikit.*
|
||||||
import com.discord.models.message.Message
|
import com.discord.models.message.Message
|
||||||
import com.discord.stores.StoreApplicationInteractions.InteractionSendState
|
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.WidgetChatListAdapter
|
||||||
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemBotComponentRow
|
import com.discord.widgets.chat.list.adapter.WidgetChatListAdapterItemBotComponentRow
|
||||||
import com.discord.widgets.chat.list.entries.BotUiComponentEntry
|
import com.discord.widgets.chat.list.entries.BotUiComponentEntry
|
||||||
import com.google.gson.stream.JsonReader
|
|
||||||
import com.lytefast.flexinput.R
|
import com.lytefast.flexinput.R
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
val Message.isComponentV2 get() = (flags shr 15) and 1 == 1L
|
val Message.isComponentV2 get() = (flags shr 15) and 1 == 1L
|
||||||
|
|
||||||
@AliucordPlugin(requiresRestart = true)
|
@AliucordPlugin(requiresRestart = true)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class ComponentsV2 : Plugin() {
|
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) {
|
override fun start(context: Context) {
|
||||||
val oldFile = File("${Constants.PLUGINS_PATH}/ComponentsV2-Beta.zip")
|
compat(patcher)
|
||||||
if (oldFile.exists()) {
|
|
||||||
logger.info("old plugin found, deleting and prompting restart")
|
|
||||||
oldFile.delete()
|
|
||||||
Utils.promptRestart()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
XposedBridge.makeClassInheritable(BotUiComponentEntry::class.java)
|
XposedBridge.makeClassInheritable(BotUiComponentEntry::class.java)
|
||||||
ComponentV2Type.make()
|
|
||||||
patchGson()
|
|
||||||
// https://github.com/LSPosed/LSPlant/issues/41
|
// https://github.com/LSPosed/LSPlant/issues/41
|
||||||
patchMessageItems(patcher)
|
patchMessageItems(patcher)
|
||||||
|
|
||||||
|
|
@ -205,32 +160,6 @@ class ComponentsV2 : Plugin() {
|
||||||
|
|
||||||
override fun stop(context: Context) {
|
override fun stop(context: Context) {
|
||||||
patcher.unpatchAll()
|
patcher.unpatchAll()
|
||||||
unpatchGson()
|
stopCompat()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import android.widget.FrameLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
||||||
import com.aliucord.Logger
|
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.BotUiComponentV2Entry
|
||||||
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
import com.aliucord.coreplugins.componentsv2.ComponentV2Type
|
||||||
import com.aliucord.coreplugins.componentsv2.models.MediaGalleryMessageComponent
|
import com.aliucord.coreplugins.componentsv2.models.MediaGalleryMessageComponent
|
||||||
|
|
@ -77,7 +77,7 @@ class MediaGalleryComponentView(ctx: Context) : ConstraintLayout(ctx), Component
|
||||||
val media = it.media
|
val media = it.media
|
||||||
// TODO: there's probably a utility to extract filename from url
|
// TODO: there's probably a utility to extract filename from url
|
||||||
val name = media.url.split("/").last().split("?").first()
|
val name = media.url.split("/").last().split("?").first()
|
||||||
val attachment = ComponentsV2.createAttachment(
|
val attachment = CV2Compat.createAttachment(
|
||||||
name,
|
name,
|
||||||
0,
|
0,
|
||||||
media.proxyUrl,
|
media.proxyUrl,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue