feat(send_override): optional override dialog

- fix saveable snap in chat
This commit is contained in:
rhunk
2024-04-27 14:52:41 +02:00
parent ef8e2f9911
commit 21b4eea58f
5 changed files with 68 additions and 56 deletions

View File

@ -1059,6 +1059,7 @@
"abandon_video": "Missed Video Call" "abandon_video": "Missed Video Call"
}, },
"gallery_media_send_override": { "gallery_media_send_override": {
"always_ask": "Always Ask",
"ORIGINAL": "Original", "ORIGINAL": "Original",
"NOTE": "Audio Note", "NOTE": "Audio Note",
"SNAP": "Snap", "SNAP": "Snap",

View File

@ -73,7 +73,7 @@ object DataProcessors {
val STRING_UNIQUE_SELECTION = PropertyDataProcessor( val STRING_UNIQUE_SELECTION = PropertyDataProcessor(
type = Type.STRING_UNIQUE_SELECTION, type = Type.STRING_UNIQUE_SELECTION,
serialize = { JsonPrimitive(it) }, 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( val MAP_COORDINATES = PropertyDataProcessor(

View File

@ -94,7 +94,7 @@ class MessagingTweaks : ConfigContainer() {
customOptionTranslationPath = "features.options.notifications" customOptionTranslationPath = "features.options.notifications"
} }
val messageLogger = container("message_logger", MessageLoggerConfig()) { addNotices(FeatureNotice.UNSTABLE); requireRestart() } 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 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 bypassMessageRetentionPolicy = boolean("bypass_message_retention_policy") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() } val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() }

View File

@ -176,7 +176,7 @@ class MediaFilePicker : Feature("Media File Picker", loadParams = FeatureLoadPar
val isAudio = context.androidContext.contentResolver.getType(event.intent.data!!)!!.startsWith("audio/") 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) startConversation(isAudio)
return@subscribe return@subscribe
} }

View File

@ -1,5 +1,7 @@
package me.rhunk.snapenhance.core.features.impl.messaging 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.data.ContentType
import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader 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) { class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INIT_SYNC) {
private var isLastSnapSavable = false private var isLastSnapSavable = false
private val typeNames by lazy { private val typeNames by lazy {
mutableListOf( mutableListOf("ORIGINAL", "SNAP", "NOTE").also {
"ORIGINAL",
"SNAP",
"NOTE"
).also {
if (NativeLib.initialized) { if (NativeLib.initialized) {
it.add("SAVABLE_SNAP") it.add("SAVABLE_SNAP")
} }
}.associateWith { }.associateWith { it }
it
}
} }
override fun init() { 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() val stripSnapMetadata = context.config.messaging.stripMediaMetadata.get()
context.event.subscribe(SendMessageWithContentEvent::class, { context.event.subscribe(SendMessageWithContentEvent::class, {
@ -90,9 +74,28 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI
event.messageContent.content = newMessageContent event.messageContent.content = newMessageContent
} }
context.event.subscribe(SendMessageWithContentEvent::class, { val configOverrideType = context.config.messaging.galleryMediaSendOverride.getNullable() ?: return
context.config.messaging.galleryMediaSendOverride.get()
}) { event -> 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 isLastSnapSavable = false
if (event.destinations.stories?.isNotEmpty() == true && event.destinations.conversations?.isEmpty() == true) return@subscribe if (event.destinations.stories?.isNotEmpty() == true && event.destinations.conversations?.isEmpty() == true) return@subscribe
val localMessageContent = event.messageContent val localMessageContent = event.messageContent
@ -104,43 +107,51 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI
event.canceled = true 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 { context.runOnUiThread {
ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity!!) ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity!!)
.setItems(typeNames.values.map { .setItems(typeNames.values.map {
context.translation["features.options.gallery_media_send_override.$it"] context.translation["features.options.gallery_media_send_override.$it"]
}.toTypedArray()) { dialog, which -> }.toTypedArray()) { dialog, which ->
dialog.dismiss() dialog.dismiss()
val overrideType = typeNames.keys.toTypedArray()[which] if (sendMedia(typeNames.keys.toTypedArray()[which])) {
event.invokeOriginal()
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)
}
}
event.invokeOriginal()
} }
.setNegativeButton(context.translation["button.cancel"], null) .setNegativeButton(context.translation["button.cancel"], null)
.show() .show()