diff --git a/app/src/main/java/com/bitchat/android/MainActivity.kt b/app/src/main/java/com/bitchat/android/MainActivity.kt index bfe57ca5..2e428a28 100644 --- a/app/src/main/java/com/bitchat/android/MainActivity.kt +++ b/app/src/main/java/com/bitchat/android/MainActivity.kt @@ -11,11 +11,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.lifecycle.ViewModelProvider +import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.Lifecycle import com.bitchat.android.mesh.BluetoothMeshService @@ -60,16 +62,13 @@ class MainActivity : ComponentActivity() { } } - - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // Initialize core mesh service first - meshService = BluetoothMeshService(this) - // Initialize permission management permissionManager = PermissionManager(this) + // Initialize core mesh service first + meshService = BluetoothMeshService(this) bluetoothStatusManager = BluetoothStatusManager( activity = this, context = this, @@ -124,6 +123,7 @@ class MainActivity : ComponentActivity() { @Composable private fun OnboardingFlowScreen() { + val context = LocalContext.current val onboardingState by mainViewModel.onboardingState.collectAsState() val bluetoothStatus by mainViewModel.bluetoothStatus.collectAsState() val locationStatus by mainViewModel.locationStatus.collectAsState() @@ -132,7 +132,29 @@ class MainActivity : ComponentActivity() { val isBluetoothLoading by mainViewModel.isBluetoothLoading.collectAsState() val isLocationLoading by mainViewModel.isLocationLoading.collectAsState() val isBatteryOptimizationLoading by mainViewModel.isBatteryOptimizationLoading.collectAsState() - + + DisposableEffect(context, bluetoothStatusManager) { + + val receiver = bluetoothStatusManager.monitorBluetoothState( + context = context, + bluetoothStatusManager = bluetoothStatusManager, + onBluetoothStateChanged = { status -> + if (status == BluetoothStatus.ENABLED && onboardingState == OnboardingState.BLUETOOTH_CHECK) { + checkBluetoothAndProceed() + } + } + ) + + onDispose { + try { + context.unregisterReceiver(receiver) + Log.d("BluetoothStatusUI", "BroadcastReceiver unregistered") + } catch (e: IllegalStateException) { + Log.w("BluetoothStatusUI", "Receiver was not registered") + } + } + } + when (onboardingState) { OnboardingState.CHECKING -> { InitializingScreen() @@ -209,7 +231,7 @@ class MainActivity : ComponentActivity() { // Let ChatViewModel handle navigation state val handled = chatViewModel.handleBackPressed() if (!handled) { - // If ChatViewModel doesn't handle it, disable this callback + // If ChatViewModel doesn't handle it, disable this callback // and let the system handle it (which will exit the app) this.isEnabled = false onBackPressedDispatcher.onBackPressed() @@ -217,10 +239,9 @@ class MainActivity : ComponentActivity() { } } } - + // Add the callback - this will be automatically removed when the activity is destroyed onBackPressedDispatcher.addCallback(this, backCallback) - ChatScreen(viewModel = chatViewModel) } @@ -568,7 +589,7 @@ class MainActivity : ComponentActivity() { handleOnboardingFailed("Some permissions were revoked. Please grant all permissions to continue.") return@launch } - + // Set up mesh service delegate and start services meshService.delegate = chatViewModel meshService.startServices() @@ -626,7 +647,7 @@ class MainActivity : ComponentActivity() { } } - override fun onPause() { + override fun onPause() { super.onPause() // Only set background state if app is fully initialized if (mainViewModel.onboardingState.value == OnboardingState.COMPLETE) { @@ -661,6 +682,7 @@ class MainActivity : ComponentActivity() { } } + override fun onDestroy() { super.onDestroy() diff --git a/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt index bec42f17..390df992 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt @@ -144,18 +144,20 @@ private fun BluetoothDisabledContent( ) } - OutlinedButton( - onClick = onRetry, - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = "Check Again", - style = MaterialTheme.typography.bodyMedium.copy( - fontFamily = FontFamily.Monospace - ), - modifier = Modifier.padding(vertical = 4.dp) - ) - } + //Since we are automatically checking bluetooth state -- commented + +// OutlinedButton( +// onClick = onRetry, +// modifier = Modifier.fillMaxWidth() +// ) { +// Text( +// text = "Check Again", +// style = MaterialTheme.typography.bodyMedium.copy( +// fontFamily = FontFamily.Monospace +// ), +// modifier = Modifier.padding(vertical = 4.dp) +// ) +// } } } } diff --git a/app/src/main/java/com/bitchat/android/onboarding/BluetoothStatusManager.kt b/app/src/main/java/com/bitchat/android/onboarding/BluetoothStatusManager.kt index 54c00ecc..67997af5 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/BluetoothStatusManager.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/BluetoothStatusManager.kt @@ -2,12 +2,15 @@ package com.bitchat.android.onboarding import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat /** * Manages Bluetooth enable/disable state and user prompts @@ -203,6 +206,59 @@ class BluetoothStatusManager( fun logBluetoothStatus() { Log.d(TAG, getDiagnostics()) } + + /** + * Monitors Bluetooth state changes in real-time and invokes the provided callback. + * Returns the BroadcastReceiver for manual un-registration. + */ + fun monitorBluetoothState( + context: Context, + bluetoothStatusManager: BluetoothStatusManager, + onBluetoothStateChanged: (BluetoothStatus) -> Unit + ): BroadcastReceiver { + + Log.d(TAG, "Starting Bluetooth State Monitoring") + + if (!bluetoothStatusManager.isBluetoothSupported()) { + Log.e(TAG, "Bluetooth is not supported") + onBluetoothStateChanged(BluetoothStatus.NOT_SUPPORTED) + bluetoothStatusManager.handleBluetoothStatus(BluetoothStatus.NOT_SUPPORTED) + return object : BroadcastReceiver() { override fun onReceive(p0: Context?, p1: Intent?) {} + } + } + + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { + BluetoothAdapter.STATE_ON -> { + Log.d(TAG, "Bluetooth turned ON") + onBluetoothStateChanged(BluetoothStatus.ENABLED) + bluetoothStatusManager.handleBluetoothStatus(BluetoothStatus.ENABLED) + } + BluetoothAdapter.STATE_OFF -> { + Log.d(TAG, "Bluetooth turned OFF") + onBluetoothStateChanged(BluetoothStatus.DISABLED) + bluetoothStatusManager.onBluetoothDisabled("User has turned off their Blue") + } + BluetoothAdapter.STATE_TURNING_ON, BluetoothAdapter.STATE_OFF -> { + Log.d(TAG, "Bluetooth state transitioning: ${bluetoothStatusManager.getAdapterStateName(intent.getIntExtra( + BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR))}") + } + } + } + } + + //Register the receiver + val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) + ContextCompat.registerReceiver(context, receiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED) + + //Check initial Bluetooth state + val initialStatus = bluetoothStatusManager.checkBluetoothStatus() + onBluetoothStateChanged(initialStatus) + + + return receiver + } } /**