feat(experimental): auto open snaps

This commit is contained in:
rhunk 2024-04-17 23:10:22 +02:00
parent 785fb49aa9
commit 68c61a0b73
4 changed files with 137 additions and 0 deletions

View File

@ -199,6 +199,14 @@
"whitelist": "Unsaveable Messages"
}
},
"auto_open_snaps": {
"name": "Auto Open Snaps",
"description": "Automatically opens Snaps when receiving them",
"options": {
"blacklist": "Exclude from Auto Open Snaps",
"whitelist": "Auto Open Snaps"
}
},
"hide_friend_feed": {
"name": "Hide from Friend Feed"
},
@ -945,6 +953,7 @@
"auto_download": "\u2B07\uFE0F Auto Download",
"auto_save": "\uD83D\uDCAC Auto Save Messages",
"unsaveable_messages": "\u2B07\uFE0F Unsaveable Messages",
"auto_open_snaps": "\uD83D\uDCF7 Auto Open Snaps",
"stealth": "\uD83D\uDC7B Stealth Mode",
"mark_snaps_as_seen": "\uD83D\uDC40 Mark Snaps as seen",
"mark_stories_as_seen_locally": "\uD83D\uDC40 Mark Stories as seen locally",
@ -1357,6 +1366,11 @@
"incoming_secret_message": "Your friend just accepted your public key. Click below to accept the secret."
},
"auto_open_snaps": {
"title": "Auto Open Snaps",
"notification_content": "{count} Snaps opened"
},
"suspend_location_updates": {
"switch_text": "Suspend Location Updates"
},

View File

@ -45,6 +45,7 @@ enum class MessagingRuleType(
STEALTH("stealth", true),
AUTO_DOWNLOAD("auto_download", true),
AUTO_SAVE("auto_save", true, defaultValue = "blacklist"),
AUTO_OPEN_SNAPS("auto_open_snaps", true, configNotices = arrayOf(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE), defaultValue = null),
UNSAVEABLE_MESSAGES("unsaveable_messages", true, configNotices = arrayOf(FeatureNotice.REQUIRE_NATIVE_HOOKS), defaultValue = null),
HIDE_FRIEND_FEED("hide_friend_feed", false, showInFriendMenu = false),
E2E_ENCRYPTION("e2e_encryption", false),

View File

@ -128,6 +128,7 @@ class FeatureManager(
BetterLocation(),
MediaFilePicker(),
HideActiveMusic(),
AutoOpenSnaps(),
)
initializeFeatures()

View File

@ -0,0 +1,121 @@
package me.rhunk.snapenhance.core.features.impl.experiments
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.data.MessageUpdate
import me.rhunk.snapenhance.common.data.MessagingRuleType
import me.rhunk.snapenhance.core.event.events.impl.BuildMessageEvent
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.features.MessagingRuleFeature
import me.rhunk.snapenhance.core.features.impl.messaging.Messaging
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlin.random.Random
class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.AUTO_OPEN_SNAPS, loadParams = FeatureLoadParams.INIT_SYNC) {
private val snapQueue = MutableSharedFlow<Pair<String, Long>>()
private var snapQueueSize = AtomicInteger(0)
private val openedSnaps = mutableListOf<Long>()
private val notificationManager by lazy {
context.androidContext.getSystemService(NotificationManager::class.java)
}
private val notificationId by lazy { Random.nextInt() }
private val channelId by lazy {
"auto_open_snaps".also {
notificationManager.createNotificationChannel(
NotificationChannel(it, context.translation["auto_open_snaps.title"], NotificationManager.IMPORTANCE_LOW)
)
}
}
private fun sendStatusNotification(count: Int) {
notificationManager.notify(
notificationId,
Notification.Builder(context.androidContext, channelId)
.setContentTitle(context.translation["auto_open_snaps.title"])
.setContentText(context.translation.format("auto_open_snaps.notification_content", "count" to count.toString()))
.setSmallIcon(android.R.drawable.ic_menu_view)
.setProgress(0, 0, true)
.build().apply {
flags = flags or Notification.FLAG_ONLY_ALERT_ONCE
}
)
}
override fun init() {
if (getRuleState() == null) return
val messaging = context.feature(Messaging::class)
context.coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
snapQueue.collect { (conversationId, messageId) ->
snapQueueSize.addAndGet(-1)
delay(Random.nextLong(50, 100))
var result: String?
for (i in 0..5) {
while (context.isMainActivityPaused || messaging.conversationManager == null) {
delay(2000)
}
result = suspendCoroutine { continuation ->
runCatching {
messaging.conversationManager?.updateMessage(conversationId, messageId, MessageUpdate.READ) { result ->
continuation.resume(result)
}
}.getOrNull() ?: continuation.resume("ConversationManager is null")
}
if (result != null && result != "DUPLICATEREQUEST") {
context.log.warn("Failed to mark snap as read, retrying in 3 second")
delay(3000)
continue
}
break
}
if (snapQueueSize.get() <= 5) {
notificationManager.cancel(notificationId)
synchronized(openedSnaps) {
openedSnaps.clear()
}
} else {
sendStatusNotification(openedSnaps.size)
}
}
}
context.event.subscribe(BuildMessageEvent::class, priority = 103) { event ->
val conversationId = event.message.messageDescriptor?.conversationId?.toString() ?: return@subscribe
val clientMessageId = event.message.messageDescriptor?.messageId ?: return@subscribe
if (
event.message.messageContent?.contentType != ContentType.SNAP ||
event.message.messageMetadata?.openedBy?.any { it.toString() == context.database.myUserId } == true
) {
return@subscribe
}
context.coroutineScope.launch {
if (!canUseRule(conversationId)) return@launch
synchronized(openedSnaps) {
if (openedSnaps.contains(clientMessageId)) {
return@launch
}
openedSnaps.add(clientMessageId)
}
snapQueueSize.addAndGet(1)
snapQueue.emit(conversationId to clientMessageId)
}
}
}
}