fix(mesh): address memory leaks and background persistence issues in Wi-Fi Aware

- Move message persistence and background notifications for Wi-Fi Aware into the service layer via MeshCore hooks.
- Fix memory leak in MainActivity by properly detaching WifiAwareMeshDelegate on pause.
- Ensure BluetoothMeshService is initialized before WifiAwareMeshService to guarantee shared GossipSyncManager instance.
- Deduplicate persistence logic from MainActivity delegate.
This commit is contained in:
callebtc
2026-01-10 22:45:23 +07:00
parent 1d54c30056
commit 689f3db1f9
2 changed files with 80 additions and 38 deletions
@@ -39,6 +39,8 @@ import com.bitchat.android.ui.ChatScreen
import com.bitchat.android.ui.ChatViewModel
import com.bitchat.android.ui.OrientationAwareActivity
import com.bitchat.android.ui.theme.BitchatTheme
import com.bitchat.android.wifiaware.WifiAwareController
import com.bitchat.android.wifiaware.WifiAwareMeshDelegate
import com.bitchat.android.nostr.PoWPreferenceManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -62,6 +64,37 @@ class MainActivity : OrientationAwareActivity() {
}
}
}
private val wifiAwareDelegate by lazy {
object : WifiAwareMeshDelegate {
override fun didReceiveMessage(message: com.bitchat.android.model.BitchatMessage) {
chatViewModel.didReceiveMessage(message)
}
override fun didUpdatePeerList(peers: List<String>) {
chatViewModel.onWifiPeersUpdated(peers)
}
override fun didReceiveChannelLeave(channel: String, fromPeer: String) {
chatViewModel.didReceiveChannelLeave(channel, fromPeer)
}
override fun didReceiveDeliveryAck(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveDeliveryAck(messageID, recipientPeerID)
}
override fun didReceiveReadReceipt(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveReadReceipt(messageID, recipientPeerID)
}
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
return chatViewModel.decryptChannelMessage(encryptedContent, channel)
}
override fun getNickname(): String? {
return chatViewModel.getNickname()
}
override fun isFavorite(peerID: String): Boolean {
return try {
com.bitchat.android.favorites.FavoritesPersistenceService.shared.getFavoriteStatus(peerID)?.isMutual == true
} catch (_: Exception) { false }
}
}
}
private val forceFinishReceiver = object : android.content.BroadcastReceiver() {
override fun onReceive(context: android.content.Context, intent: android.content.Intent) {
@@ -166,44 +199,10 @@ class MainActivity : OrientationAwareActivity() {
// Bridge WiFi Aware callbacks into ChatViewModel (reusing BLE delegate methods)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
com.bitchat.android.wifiaware.WifiAwareController.running.collect { running ->
val svc = com.bitchat.android.wifiaware.WifiAwareController.getService()
if (running && svc != null) {
svc.delegate = object : com.bitchat.android.wifiaware.WifiAwareMeshDelegate {
override fun didReceiveMessage(message: com.bitchat.android.model.BitchatMessage) {
if (message.isPrivate) {
message.senderPeerID?.let { pid -> com.bitchat.android.services.AppStateStore.addPrivateMessage(pid, message) }
} else if (message.channel != null) {
com.bitchat.android.services.AppStateStore.addChannelMessage(message.channel, message)
} else {
com.bitchat.android.services.AppStateStore.addPublicMessage(message)
}
chatViewModel.didReceiveMessage(message)
}
override fun didUpdatePeerList(peers: List<String>) {
chatViewModel.onWifiPeersUpdated(peers)
}
override fun didReceiveChannelLeave(channel: String, fromPeer: String) {
chatViewModel.didReceiveChannelLeave(channel, fromPeer)
}
override fun didReceiveDeliveryAck(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveDeliveryAck(messageID, recipientPeerID)
}
override fun didReceiveReadReceipt(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveReadReceipt(messageID, recipientPeerID)
}
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
return chatViewModel.decryptChannelMessage(encryptedContent, channel)
}
override fun getNickname(): String? {
return chatViewModel.getNickname()
}
override fun isFavorite(peerID: String): Boolean {
return try {
com.bitchat.android.favorites.FavoritesPersistenceService.shared.getFavoriteStatus(peerID)?.isMutual == true
} catch (_: Exception) { false }
}
}
WifiAwareController.running.collect { running ->
val svc = WifiAwareController.getService()
if (running && svc != null && lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
svc.delegate = wifiAwareDelegate
}
}
}
@@ -761,6 +760,7 @@ class MainActivity : OrientationAwareActivity() {
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
// Reattach mesh delegate to new ChatViewModel instance after Activity recreation
try { meshService.delegate = chatViewModel } catch (_: Exception) { }
try { WifiAwareController.getService()?.delegate = wifiAwareDelegate } catch (_: Exception) { }
// Set app foreground state
meshService.connectionManager.setAppBackgroundState(false)
chatViewModel.setAppBackgroundState(false)
@@ -798,6 +798,7 @@ class MainActivity : OrientationAwareActivity() {
chatViewModel.setAppBackgroundState(true)
// Detach UI delegate so the foreground service can own DM notifications while UI is closed
try { meshService.delegate = null } catch (_: Exception) { }
try { WifiAwareController.getService()?.delegate = null } catch (_: Exception) { }
}
}