commit 671b35f7e061abc0267a404557311e5a9cadddfd
parent 915216ec5db23db2cee276bfacfc8329a0d26e06
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Thu, 18 May 2023 10:05:04 +0200
fix(notifications): external medias & message history
Diffstat:
2 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/Constants.kt b/app/src/main/kotlin/me/rhunk/snapenhance/Constants.kt
@@ -10,6 +10,7 @@ object Constants {
val ARROYO_NOTE_ENCRYPTION_PROTO_PATH = intArrayOf(4, 4, 6, 1, 1)
val ARROYO_SNAP_ENCRYPTION_PROTO_PATH = intArrayOf(4, 4, 11, 5, 1, 1)
val MESSAGE_SNAP_ENCRYPTION_PROTO_PATH = intArrayOf(11, 5, 1, 1)
+ val MESSAGE_EXTERNAL_MEDIA_ENCRYPTION_PROTO_PATH = intArrayOf(3, 3, 5, 1, 1)
val ARROYO_EXTERNAL_MEDIA_ENCRYPTION_PROTO_PATH = intArrayOf(4, 4, 3, 3, 5, 1, 1)
val ARROYO_STRING_CHAT_MESSAGE_PROTO = intArrayOf(4, 4, 2, 1)
val ARROYO_URL_KEY_PROTO_PATH = intArrayOf(4, 5, 1, 3, 2, 2)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/Notifications.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/extras/Notifications.kt
@@ -27,8 +27,9 @@ import me.rhunk.snapenhance.util.download.CdnDownloader
import me.rhunk.snapenhance.util.protobuf.ProtoReader
class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.INIT_SYNC) {
- private val notificationDataQueue = mutableMapOf<Long, NotificationData>()
- private val cachedNotifications = mutableMapOf<String, MutableList<String>>()
+ private val notificationDataQueue = mutableMapOf<Long, NotificationData>() // messageId => notification
+ private val cachedMessages = mutableMapOf<String, MutableList<String>>() // conversationId => cached messages
+ private val notificationIdMap = mutableMapOf<Int, String>() // notificationId => conversationId
private val notifyAsUserMethod by lazy {
XposedHelpers.findMethodExact(
@@ -40,6 +41,10 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
)
}
+ private val cancelAsUserMethod by lazy {
+ XposedHelpers.findMethodExact(NotificationManager::class.java, "cancelAsUser", String::class.java, Int::class.javaPrimitiveType, UserHandle::class.java)
+ }
+
private val fetchConversationWithMessagesMethod by lazy {
context.classCache.conversationManager.methods.first { it.name == "fetchConversationWithMessages"}
}
@@ -57,7 +62,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
private fun computeNotificationText(conversationId: String): String {
val messageBuilder = StringBuilder()
- cachedNotifications.computeIfAbsent(conversationId) { mutableListOf() }.forEach {
+ cachedMessages.computeIfAbsent(conversationId) { mutableListOf() }.forEach {
if (messageBuilder.isNotEmpty()) messageBuilder.append("\n")
messageBuilder.append(it)
}
@@ -65,9 +70,12 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
}
private fun fetchMessagesResult(conversationId: String, messages: List<Message>) {
- val sendNotificationData = { it: NotificationData ->
+ val sendNotificationData = { notificationData: NotificationData, forceCreate: Boolean ->
+ val notificationId = if (forceCreate) System.nanoTime().toInt() else notificationData.id
+ notificationIdMap.computeIfAbsent(notificationId) { conversationId }
+
XposedBridge.invokeOriginalMethod(notifyAsUserMethod, notificationManager, arrayOf(
- it.tag, it.id, it.notification, it.userHandle
+ notificationData.tag, if (forceCreate) System.nanoTime().toInt() else notificationData.id, notificationData.notification, notificationData.userHandle
))
}
@@ -79,7 +87,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
val contentData = snapMessage.messageContent.content
val formatUsername: (String) -> String = { "$senderUsername: $it" }
- val notificationCache = cachedNotifications.let { it.computeIfAbsent(conversationId) { mutableListOf() } }
+ val notificationCache = cachedMessages.let { it.computeIfAbsent(conversationId) { mutableListOf() } }
val appendNotifications: () -> Unit = { setNotificationText(notificationData, computeNotificationText(conversationId))}
when (contentType) {
@@ -93,7 +101,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
}
appendNotifications()
}
- ContentType.SNAP -> {
+ ContentType.SNAP, ContentType.EXTERNAL_MEDIA-> {
//serialize the message content into a json object
val serializedMessageContent = context.gson.toJsonTree(snapMessage.messageContent.instanceNonNull()).asJsonObject
val mediaReferences = serializedMessageContent["mRemoteMediaReferences"]
@@ -107,7 +115,13 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
runCatching {
//download the media
var mediaInputStream = CdnDownloader.downloadWithDefaultEndpoints(urlKey)!!
- val mediaInfo = ProtoReader(contentData).readPath(*Constants.MESSAGE_SNAP_ENCRYPTION_PROTO_PATH) ?: return@runCatching
+ val mediaInfo = ProtoReader(contentData).let {
+ if (contentType == ContentType.EXTERNAL_MEDIA)
+ return@let it.readPath(*Constants.MESSAGE_EXTERNAL_MEDIA_ENCRYPTION_PROTO_PATH)
+ else
+ return@let it.readPath(*Constants.MESSAGE_SNAP_ENCRYPTION_PROTO_PATH)
+ }?: return@runCatching
+
//decrypt if necessary
if (mediaInfo.exists(Constants.ARROYO_ENCRYPTION_PROTO_INDEX)) {
mediaInputStream = EncryptionUtils.decryptInputStream(mediaInputStream, false, mediaInfo, Constants.ARROYO_ENCRYPTION_PROTO_INDEX)
@@ -124,7 +138,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
notificationBuilder.setLargeIcon(bitmapPreview)
notificationBuilder.style = Notification.BigPictureStyle().bigPicture(bitmapPreview).bigLargeIcon(null as Bitmap?)
- sendNotificationData(notificationData.copy(id = System.nanoTime().toInt(), notification = notificationBuilder.build()))
+ sendNotificationData(notificationData.copy(notification = notificationBuilder.build()), true)
return@onEach
}.onFailure {
Logger.xposedLog("Failed to send preview notification", it)
@@ -136,7 +150,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
}
}
- sendNotificationData(notificationData)
+ sendNotificationData(notificationData, false)
}.clear()
}
@@ -146,10 +160,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
Hooker.hook(notifyAsUserMethod, HookStage.BEFORE, { context.config.bool(ConfigProperty.SHOW_MESSAGE_CONTENT) }) {
val notificationData = NotificationData(it.argNullable(0), it.arg(1), it.arg(2), it.arg(3))
- if (!notificationData.notification.extras.containsKey("system_notification_extras")) {
- return@hook
- }
- val extras: Bundle = notificationData.notification.extras.getBundle("system_notification_extras")!!
+ val extras: Bundle = notificationData.notification.extras.getBundle("system_notification_extras")?: return@hook
val messageId = extras.getString("message_id")!!
val notificationType = extras.getString("notification_type")!!
@@ -172,6 +183,13 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instanceNonNull(), callback)
it.setResult(null)
}
+
+ Hooker.hook(cancelAsUserMethod, HookStage.BEFORE) { param ->
+ val notificationId = param.arg<Int>(1)
+ notificationIdMap[notificationId]?.let {
+ cachedMessages[it]?.clear()
+ }
+ }
}
data class NotificationData(