commit 3d6d070b291cac933eb82f526d06767bea2deb18
parent 52addbe78063537fcc0b9704d9dc24faaaea4906
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sun, 17 Mar 2024 00:08:08 +0100
feat(core): message indicators
Diffstat:
6 files changed, 198 insertions(+), 48 deletions(-)
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -336,9 +336,9 @@
"name": "Friend Feed Menu Bar",
"description": "Enables the new Friend Feed Menu Bar"
},
- "fidelius_indicator": {
- "name": "Fidelius Indicator",
- "description": "Adds a green circle next to messages that have been sent only to you"
+ "message_indicators": {
+ "name": "Message Indicators",
+ "description": "Adds specific indicators icons to messages\nNote: indicators might not be 100% accurate"
},
"stealth_mode_indicator": {
"name": "Stealth Mode Indicator",
@@ -944,6 +944,11 @@
"read_contacts": "Read Contacts",
"nearby_devices": "Nearby Devices",
"phone_calls": "Phone Calls"
+ },
+ "message_indicators": {
+ "encryption_indicator": "Adds a \uD83D\uDD12 icon next to messages that have been sent only to you",
+ "platform_indicator": "Adds the platform icon from which a media was sent (e.g. Android, iOS, Web)",
+ "location_indicator": "Adds a \uD83D\uDCCD icon to snaps when they have been sent with location enabled"
}
}
},
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/UserInterfaceTweaks.kt
@@ -47,7 +47,7 @@ class UserInterfaceTweaks : ConfigContainer() {
val disableSpotlight = boolean("disable_spotlight") { requireRestart() }
val hideSettingsGear = boolean("hide_settings_gear") { requireRestart() }
val verticalStoryViewer = boolean("vertical_story_viewer") { requireRestart() }
- val fideliusIndicator = boolean("fidelius_indicator") { requireRestart() }
+ val messageIndicators = multiple("message_indicators", "encryption_indicator", "platform_indicator", "location_indicator") { requireRestart() }
val stealthModeIndicator = boolean("stealth_mode_indicator") { requireRestart() }
val editTextOverride = multiple("edit_text_override", "multi_line_chat_input", "bypass_text_input_limit") {
requireRestart(); addNotices(FeatureNotice.BAN_RISK, FeatureNotice.INTERNAL_BEHAVIOR)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt
@@ -112,7 +112,7 @@ class FeatureManager(
DisableConfirmationDialogs(),
MixerStories(),
DisableComposerModules(),
- FideliusIndicator(),
+ MessageIndicators(),
EditTextOverride(),
PreventForcedLogout(),
SuspendLocationUpdates(),
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/FideliusIndicator.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/FideliusIndicator.kt
@@ -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()
- })
- }
- }))
- }
- }
- }
-}-
\ No newline at end of file
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/MessageIndicators.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ui/MessageIndicators.kt
@@ -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)
+ }
+ }
+ }
+ }
+}+
\ No newline at end of file
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/ComposeIcons.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ui/ComposeIcons.kt
@@ -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()
+ }
+