mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
feat(core/better_notifications): mark as read
This commit is contained in:
@ -597,6 +597,7 @@
|
|||||||
"snap": "Show media",
|
"snap": "Show media",
|
||||||
"reply_button": "Add reply button",
|
"reply_button": "Add reply button",
|
||||||
"download_button": "Add download button",
|
"download_button": "Add download button",
|
||||||
|
"mark_as_read_button": "Mark as Read button",
|
||||||
"group": "Group notifications"
|
"group": "Group notifications"
|
||||||
},
|
},
|
||||||
"friend_feed_menu_buttons": {
|
"friend_feed_menu_buttons": {
|
||||||
@ -782,6 +783,15 @@
|
|||||||
"download": "Download"
|
"download": "Download"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"better_notifications": {
|
||||||
|
"button": {
|
||||||
|
"reply": "Reply",
|
||||||
|
"download": "Download",
|
||||||
|
"mark_as_read": "Mark as Read"
|
||||||
|
},
|
||||||
|
"stealth_mode_notice": "Can't mark as read in stealth mode"
|
||||||
|
},
|
||||||
|
|
||||||
"profile_picture_downloader": {
|
"profile_picture_downloader": {
|
||||||
"button": "Download Profile Picture",
|
"button": "Download Profile Picture",
|
||||||
"title": "Profile Picture Downloader",
|
"title": "Profile Picture Downloader",
|
||||||
|
@ -23,7 +23,7 @@ class MessagingTweaks : ConfigContainer() {
|
|||||||
customOptionTranslationPath = "features.options.notifications"
|
customOptionTranslationPath = "features.options.notifications"
|
||||||
nativeHooks()
|
nativeHooks()
|
||||||
}
|
}
|
||||||
val betterNotifications = multiple("better_notifications", "snap", "chat", "reply_button", "download_button", "group") { requireRestart() }
|
val betterNotifications = multiple("better_notifications", "snap", "chat", "reply_button", "download_button", "mark_as_read_button", "group") { requireRestart() }
|
||||||
val notificationBlacklist = multiple("notification_blacklist", *NotificationType.getIncomingValues().map { it.key }.toTypedArray()) {
|
val notificationBlacklist = multiple("notification_blacklist", *NotificationType.getIncomingValues().map { it.key }.toTypedArray()) {
|
||||||
customOptionTranslationPath = "features.options.notifications"
|
customOptionTranslationPath = "features.options.notifications"
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,9 @@ import me.rhunk.snapenhance.core.features.Feature
|
|||||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||||
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
||||||
import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
|
import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
|
||||||
import me.rhunk.snapenhance.core.logger.CoreLogger
|
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
||||||
import me.rhunk.snapenhance.core.util.CallbackBuilder
|
import me.rhunk.snapenhance.core.util.CallbackBuilder
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||||
import me.rhunk.snapenhance.core.util.hook.Hooker
|
|
||||||
import me.rhunk.snapenhance.core.util.hook.hook
|
import me.rhunk.snapenhance.core.util.hook.hook
|
||||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
||||||
import me.rhunk.snapenhance.core.util.media.PreviewUtils
|
import me.rhunk.snapenhance.core.util.media.PreviewUtils
|
||||||
@ -39,6 +38,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
companion object{
|
companion object{
|
||||||
const val ACTION_REPLY = "me.rhunk.snapenhance.action.notification.REPLY"
|
const val ACTION_REPLY = "me.rhunk.snapenhance.action.notification.REPLY"
|
||||||
const val ACTION_DOWNLOAD = "me.rhunk.snapenhance.action.notification.DOWNLOAD"
|
const val ACTION_DOWNLOAD = "me.rhunk.snapenhance.action.notification.DOWNLOAD"
|
||||||
|
const val ACTION_MARK_AS_READ = "me.rhunk.snapenhance.action.notification.MARK_AS_READ"
|
||||||
const val SNAPCHAT_NOTIFICATION_GROUP = "snapchat_notification_group"
|
const val SNAPCHAT_NOTIFICATION_GROUP = "snapchat_notification_group"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +64,8 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
context.androidContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
context.androidContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val translations by lazy { context.translation.getCategory("better_notifications") }
|
||||||
|
|
||||||
private val betterNotificationFilter by lazy {
|
private val betterNotificationFilter by lazy {
|
||||||
context.config.messaging.betterNotifications.get()
|
context.config.messaging.betterNotifications.get()
|
||||||
}
|
}
|
||||||
@ -118,19 +120,23 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
actions.add(action)
|
actions.add(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
newAction("Reply", ACTION_REPLY, {
|
newAction(translations["button.reply"], ACTION_REPLY, {
|
||||||
betterNotificationFilter.contains("reply_button") && contentType == ContentType.CHAT
|
betterNotificationFilter.contains("reply_button") && contentType == ContentType.CHAT
|
||||||
}) {
|
}) {
|
||||||
val chatReplyInput = RemoteInput.Builder("chat_reply_input")
|
val chatReplyInput = RemoteInput.Builder("chat_reply_input")
|
||||||
.setLabel("Reply")
|
.setLabel(translations["button.reply"])
|
||||||
.build()
|
.build()
|
||||||
it.addRemoteInput(chatReplyInput)
|
it.addRemoteInput(chatReplyInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
newAction("Download", ACTION_DOWNLOAD, {
|
newAction(translations["button.download"], ACTION_DOWNLOAD, {
|
||||||
betterNotificationFilter.contains("download_button") && (contentType == ContentType.EXTERNAL_MEDIA || contentType == ContentType.SNAP)
|
betterNotificationFilter.contains("download_button") && (contentType == ContentType.EXTERNAL_MEDIA || contentType == ContentType.SNAP)
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
|
newAction(translations["button.mark_as_read"], ACTION_MARK_AS_READ, {
|
||||||
|
betterNotificationFilter.contains("mark_as_read_button")
|
||||||
|
}) {}
|
||||||
|
|
||||||
notificationBuilder.setActions(*actions.toTypedArray())
|
notificationBuilder.setActions(*actions.toTypedArray())
|
||||||
notificationData.notification = notificationBuilder.build()
|
notificationData.notification = notificationBuilder.build()
|
||||||
}
|
}
|
||||||
@ -141,7 +147,6 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
val conversationId = intent.getStringExtra("conversation_id") ?: return@subscribe
|
val conversationId = intent.getStringExtra("conversation_id") ?: return@subscribe
|
||||||
val messageId = intent.getLongExtra("message_id", -1)
|
val messageId = intent.getLongExtra("message_id", -1)
|
||||||
val notificationId = intent.getIntExtra("notification_id", -1)
|
val notificationId = intent.getIntExtra("notification_id", -1)
|
||||||
val notificationManager = event.androidContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
|
|
||||||
val updateNotification: (Int, (Notification) -> Unit) -> Unit = { id, notificationBuilder ->
|
val updateNotification: (Int, (Notification) -> Unit) -> Unit = { id, notificationBuilder ->
|
||||||
notificationManager.activeNotifications.firstOrNull { it.id == id }?.let {
|
notificationManager.activeNotifications.firstOrNull { it.id == id }?.let {
|
||||||
@ -177,6 +182,47 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
context.longToast(it)
|
context.longToast(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ACTION_MARK_AS_READ -> {
|
||||||
|
runCatching {
|
||||||
|
if (context.feature(StealthMode::class).canUseRule(conversationId)) {
|
||||||
|
context.longToast(translations["stealth_mode_notice"])
|
||||||
|
return@subscribe
|
||||||
|
}
|
||||||
|
|
||||||
|
val conversationManager = context.feature(Messaging::class).conversationManager ?: return@subscribe
|
||||||
|
|
||||||
|
context.classCache.conversationManager.methods.first { it.name == "displayedMessages"}?.invoke(
|
||||||
|
conversationManager,
|
||||||
|
SnapUUID.fromString(conversationId).instanceNonNull(),
|
||||||
|
messageId,
|
||||||
|
CallbackBuilder(context.mappings.getMappedClass("callbacks", "Callback"))
|
||||||
|
.override("onError") {
|
||||||
|
context.log.error("Failed to mark message as read: ${it.arg(0) as Any}")
|
||||||
|
context.shortToast("Failed to mark message as read")
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
val conversationMessage = context.database.getConversationMessageFromId(messageId) ?: return@subscribe
|
||||||
|
|
||||||
|
if (conversationMessage.contentType == ContentType.SNAP.id) {
|
||||||
|
context.classCache.conversationManager.methods.first { it.name == "updateMessage"}?.invoke(
|
||||||
|
conversationManager,
|
||||||
|
SnapUUID.fromString(conversationId).instanceNonNull(),
|
||||||
|
messageId,
|
||||||
|
context.classCache.messageUpdateEnum.enumConstants.first { it.toString() == "READ" },
|
||||||
|
CallbackBuilder(context.mappings.getMappedClass("callbacks", "Callback"))
|
||||||
|
.override("onError") {
|
||||||
|
context.log.error("Failed to open snap: ${it.arg(0) as Any}")
|
||||||
|
context.shortToast("Failed to open snap")
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
context.log.error("Failed to mark message as read", it)
|
||||||
|
context.shortToast("Failed to mark message as read. Check logs for more details")
|
||||||
|
}
|
||||||
|
notificationManager.cancel(notificationId)
|
||||||
|
}
|
||||||
else -> return@subscribe
|
else -> return@subscribe
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +328,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
sendNotificationData(notificationData.copy(notification = notificationBuilder.build()), true)
|
sendNotificationData(notificationData.copy(notification = notificationBuilder.build()), true)
|
||||||
return@onEach
|
return@onEach
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
CoreLogger.xposedLog("Failed to send preview notification", it)
|
context.log.error("Failed to send preview notification", it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +348,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
|
|
||||||
val fetchConversationWithMessagesCallback = context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback")
|
val fetchConversationWithMessagesCallback = context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback")
|
||||||
|
|
||||||
Hooker.hook(notifyAsUserMethod, HookStage.BEFORE) { param ->
|
notifyAsUserMethod.hook(HookStage.BEFORE) { param ->
|
||||||
val notificationData = NotificationData(param.argNullable(0), param.arg(1), param.arg(2), param.arg(3))
|
val notificationData = NotificationData(param.argNullable(0), param.arg(1), param.arg(2), param.arg(3))
|
||||||
|
|
||||||
val extras: Bundle = notificationData.notification.extras.getBundle("system_notification_extras")?: return@hook
|
val extras: Bundle = notificationData.notification.extras.getBundle("system_notification_extras")?: return@hook
|
||||||
|
Reference in New Issue
Block a user