mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 04:45:21 +00:00
check bluetooth
This commit is contained in:
@@ -28,14 +28,18 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
private lateinit var permissionManager: PermissionManager
|
private lateinit var permissionManager: PermissionManager
|
||||||
private lateinit var onboardingCoordinator: OnboardingCoordinator
|
private lateinit var onboardingCoordinator: OnboardingCoordinator
|
||||||
|
private lateinit var bluetoothStatusManager: BluetoothStatusManager
|
||||||
private val chatViewModel: ChatViewModel by viewModels()
|
private val chatViewModel: ChatViewModel by viewModels()
|
||||||
|
|
||||||
// UI state for onboarding flow
|
// UI state for onboarding flow
|
||||||
private var onboardingState by mutableStateOf(OnboardingState.CHECKING)
|
private var onboardingState by mutableStateOf(OnboardingState.CHECKING)
|
||||||
|
private var bluetoothStatus by mutableStateOf(BluetoothStatus.ENABLED)
|
||||||
private var errorMessage by mutableStateOf("")
|
private var errorMessage by mutableStateOf("")
|
||||||
|
private var isBluetoothLoading by mutableStateOf(false)
|
||||||
|
|
||||||
enum class OnboardingState {
|
enum class OnboardingState {
|
||||||
CHECKING,
|
CHECKING,
|
||||||
|
BLUETOOTH_CHECK,
|
||||||
PERMISSION_EXPLANATION,
|
PERMISSION_EXPLANATION,
|
||||||
PERMISSION_REQUESTING,
|
PERMISSION_REQUESTING,
|
||||||
INITIALIZING,
|
INITIALIZING,
|
||||||
@@ -48,6 +52,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
// Initialize permission management
|
// Initialize permission management
|
||||||
permissionManager = PermissionManager(this)
|
permissionManager = PermissionManager(this)
|
||||||
|
bluetoothStatusManager = BluetoothStatusManager(
|
||||||
|
activity = this,
|
||||||
|
context = this,
|
||||||
|
onBluetoothEnabled = ::handleBluetoothEnabled,
|
||||||
|
onBluetoothDisabled = ::handleBluetoothDisabled
|
||||||
|
)
|
||||||
onboardingCoordinator = OnboardingCoordinator(
|
onboardingCoordinator = OnboardingCoordinator(
|
||||||
activity = this,
|
activity = this,
|
||||||
permissionManager = permissionManager,
|
permissionManager = permissionManager,
|
||||||
@@ -77,6 +87,20 @@ class MainActivity : ComponentActivity() {
|
|||||||
InitializingScreen()
|
InitializingScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnboardingState.BLUETOOTH_CHECK -> {
|
||||||
|
BluetoothCheckScreen(
|
||||||
|
status = bluetoothStatus,
|
||||||
|
onEnableBluetooth = {
|
||||||
|
isBluetoothLoading = true
|
||||||
|
bluetoothStatusManager.requestEnableBluetooth()
|
||||||
|
},
|
||||||
|
onRetry = {
|
||||||
|
checkBluetoothAndProceed()
|
||||||
|
},
|
||||||
|
isLoading = isBluetoothLoading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
OnboardingState.PERMISSION_EXPLANATION -> {
|
OnboardingState.PERMISSION_EXPLANATION -> {
|
||||||
PermissionExplanationScreen(
|
PermissionExplanationScreen(
|
||||||
permissionCategories = permissionManager.getCategorizedPermissions(),
|
permissionCategories = permissionManager.getCategorizedPermissions(),
|
||||||
@@ -124,6 +148,49 @@ class MainActivity : ComponentActivity() {
|
|||||||
// Small delay to show the checking state
|
// Small delay to show the checking state
|
||||||
delay(500)
|
delay(500)
|
||||||
|
|
||||||
|
// First check Bluetooth status (always required)
|
||||||
|
checkBluetoothAndProceed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Bluetooth status and proceed with onboarding flow
|
||||||
|
*/
|
||||||
|
private fun checkBluetoothAndProceed() {
|
||||||
|
android.util.Log.d("MainActivity", "Checking Bluetooth status")
|
||||||
|
bluetoothStatusManager.logBluetoothStatus()
|
||||||
|
|
||||||
|
bluetoothStatus = bluetoothStatusManager.checkBluetoothStatus()
|
||||||
|
|
||||||
|
when (bluetoothStatus) {
|
||||||
|
BluetoothStatus.ENABLED -> {
|
||||||
|
// Bluetooth is enabled, proceed with permission/onboarding check
|
||||||
|
proceedWithPermissionCheck()
|
||||||
|
}
|
||||||
|
BluetoothStatus.DISABLED -> {
|
||||||
|
// Show Bluetooth enable screen
|
||||||
|
android.util.Log.d("MainActivity", "Bluetooth disabled, showing enable screen")
|
||||||
|
onboardingState = OnboardingState.BLUETOOTH_CHECK
|
||||||
|
isBluetoothLoading = false
|
||||||
|
}
|
||||||
|
BluetoothStatus.NOT_SUPPORTED -> {
|
||||||
|
// Device doesn't support Bluetooth
|
||||||
|
android.util.Log.e("MainActivity", "Bluetooth not supported")
|
||||||
|
onboardingState = OnboardingState.BLUETOOTH_CHECK
|
||||||
|
isBluetoothLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proceed with permission checking after Bluetooth is confirmed enabled
|
||||||
|
*/
|
||||||
|
private fun proceedWithPermissionCheck() {
|
||||||
|
android.util.Log.d("MainActivity", "Bluetooth enabled, checking permissions")
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
delay(200) // Small delay for smooth transition
|
||||||
|
|
||||||
if (permissionManager.isFirstTimeLaunch()) {
|
if (permissionManager.isFirstTimeLaunch()) {
|
||||||
android.util.Log.d("MainActivity", "First time launch, showing permission explanation")
|
android.util.Log.d("MainActivity", "First time launch, showing permission explanation")
|
||||||
onboardingState = OnboardingState.PERMISSION_EXPLANATION
|
onboardingState = OnboardingState.PERMISSION_EXPLANATION
|
||||||
@@ -138,6 +205,34 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Bluetooth enabled callback
|
||||||
|
*/
|
||||||
|
private fun handleBluetoothEnabled() {
|
||||||
|
android.util.Log.d("MainActivity", "Bluetooth enabled by user")
|
||||||
|
isBluetoothLoading = false
|
||||||
|
bluetoothStatus = BluetoothStatus.ENABLED
|
||||||
|
proceedWithPermissionCheck()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Bluetooth disabled callback
|
||||||
|
*/
|
||||||
|
private fun handleBluetoothDisabled(message: String) {
|
||||||
|
android.util.Log.w("MainActivity", "Bluetooth disabled or failed: $message")
|
||||||
|
isBluetoothLoading = false
|
||||||
|
bluetoothStatus = bluetoothStatusManager.checkBluetoothStatus()
|
||||||
|
|
||||||
|
if (bluetoothStatus == BluetoothStatus.NOT_SUPPORTED) {
|
||||||
|
// Show permanent error for unsupported devices
|
||||||
|
errorMessage = message
|
||||||
|
onboardingState = OnboardingState.ERROR
|
||||||
|
} else {
|
||||||
|
// Stay on Bluetooth check screen for retry
|
||||||
|
onboardingState = OnboardingState.BLUETOOTH_CHECK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleOnboardingComplete() {
|
private fun handleOnboardingComplete() {
|
||||||
android.util.Log.d("MainActivity", "Onboarding completed, initializing app")
|
android.util.Log.d("MainActivity", "Onboarding completed, initializing app")
|
||||||
onboardingState = OnboardingState.INITIALIZING
|
onboardingState = OnboardingState.INITIALIZING
|
||||||
@@ -198,9 +293,18 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
// Only set background state if app is fully initialized
|
// Check Bluetooth status on resume and handle accordingly
|
||||||
if (onboardingState == OnboardingState.COMPLETE) {
|
if (onboardingState == OnboardingState.COMPLETE) {
|
||||||
chatViewModel.setAppBackgroundState(false)
|
chatViewModel.setAppBackgroundState(false)
|
||||||
|
|
||||||
|
// Check if Bluetooth was disabled while app was backgrounded
|
||||||
|
val currentBluetoothStatus = bluetoothStatusManager.checkBluetoothStatus()
|
||||||
|
if (currentBluetoothStatus != BluetoothStatus.ENABLED) {
|
||||||
|
android.util.Log.w("MainActivity", "Bluetooth disabled while app was backgrounded")
|
||||||
|
bluetoothStatus = currentBluetoothStatus
|
||||||
|
onboardingState = OnboardingState.BLUETOOTH_CHECK
|
||||||
|
isBluetoothLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
package com.bitchat.android.onboarding
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen shown when checking Bluetooth status or requesting Bluetooth enable
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun BluetoothCheckScreen(
|
||||||
|
status: BluetoothStatus,
|
||||||
|
onEnableBluetooth: () -> Unit,
|
||||||
|
onRetry: () -> Unit,
|
||||||
|
isLoading: Boolean = false
|
||||||
|
) {
|
||||||
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(32.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
when (status) {
|
||||||
|
BluetoothStatus.DISABLED -> {
|
||||||
|
BluetoothDisabledContent(
|
||||||
|
onEnableBluetooth = onEnableBluetooth,
|
||||||
|
onRetry = onRetry,
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
isLoading = isLoading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BluetoothStatus.NOT_SUPPORTED -> {
|
||||||
|
BluetoothNotSupportedContent(
|
||||||
|
colorScheme = colorScheme
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BluetoothStatus.ENABLED -> {
|
||||||
|
BluetoothCheckingContent(
|
||||||
|
colorScheme = colorScheme
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BluetoothDisabledContent(
|
||||||
|
onEnableBluetooth: () -> Unit,
|
||||||
|
onRetry: () -> Unit,
|
||||||
|
colorScheme: ColorScheme,
|
||||||
|
isLoading: Boolean
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
// Bluetooth icon
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = Color(0xFFE3F2FD)
|
||||||
|
),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "📶",
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Bluetooth Required",
|
||||||
|
style = MaterialTheme.typography.headlineSmall.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = colorScheme.primary
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = colorScheme.surfaceVariant.copy(alpha = 0.3f)
|
||||||
|
),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "bitchat needs Bluetooth to:",
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = colorScheme.onSurface
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "• Discover nearby users\n" +
|
||||||
|
"• Create mesh network connections\n" +
|
||||||
|
"• Send and receive messages\n" +
|
||||||
|
"• Work without internet or servers",
|
||||||
|
style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
color = colorScheme.onSurface.copy(alpha = 0.8f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
BluetoothLoadingIndicator()
|
||||||
|
} else {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onEnableBluetooth,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color(0xFF2196F3) // Bluetooth blue
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Enable Bluetooth",
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
),
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onRetry,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Check Again",
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontFamily = FontFamily.Monospace
|
||||||
|
),
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BluetoothNotSupportedContent(
|
||||||
|
colorScheme: ColorScheme
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
// Error icon
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = Color(0xFFFFEBEE)
|
||||||
|
),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "❌",
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Bluetooth Not Supported",
|
||||||
|
style = MaterialTheme.typography.headlineSmall.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = colorScheme.error
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = colorScheme.errorContainer.copy(alpha = 0.1f)
|
||||||
|
),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "This device doesn't support Bluetooth Low Energy (BLE), which is required for bitchat to function.\n\nbitchat needs BLE to create mesh networks and communicate with nearby devices without internet.",
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
color = colorScheme.onSurface
|
||||||
|
),
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BluetoothCheckingContent(
|
||||||
|
colorScheme: ColorScheme
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(32.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "bitchat*",
|
||||||
|
style = MaterialTheme.typography.headlineLarge.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = colorScheme.primary
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
BluetoothLoadingIndicator()
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Checking Bluetooth status...",
|
||||||
|
style = MaterialTheme.typography.bodyLarge.copy(
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
color = colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BluetoothLoadingIndicator() {
|
||||||
|
// Animated rotation for the loading indicator
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "bluetooth_loading")
|
||||||
|
val rotationAngle by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 360f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(durationMillis = 2000, easing = LinearEasing),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
),
|
||||||
|
label = "rotation"
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(60.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.rotate(rotationAngle),
|
||||||
|
color = Color(0xFF2196F3), // Bluetooth blue
|
||||||
|
strokeWidth = 3.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package com.bitchat.android.onboarding
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
import android.bluetooth.BluetoothManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages Bluetooth enable/disable state and user prompts
|
||||||
|
* Checks Bluetooth status on every app startup
|
||||||
|
*/
|
||||||
|
class BluetoothStatusManager(
|
||||||
|
private val activity: ComponentActivity,
|
||||||
|
private val context: Context,
|
||||||
|
private val onBluetoothEnabled: () -> Unit,
|
||||||
|
private val onBluetoothDisabled: (String) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "BluetoothStatusManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var bluetoothEnableLauncher: ActivityResultLauncher<Intent>? = null
|
||||||
|
private var bluetoothAdapter: BluetoothAdapter? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
setupBluetoothAdapter()
|
||||||
|
setupBluetoothEnableLauncher()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Bluetooth adapter reference
|
||||||
|
*/
|
||||||
|
private fun setupBluetoothAdapter() {
|
||||||
|
try {
|
||||||
|
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||||
|
bluetoothAdapter = bluetoothManager.adapter
|
||||||
|
Log.d(TAG, "Bluetooth adapter initialized: ${bluetoothAdapter != null}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to initialize Bluetooth adapter", e)
|
||||||
|
bluetoothAdapter = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup launcher for Bluetooth enable request
|
||||||
|
*/
|
||||||
|
private fun setupBluetoothEnableLauncher() {
|
||||||
|
bluetoothEnableLauncher = activity.registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
val isEnabled = bluetoothAdapter?.isEnabled == true
|
||||||
|
Log.d(TAG, "Bluetooth enable request result: $isEnabled (result code: ${result.resultCode})")
|
||||||
|
if (isEnabled) {
|
||||||
|
onBluetoothEnabled()
|
||||||
|
} else {
|
||||||
|
onBluetoothDisabled("Bluetooth is required for bitchat to discover and connect to nearby users. Please enable Bluetooth to continue.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Bluetooth is supported on this device
|
||||||
|
*/
|
||||||
|
fun isBluetoothSupported(): Boolean {
|
||||||
|
return bluetoothAdapter != null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Bluetooth is currently enabled
|
||||||
|
*/
|
||||||
|
fun isBluetoothEnabled(): Boolean {
|
||||||
|
return bluetoothAdapter?.isEnabled == true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Bluetooth status and handle accordingly
|
||||||
|
* This should be called on every app startup
|
||||||
|
*/
|
||||||
|
fun checkBluetoothStatus(): BluetoothStatus {
|
||||||
|
Log.d(TAG, "Checking Bluetooth status")
|
||||||
|
|
||||||
|
return when {
|
||||||
|
bluetoothAdapter == null -> {
|
||||||
|
Log.e(TAG, "Bluetooth not supported on this device")
|
||||||
|
BluetoothStatus.NOT_SUPPORTED
|
||||||
|
}
|
||||||
|
!bluetoothAdapter!!.isEnabled -> {
|
||||||
|
Log.w(TAG, "Bluetooth is disabled")
|
||||||
|
BluetoothStatus.DISABLED
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.d(TAG, "Bluetooth is enabled and ready")
|
||||||
|
BluetoothStatus.ENABLED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request user to enable Bluetooth
|
||||||
|
*/
|
||||||
|
fun requestEnableBluetooth() {
|
||||||
|
Log.d(TAG, "Requesting user to enable Bluetooth")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||||
|
bluetoothEnableLauncher?.launch(enableBluetoothIntent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to request Bluetooth enable", e)
|
||||||
|
onBluetoothDisabled("Failed to request Bluetooth enable: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Bluetooth status check result
|
||||||
|
*/
|
||||||
|
fun handleBluetoothStatus(status: BluetoothStatus) {
|
||||||
|
when (status) {
|
||||||
|
BluetoothStatus.ENABLED -> {
|
||||||
|
Log.d(TAG, "Bluetooth is enabled, proceeding")
|
||||||
|
onBluetoothEnabled()
|
||||||
|
}
|
||||||
|
BluetoothStatus.DISABLED -> {
|
||||||
|
Log.d(TAG, "Bluetooth is disabled, requesting enable")
|
||||||
|
requestEnableBluetooth()
|
||||||
|
}
|
||||||
|
BluetoothStatus.NOT_SUPPORTED -> {
|
||||||
|
Log.e(TAG, "Bluetooth not supported")
|
||||||
|
onBluetoothDisabled("This device doesn't support Bluetooth, which is required for bitchat to function.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user-friendly status message
|
||||||
|
*/
|
||||||
|
fun getStatusMessage(status: BluetoothStatus): String {
|
||||||
|
return when (status) {
|
||||||
|
BluetoothStatus.ENABLED -> "Bluetooth is enabled and ready"
|
||||||
|
BluetoothStatus.DISABLED -> "Bluetooth is disabled. Please enable Bluetooth to use bitchat."
|
||||||
|
BluetoothStatus.NOT_SUPPORTED -> "This device doesn't support Bluetooth."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get detailed diagnostics
|
||||||
|
*/
|
||||||
|
fun getDiagnostics(): String {
|
||||||
|
return buildString {
|
||||||
|
appendLine("Bluetooth Status Diagnostics:")
|
||||||
|
appendLine("Adapter available: ${bluetoothAdapter != null}")
|
||||||
|
appendLine("Bluetooth supported: ${isBluetoothSupported()}")
|
||||||
|
appendLine("Bluetooth enabled: ${isBluetoothEnabled()}")
|
||||||
|
appendLine("Current status: ${checkBluetoothStatus()}")
|
||||||
|
|
||||||
|
bluetoothAdapter?.let { adapter ->
|
||||||
|
appendLine("Adapter name: ${adapter.name ?: "Unknown"}")
|
||||||
|
appendLine("Adapter address: ${adapter.address ?: "Unknown"}")
|
||||||
|
appendLine("Adapter state: ${getAdapterStateName(adapter.state)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAdapterStateName(state: Int): String {
|
||||||
|
return when (state) {
|
||||||
|
BluetoothAdapter.STATE_OFF -> "OFF"
|
||||||
|
BluetoothAdapter.STATE_TURNING_ON -> "TURNING_ON"
|
||||||
|
BluetoothAdapter.STATE_ON -> "ON"
|
||||||
|
BluetoothAdapter.STATE_TURNING_OFF -> "TURNING_OFF"
|
||||||
|
else -> "UNKNOWN($state)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log current Bluetooth status for debugging
|
||||||
|
*/
|
||||||
|
fun logBluetoothStatus() {
|
||||||
|
Log.d(TAG, getDiagnostics())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bluetooth status enum
|
||||||
|
*/
|
||||||
|
enum class BluetoothStatus {
|
||||||
|
ENABLED,
|
||||||
|
DISABLED,
|
||||||
|
NOT_SUPPORTED
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user