diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/AutoOpenSnaps.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/AutoOpenSnaps.kt index f413b786..1cf39601 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/AutoOpenSnaps.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/AutoOpenSnaps.kt @@ -54,6 +54,7 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A private const val NOTIFICATION_GROUP_KEY = "purrfectsnap.AUTO_OPEN" private const val PREF_TOTAL_OPENED = "auto_open_total_opened" private const val PREF_SESSION_START = "auto_open_session_start" + private const val PREF_PROCESSED_IDS = "auto_open_processed_ids" private const val LAZY_SAVE_INTERVAL_MS = 600_000L } @@ -78,6 +79,12 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A private val prefs by lazy { this@AutoOpenSnaps.context.androidContext.getSharedPreferences("me.eternal.purrfectsnap_preferences", Context.MODE_PRIVATE) } private val messaging by lazy { this@AutoOpenSnaps.context.feature(Messaging::class) } private var wakeLock: PowerManager.WakeLock? = null + private var wakeLockCooldownJob: Job? = null + + // Optimized Metadata Cache: 500 entries limit to prevent OOM crashes + private val metadataCache = Collections.synchronizedMap(object : LinkedHashMap(100, 0.75f, true) { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry?): Boolean = size > 500 + }) private var currentStatusText = "Monitoring..." private var currentSpeedText = "Full Speed" @@ -110,7 +117,6 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A } override fun init() { - restorePersistence() createNotificationChannels() // NATIVE HOOKS: Ensuring Snapchat never sees the app as "In Background" @@ -195,7 +201,7 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A sessionProcessed.incrementAndGet(); totalProcessed.incrementAndGet(); recordSpeedTimestamp() val duration = System.currentTimeMillis() - startTime averageProcessingTime.set((averageProcessingTime.get() * 0.7 + duration * 0.3).toLong()) - triggerLazySave(); break + break } delay((autoOpenConfig.retryDelay as PropertyValue).get().toLong()) } @@ -259,7 +265,18 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A val contentType = message.messageContent?.contentType if (contentType != ContentType.SNAP && contentType != ContentType.EXTERNAL_MEDIA) return@subscribe + + // Prevent processing of snaps already viewed manually by the local user + if (message.messageMetadata?.openedBy?.any { it.toString() == this@AutoOpenSnaps.context.database.myUserId } == true) { + openedSnapsIds.add(clientMessageId) + return@subscribe + } + if (!canUseRule(conversationId)) return@subscribe + + val currentQueueSize = synchronized(queuedSnaps) { queuedSnaps.size } + if (currentQueueSize >= (autoOpenConfig.queueSize as PropertyValue).get()) return@subscribe + if (openedSnapsIds.contains(clientMessageId)) return@subscribe openedSnapsIds.add(clientMessageId) @@ -269,32 +286,10 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A synchronized(queuedSnaps) { queuedSnaps.add(item) } snapChannel.trySend(item) - acquireWakeLock(); updateStatusNotification(); triggerLazySave() + acquireWakeLock(); updateStatusNotification() } } - private fun triggerLazySave() { - needsSaving.set(true) - if (isSaving.compareAndSet(false, true)) { - this@AutoOpenSnaps.context.coroutineScope.launch(Dispatchers.IO) { - while (needsSaving.get() && engineActive.get()) { - needsSaving.set(false); saveQueueToDisk(); delay(LAZY_SAVE_INTERVAL_MS) - } - isSaving.set(false) - } - } - } - - private fun saveQueueToDisk() { - prefs.edit { putInt(PREF_TOTAL_OPENED, totalProcessed.get()); putLong(PREF_SESSION_START, sessionStartTime.get()) } - } - - private fun restorePersistence() { - val savedStartTime = prefs.getLong(PREF_SESSION_START, 0) - if (System.currentTimeMillis() - savedStartTime > 21600000) return - totalProcessed.set(prefs.getInt(PREF_TOTAL_OPENED, 0)); sessionStartTime.set(savedStartTime) - } - private fun isWifiConnected(): Boolean { val cm = this@AutoOpenSnaps.context.androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager return cm.getNetworkCapabilities(cm.activeNetwork)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true @@ -359,7 +354,11 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A val isCompact = (autoOpenConfig.compactNotification as PropertyValue).get() if (isWorking) { builder.setContentText("Opened: $processed │ Queue: $remaining ($progressPercent%)") - builder.setSubText("Speed: ${String.format(Locale.US, "%.1f", speed)}/s • Ends in: $eta") + if (isCompact) { + builder.setSubText("Speed: ${String.format(Locale.US, "%.1f", speed)}/s • Ends in: $eta") + } else { + builder.setSubText("") + } builder.setProgress(sessionTotal, processed, false) } else { builder.setContentText("$processed Opened Today │ $total Total") @@ -383,10 +382,10 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A } val speedNotion = if (isWorking) currentSpeedText else "Idle" val speedValue = "${String.format(Locale.US, "%.1f", speed)}/s" - append("└─ Speed: $speedNotion ($speedValue)\n") + append("└─ Speed: $speedNotion ($speedValue)") if ((autoOpenConfig.showQueuePreview as PropertyValue).get()) { - append("\nQUEUE PREVIEW\n") + append("\n\nQUEUE PREVIEW\n") if (isWorking && remaining > 0) { recentSnaps.reversed().forEach { item -> append("• ${item.senderName} │ ${item.conversationType} (${item.contentType})\n")