commit 0e4667603a5130185f34222914bf719b0f23dd93
parent e7df5d1edfabb6dc624af1f8c1c0596d1e0b28c3
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Fri, 19 Apr 2024 00:33:25 +0200
feat(core): relationship notifier
Diffstat:
3 files changed, 69 insertions(+), 0 deletions(-)
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -538,6 +538,10 @@
"name": "Instant Delete",
"description": "Removes the confirmation dialog when deleting messages"
},
+ "relationship_notifier": {
+ "name": "Relationship Notifier",
+ "description": "Notifies you when someone removes you as a friend"
+ },
"better_notifications": {
"name": "Better Notifications",
"description": "Adds more information in received notifications"
@@ -1376,6 +1380,13 @@
"notification_content": "{count} Snaps opened"
},
+ "friend_mutation_observer": {
+ "notification_channel_name": "Friend Mutation Observer",
+ "removed_friend_notification_title": "Friend Removed",
+ "removed_friend_notification_content": "{username} has removed you as a friend",
+ "removed_friend_notification_content_with_display_name": "{displayName} ({username}) has removed you as a friend"
+ },
+
"suspend_location_updates": {
"switch_text": "Suspend Location Updates"
},
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
@@ -73,6 +73,7 @@ class MessagingTweaks : ConfigContainer() {
nativeHooks()
}
val instantDelete = boolean("instant_delete") { requireRestart() }
+ val relationshipNotifier = boolean("relationship_notifier") { requireRestart() }
val betterNotifications = multiple("better_notifications",
"chat_preview",
"media_preview",
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/FriendMutationObserver.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/FriendMutationObserver.kt
@@ -1,6 +1,12 @@
package me.rhunk.snapenhance.core.features.impl
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.WarningAmber
import com.google.gson.JsonObject
+import me.rhunk.snapenhance.common.data.FriendLinkType
import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
@@ -8,12 +14,46 @@ import me.rhunk.snapenhance.core.util.EvictingMap
import java.io.InputStreamReader
class FriendMutationObserver: Feature("FriendMutationObserver", loadParams = FeatureLoadParams.INIT_SYNC) {
+ private val translation by lazy { context.translation.getCategory("friend_mutation_observer") }
private val addSourceCache = EvictingMap<String, String>(500)
+ private val notificationManager get() = context.androidContext.getSystemService(NotificationManager::class.java)
+ private val channelId = "friend_mutation_observer".also {
+ notificationManager.createNotificationChannel(
+ NotificationChannel(
+ it,
+ translation["notification_channel_name"],
+ NotificationManager.IMPORTANCE_HIGH
+ )
+ )
+ }
+
fun getFriendAddSource(userId: String): String? {
return addSourceCache[userId]
}
+ private fun sendFriendRemoveNotification(displayName: String?, username: String) {
+ val contentText = (if (displayName != null)
+ translation.format("removed_friend_notification_content_with_display_name", "displayName" to displayName, "username" to username)
+ else translation.format("removed_friend_notification_content", "username" to username))
+
+ notificationManager.notify(System.nanoTime().toInt(),
+ Notification.Builder(context.androidContext, channelId)
+ .setSmallIcon(android.R.drawable.ic_dialog_alert)
+ .setContentTitle(translation["removed_friend_notification_title"])
+ .setContentText(contentText)
+ .setShowWhen(true)
+ .setWhen(System.currentTimeMillis())
+ .build()
+ )
+
+ context.inAppOverlay.showStatusToast(
+ Icons.Default.WarningAmber,
+ contentText,
+ durationMs = 6000
+ )
+ }
+
override fun init() {
context.event.subscribe(NetworkApiRequestEvent::class) { event ->
if (!event.url.contains("ami/friends")) return@subscribe
@@ -31,6 +71,23 @@ class FriendMutationObserver: Feature("FriendMutationObserver", loadParams = Fea
addSourceCache[userId] = it
}
}
+
+ if (context.config.messaging.relationshipNotifier.get()) {
+ jsonObject.getAsJsonArray("friends").map { it.asJsonObject }.forEach { friend ->
+ val userId = friend.get("user_id")?.asString
+ if (userId == context.database.myUserId) return@forEach
+ val direction = friend.get("direction")?.asString
+ if (direction != "OUTGOING") return@forEach
+
+ val databaseFriend = context.database.getFriendInfo(userId ?: return@forEach) ?: return@forEach
+ val mutableUsername = friend.get("mutable_username").asString
+ val databaseLinkType = FriendLinkType.fromValue(databaseFriend.friendLinkType)
+
+ if (databaseLinkType == FriendLinkType.MUTUAL && !friend.has("fidelius_info")) {
+ sendFriendRemoveNotification(databaseFriend.displayName, mutableUsername)
+ }
+ }
+ }
}.onFailure {
context.log.error("Failed to process friends", it)
}