feat(core): message indicators

This commit is contained in:
rhunk
2024-03-17 00:08:08 +01:00
parent 52addbe780
commit 3d6d070b29
6 changed files with 197 additions and 47 deletions

View File

@ -112,7 +112,7 @@ class FeatureManager(
DisableConfirmationDialogs(),
MixerStories(),
DisableComposerModules(),
FideliusIndicator(),
MessageIndicators(),
EditTextOverride(),
PreventForcedLogout(),
SuspendLocationUpdates(),

View File

@ -1,42 +0,0 @@
package me.rhunk.snapenhance.core.features.impl.ui
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.Shape
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.ui.addForegroundDrawable
import me.rhunk.snapenhance.core.ui.removeForegroundDrawable
class FideliusIndicator : Feature("Fidelius Indicator", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
override fun onActivityCreate() {
if (!context.config.userInterface.fideliusIndicator.get()) return
context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { _, messageId ->
event.view.removeForegroundDrawable("fideliusIndicator")
val message = context.database.getConversationMessageFromId(messageId.toLong()) ?: return@chatMessage
if (message.senderId == context.database.myUserId) return@chatMessage
if (message.contentType != ContentType.SNAP.id && message.contentType != ContentType.EXTERNAL_MEDIA.id) return@chatMessage
if (!ProtoReader(message.messageContent ?: return@chatMessage).containsPath(4, 3, 3, 6)) return@chatMessage
event.view.addForegroundDrawable("fideliusIndicator", ShapeDrawable(object: Shape() {
override fun draw(canvas: Canvas, paint: Paint) {
val margin = 25f
val radius = 15f
canvas.drawCircle(margin + radius, canvas.height - margin - radius, radius, paint.apply {
color = 0xFF00FF00.toInt()
})
}
}))
}
}
}
}

View File

@ -0,0 +1,110 @@
package me.rhunk.snapenhance.core.features.impl.ui
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.Laptop
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.Lock
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.unit.dp
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.ui.createComposeView
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.ui.AppleLogo
import me.rhunk.snapenhance.core.ui.removeForegroundDrawable
import kotlin.random.Random
class MessageIndicators : Feature("Message Indicators", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
override fun onActivityCreate() {
val messageIndicatorsConfig = context.config.userInterface.messageIndicators.getNullable() ?: return
if (messageIndicatorsConfig.isEmpty()) return
val messageInfoTag = Random.nextLong().toString()
val appleLogo = AppleLogo
context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { _, messageId ->
val parentLinearLayout = event.view.parent as? ViewGroup ?: return@subscribe
parentLinearLayout.findViewWithTag<View>(messageInfoTag)?.let { parentLinearLayout.removeView(it) }
event.view.removeForegroundDrawable("messageIndicators")
val message = context.database.getConversationMessageFromId(messageId.toLong()) ?: return@chatMessage
if (message.contentType != ContentType.SNAP.id && message.contentType != ContentType.EXTERNAL_MEDIA.id) return@chatMessage
val reader = ProtoReader(message.messageContent ?: return@chatMessage)
val hasEncryption = if (reader.containsPath(4, 3)) reader.getByteArray(4, 3, 1) == null else false
val sentFromIosDevice = if (reader.containsPath(4, 4, 3)) !reader.containsPath(4, 4, 3, 3, 17) else reader.getVarInt(4, 4, 11, 17, 7) != null
val sentFromWebApp = reader.getVarInt(4, 4, *(if (reader.containsPath(4, 4, 3)) intArrayOf(3, 3, 22, 1) else intArrayOf(11, 22, 1))) == 7L
val sentWithLocation = reader.getVarInt(4, 4, 11, 17, 5) != null
createComposeView(event.view.context) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp, end = 1.dp),
contentAlignment = Alignment.BottomEnd
) {
Row {
if (messageIndicatorsConfig.contains("location_indicator")) {
if (sentWithLocation) {
Image(
imageVector = Icons.Default.LocationOn,
colorFilter = ColorFilter.tint(Color.Green),
contentDescription = null,
modifier = Modifier.size(15.dp)
)
}
}
if (messageIndicatorsConfig.contains("platform_indicator")) {
Image(
imageVector = when {
sentFromWebApp -> Icons.Default.Laptop
sentFromIosDevice -> appleLogo
else -> Icons.Default.Android
},
colorFilter = ColorFilter.tint(Color.Green),
contentDescription = null,
modifier = Modifier.size(15.dp)
)
}
if (hasEncryption && messageIndicatorsConfig.contains("encryption_indicator")) {
Image(
imageVector = Icons.Default.Lock,
colorFilter = ColorFilter.tint(Color.Green),
contentDescription = null,
modifier = Modifier.size(15.dp)
)
}
}
}
}.apply {
tag = messageInfoTag
addOnLayoutChangeListener { _, left, _, right, _, _, _, _, _ ->
layout(left, 0, right, 0)
}
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
)
parentLinearLayout.addView(this)
}
}
}
}
}

