From 23a5e9e66bc7d034edd9da4020efa2d7e44f530e Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sun, 19 Feb 2023 12:40:19 -0500 Subject: [PATCH] Memory Pruning: Only keeping 1000 messages per channel --- .../vitorpamplona/amethyst/ServiceManager.kt | 9 ++++++++ .../vitorpamplona/amethyst/model/Channel.kt | 18 +++++++++++++++ .../amethyst/model/LocalCache.kt | 23 +++++++++++++++++++ .../com/vitorpamplona/amethyst/model/User.kt | 1 + .../service/NostrChatroomListDataSource.kt | 4 ++-- .../vitorpamplona/amethyst/ui/MainActivity.kt | 12 ++++++++++ 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt index 1034baea2..d07c774ec 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt @@ -1,6 +1,7 @@ package com.vitorpamplona.amethyst import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.relays.Constants import com.vitorpamplona.amethyst.service.NostrAccountDataSource import com.vitorpamplona.amethyst.service.NostrChannelDataSource @@ -66,4 +67,12 @@ object ServiceManager { Client.disconnect() } + fun cleanUp() { + LocalCache.cleanObservers() + + account?.let { + LocalCache.pruneOldAndHiddenMessages(it) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt index c8d03634d..e3303db2e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent +import com.vitorpamplona.amethyst.ui.dal.ChannelFeedFilter import com.vitorpamplona.amethyst.ui.note.toShortenHex import fr.acinq.secp256k1.Hex import java.util.concurrent.ConcurrentHashMap @@ -53,6 +54,23 @@ class Channel(val idHex: String) { private fun refreshObservers() { live.refresh() } + + fun pruneOldAndHiddenMessages(account: Account): Set { + val important = notes.values + .filter { it.author?.let { it1 -> account.isHidden(it1) } == false } + .sortedBy { it.event?.createdAt } + .reversed() + .take(1000) + .toSet() + + val toBeRemoved = notes.values.filter { it in important }.toSet() + + toBeRemoved.forEach { + notes.remove(it.idHex) + } + + return toBeRemoved + } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 2e130292d..bedc87377 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -544,6 +544,29 @@ object LocalCache { } } + fun cleanObservers() { + notes.forEach { + it.value.clearLive() + } + + users.forEach { + it.value.clearLive() + } + } + + fun pruneOldAndHiddenMessages(account: Account) { + channels.forEach { + val toBeRemoved = it.value.pruneOldAndHiddenMessages(account) + + toBeRemoved.forEach { + notes.remove(it.idHex) + // Doesn't need to clean up the replies and mentions.. Too small to matter. + } + + println("PRUNE: ${toBeRemoved.size} messages removed from ${it.value.info.name}") + } + } + // Observers line up here. val live: LocalCacheLiveData = LocalCacheLiveData(this) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt index c51044d93..53f705db0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt @@ -363,3 +363,4 @@ class UserLiveData(val user: User): LiveData(UserState(user)) { } class UserState(val user: User) + diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt index ead2962fe..7a9b7b841 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt @@ -38,7 +38,7 @@ object NostrChatroomListDataSource: NostrDataSource("MailBoxFeed") { ) fun createMyChannelsFilter() = TypedFilter( - types = setOf(FeedType.PUBLIC_CHATS), + types = FeedType.values().toSet(), // Metadata comes from any relay filter = JsonFilter( kinds = listOf(ChannelCreateEvent.kind), ids = account.followingChannels.toList() @@ -48,7 +48,7 @@ object NostrChatroomListDataSource: NostrDataSource("MailBoxFeed") { fun createLastChannelInfoFilter(): List { return account.followingChannels.map { TypedFilter( - types = setOf(FeedType.PUBLIC_CHATS), + types = FeedType.values().toSet(), // Metadata comes from any relay filter = JsonFilter( kinds = listOf(ChannelMetadataEvent.kind), tags = mapOf("e" to listOf(it)), diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index 845d0f129..20d6b04ce 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -1,5 +1,6 @@ package com.vitorpamplona.amethyst.ui +import android.content.ComponentCallbacks2 import android.net.Uri import android.os.Build.VERSION.SDK_INT import android.os.Bundle @@ -22,6 +23,7 @@ import coil.util.DebugLogger import com.vitorpamplona.amethyst.EncryptedStorage import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.ServiceManager +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.decodePublicKey import com.vitorpamplona.amethyst.model.toHexKey import com.vitorpamplona.amethyst.service.Nip19 @@ -85,4 +87,14 @@ class MainActivity : ComponentActivity() { super.onPause() } + + /** + * Release memory when the UI becomes hidden or when system resources become low. + * @param level the memory-related event that was raised. + */ + override fun onTrimMemory(level: Int) { + super.onTrimMemory(level) + println("Trim Memory $level") + ServiceManager.cleanUp() + } }