Background persistence (#505)

* persistence step 1

* fix build

* messages in the background work, notifications not yet

* app state store

* DM icon shows up

* notification launches when app is closed!

* keep ui updated

* lifecycle fixes

* extensive logging, maybe revert later

* send nickname in announcement

* quit in notification

* setting in about sheet

* fix quit bitchat

* lifecycle fixes

* power mode based on background state

* stats for both direciotns

* fix graph persistence

* better counting

* count per device

* only compute when debug sheet is open? untested

* fix read receipts

* fix read receipts fully

* fix unread badge if messages have been read in focus

* foreground promotion fix

* fix app kill in notification

* adjust to new tor

* nice

* about sheet design
This commit is contained in:
callebtc
2025-12-13 16:43:39 +07:00
committed by GitHub
parent e96330e50b
commit 3f8c236a72
24 changed files with 2079 additions and 575 deletions
@@ -51,7 +51,7 @@ class MainActivity : OrientationAwareActivity() {
private lateinit var locationStatusManager: LocationStatusManager
private lateinit var batteryOptimizationManager: BatteryOptimizationManager
// Core mesh service - managed at app level
// Core mesh service - provided by the foreground service holder
private lateinit var meshService: BluetoothMeshService
private val mainViewModel: MainViewModel by viewModels()
private val chatViewModel: ChatViewModel by viewModels {
@@ -66,13 +66,21 @@ class MainActivity : OrientationAwareActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Check if this is a quit request from the notification
if (intent.getBooleanExtra("ACTION_QUIT_APP", false)) {
android.util.Log.d("MainActivity", "Quit request received in onCreate, finishing activity")
finish()
return
}
// Enable edge-to-edge display for modern Android look
enableEdgeToEdge()
// Initialize permission management
permissionManager = PermissionManager(this)
// Initialize core mesh service first
meshService = BluetoothMeshService(this)
// Ensure foreground service is running and get mesh instance from holder
try { com.bitchat.android.service.MeshForegroundService.start(applicationContext) } catch (_: Exception) { }
meshService = com.bitchat.android.service.MeshServiceHolder.getOrCreate(applicationContext)
bluetoothStatusManager = BluetoothStatusManager(
activity = this,
context = this,
@@ -630,6 +638,15 @@ class MainActivity : OrientationAwareActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
// Check if this is a quit request from the notification
if (intent.getBooleanExtra("ACTION_QUIT_APP", false)) {
android.util.Log.d("MainActivity", "Quit request received, finishing activity")
finish()
return
}
// Handle notification intents when app is already running
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
handleNotificationIntent(intent)
@@ -640,6 +657,8 @@ class MainActivity : OrientationAwareActivity() {
super.onResume()
// Check Bluetooth and Location status on resume and handle accordingly
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
// Reattach mesh delegate to new ChatViewModel instance after Activity recreation
try { meshService.delegate = chatViewModel } catch (_: Exception) { }
// Set app foreground state
meshService.connectionManager.setAppBackgroundState(false)
chatViewModel.setAppBackgroundState(false)
@@ -665,13 +684,15 @@ class MainActivity : OrientationAwareActivity() {
}
}
override fun onPause() {
override fun onPause() {
super.onPause()
// Only set background state if app is fully initialized
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
// Set app background state
meshService.connectionManager.setAppBackgroundState(true)
chatViewModel.setAppBackgroundState(true)
// Detach UI delegate so the foreground service can own DM notifications while UI is closed
try { meshService.delegate = null } catch (_: Exception) { }
}
}
@@ -746,14 +767,6 @@ class MainActivity : OrientationAwareActivity() {
Log.w("MainActivity", "Error cleaning up location status manager: ${e.message}")
}
// Stop mesh services if app was fully initialized
if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) {
try {
meshService.stopServices()
Log.d("MainActivity", "Mesh services stopped successfully")
} catch (e: Exception) {
Log.w("MainActivity", "Error stopping mesh services in onDestroy: ${e.message}")
}
}
// Do not stop mesh here; ForegroundService owns lifecycle for background reliability
}
}