View File

@ -0,0 +1,77 @@
package me.rhunk.snapenhance.core.ui
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.group
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.unit.dp
val AppleLogo: ImageVector
get() {
return ImageVector.Builder(
defaultWidth = 800.dp,
defaultHeight = 800.dp,
viewportWidth = 27f,
viewportHeight = 27f,
).apply {
group {
path(
fill = SolidColor(Color(0xFF000000)),
fillAlpha = 1.0f,
stroke = null,
strokeAlpha = 1.0f,
strokeLineWidth = 1.0f,
strokeLineCap = StrokeCap.Butt,
strokeLineJoin = StrokeJoin.Miter,
strokeLineMiter = 1.0f,
pathFillType = PathFillType.NonZero
) {
moveTo(15.769f, 0f)
curveToRelative(0.053f, 0f, 0.106f, 0f, 0.162f, 0f)
curveToRelative(0.13f, 1.606f, -0.483f, 2.806f, -1.228f, 3.675f)
curveToRelative(-0.731f, 0.863f, -1.732f, 1.7f, -3.351f, 1.573f)
curveToRelative(-0.108f, -1.583f, 0.506f, -2.694f, 1.25f, -3.561f)
curveTo(13.292f, 0.879f, 14.557f, 0.16f, 15.769f, 0f)
close()
}
path(
fill = SolidColor(Color(0xFF000000)),
fillAlpha = 1.0f,
stroke = null,
strokeAlpha = 1.0f,
strokeLineWidth = 1.0f,
strokeLineCap = StrokeCap.Butt,
strokeLineJoin = StrokeJoin.Miter,
strokeLineMiter = 1.0f,
pathFillType = PathFillType.NonZero
) {
moveTo(20.67f, 16.716f)
curveToRelative(0f, 0.016f, 0f, 0.03f, 0f, 0.045f)
curveToRelative(-0.455f, 1.378f, -1.104f, 2.559f, -1.896f, 3.655f)
curveToRelative(-0.723f, 0.995f, -1.609f, 2.334f, -3.191f, 2.334f)
curveToRelative(-1.367f, 0f, -2.275f, -0.879f, -3.676f, -0.903f)
curveToRelative(-1.482f, -0.024f, -2.297f, 0.735f, -3.652f, 0.926f)
curveToRelative(-0.155f, 0f, -0.31f, 0f, -0.462f, 0f)
curveToRelative(-0.995f, -0.144f, -1.798f, -0.932f, -2.383f, -1.642f)
curveToRelative(-1.725f, -2.098f, -3.058f, -4.808f, -3.306f, -8.276f)
curveToRelative(0f, -0.34f, 0f, -0.679f, 0f, -1.019f)
curveToRelative(0.105f, -2.482f, 1.311f, -4.5f, 2.914f, -5.478f)
curveToRelative(0.846f, -0.52f, 2.009f, -0.963f, 3.304f, -0.765f)
curveToRelative(0.555f, 0.086f, 1.122f, 0.276f, 1.619f, 0.464f)
curveToRelative(0.471f, 0.181f, 1.06f, 0.502f, 1.618f, 0.485f)
curveToRelative(0.378f, -0.011f, 0.754f, -0.208f, 1.135f, -0.347f)
curveToRelative(1.116f, -0.403f, 2.21f, -0.865f, 3.652f, -0.648f)
curveToRelative(1.733f, 0.262f, 2.963f, 1.032f, 3.723f, 2.22f)
curveToRelative(-1.466f, 0.933f, -2.625f, 2.339f, -2.427f, 4.74f)
curveTo(17.818f, 14.688f, 19.086f, 15.964f, 20.67f, 16.716f)
close()
}
}
}.build()
}