commit 21b4eea58f554db650a900ef76db35e5ab9f3205
parent ef8e2f99115456fdf1d9d43213fcdf6dc39ce861
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sat, 27 Apr 2024 14:52:41 +0200

feat(send_override): optional override dialog
- fix saveable snap in chat

Diffstat:
Mcommon/src/main/assets/lang/en_US.json | 1+
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/DataProcessors.kt | 2+-
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/MediaFilePicker.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/SendOverride.kt | 117+++++++++++++++++++++++++++++++++++++++++++------------------------------------
5 files changed, 68 insertions(+), 56 deletions(-)

diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -1059,6 +1059,7 @@ "abandon_video": "Missed Video Call" }, "gallery_media_send_override": { + "always_ask": "Always Ask", "ORIGINAL": "Original", "NOTE": "Audio Note", "SNAP": "Snap", diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/DataProcessors.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/DataProcessors.kt @@ -73,7 +73,7 @@ object DataProcessors { val STRING_UNIQUE_SELECTION = PropertyDataProcessor( type = Type.STRING_UNIQUE_SELECTION, serialize = { JsonPrimitive(it) }, - deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString } + deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString?.takeIf { it != "false" && it != "true" } } ) val MAP_COORDINATES = PropertyDataProcessor( diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt @@ -94,7 +94,7 @@ class MessagingTweaks : ConfigContainer() { customOptionTranslationPath = "features.options.notifications" } val messageLogger = container("message_logger", MessageLoggerConfig()) { addNotices(FeatureNotice.UNSTABLE); requireRestart() } - val galleryMediaSendOverride = boolean("gallery_media_send_override") { nativeHooks() } + val galleryMediaSendOverride = unique("gallery_media_send_override", "always_ask", "SNAP", "NOTE", "SAVABLE_SNAP") { requireRestart(); nativeHooks() } val stripMediaMetadata = multiple("strip_media_metadata", "hide_caption_text", "hide_snap_filters", "hide_extras", "remove_audio_note_duration", "remove_audio_note_transcript_capability") { requireRestart() } val bypassMessageRetentionPolicy = boolean("bypass_message_retention_policy") { addNotices(FeatureNotice.UNSTABLE); requireRestart() } val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/MediaFilePicker.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/MediaFilePicker.kt @@ -176,7 +176,7 @@ class MediaFilePicker : Feature("Media File Picker", loadParams = FeatureLoadPar val isAudio = context.androidContext.contentResolver.getType(event.intent.data!!)!!.startsWith("audio/") - if (isAudio || !context.config.messaging.galleryMediaSendOverride.get()) { + if (isAudio || context.config.messaging.galleryMediaSendOverride.getNullable() == null) { startConversation(isAudio) return@subscribe } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/SendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/SendOverride.kt @@ -1,5 +1,7 @@ package me.rhunk.snapenhance.core.features.impl.messaging +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.WarningAmber import me.rhunk.snapenhance.common.data.ContentType import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor import me.rhunk.snapenhance.common.util.protobuf.ProtoReader @@ -15,32 +17,14 @@ import me.rhunk.snapenhance.nativelib.NativeLib class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INIT_SYNC) { private var isLastSnapSavable = false private val typeNames by lazy { - mutableListOf( - "ORIGINAL", - "SNAP", - "NOTE" - ).also { + mutableListOf("ORIGINAL", "SNAP", "NOTE").also { if (NativeLib.initialized) { it.add("SAVABLE_SNAP") } - }.associateWith { - it - } + }.associateWith { it } } override fun init() { - context.event.subscribe(NativeUnaryCallEvent::class) { event -> - if (event.uri != "/messagingcoreservice.MessagingCoreService/CreateContentMessage") return@subscribe - if (isLastSnapSavable) { - val protoEditor = ProtoEditor(event.buffer) - protoEditor.edit(4) { - remove(7) - addVarInt(7, 3) - } - event.buffer = protoEditor.toByteArray() - } - } - val stripSnapMetadata = context.config.messaging.stripMediaMetadata.get() context.event.subscribe(SendMessageWithContentEvent::class, { @@ -90,9 +74,28 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI event.messageContent.content = newMessageContent } - context.event.subscribe(SendMessageWithContentEvent::class, { - context.config.messaging.galleryMediaSendOverride.get() - }) { event -> + val configOverrideType = context.config.messaging.galleryMediaSendOverride.getNullable() ?: return + + context.event.subscribe(NativeUnaryCallEvent::class) { event -> + if (event.uri != "/messagingcoreservice.MessagingCoreService/CreateContentMessage") return@subscribe + if (isLastSnapSavable) { + event.buffer = ProtoEditor(event.buffer).apply { + edit { + edit(4) { + remove(7) + addVarInt(7, 3) // savePolicy = VIEW_SESSION + } + add(6) { + from(9) { + addVarInt(1, 1) + } + } + } + }.toByteArray() + } + } + + context.event.subscribe(SendMessageWithContentEvent::class) { event -> isLastSnapSavable = false if (event.destinations.stories?.isNotEmpty() == true && event.destinations.conversations?.isEmpty() == true) return@subscribe val localMessageContent = event.messageContent @@ -104,43 +107,51 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI event.canceled = true + fun sendMedia(overrideType: String): Boolean { + if (overrideType != "ORIGINAL" && messageProtoReader.followPath(3)?.getCount(3) != 1) { + context.inAppOverlay.showStatusToast( + icon = Icons.Default.WarningAmber, + context.translation["gallery_media_send_override.multiple_media_toast"] + ) + return false + } + + when (overrideType) { + "SNAP", "SAVABLE_SNAP" -> { + val extras = messageProtoReader.followPath(3, 3, 13)?.getBuffer() + + localMessageContent.contentType = ContentType.SNAP + localMessageContent.content = MessageSender.redSnapProto(extras) + if (overrideType == "SAVABLE_SNAP") { + isLastSnapSavable = true + } + } + "NOTE" -> { + localMessageContent.contentType = ContentType.NOTE + localMessageContent.content = + MessageSender.audioNoteProto(messageProtoReader.getVarInt(3, 3, 5, 1, 1, 15) ?: context.feature(MediaFilePicker::class).lastMediaDuration ?: 0) + } + } + + return true + } + + if (configOverrideType != "always_ask") { + if (sendMedia(configOverrideType)) { + event.invokeOriginal() + } + return@subscribe + } + context.runOnUiThread { ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity!!) .setItems(typeNames.values.map { context.translation["features.options.gallery_media_send_override.$it"] }.toTypedArray()) { dialog, which -> dialog.dismiss() - val overrideType = typeNames.keys.toTypedArray()[which] - - if (overrideType != "ORIGINAL" && messageProtoReader.followPath(3)?.getCount(3) != 1) { - context.runOnUiThread { - ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity!!) - .setMessage(context.translation["gallery_media_send_override.multiple_media_toast"]) - .setPositiveButton(context.translation["button.ok"], null) - .show() - } - return@setItems - } - - when (overrideType) { - "SNAP", "SAVABLE_SNAP" -> { - val extras = messageProtoReader.followPath(3, 3, 13)?.getBuffer() - - localMessageContent.contentType = ContentType.SNAP - localMessageContent.content = MessageSender.redSnapProto(extras) - if (overrideType == "SAVABLE_SNAP") { - isLastSnapSavable = true - } - } - - "NOTE" -> { - localMessageContent.contentType = ContentType.NOTE - localMessageContent.content = - MessageSender.audioNoteProto(messageProtoReader.getVarInt(3, 3, 5, 1, 1, 15) ?: context.feature(MediaFilePicker::class).lastMediaDuration ?: 0) - } + if (sendMedia(typeNames.keys.toTypedArray()[which])) { + event.invokeOriginal() } - - event.invokeOriginal() } .setNegativeButton(context.translation["button.cancel"], null) .show()