Skip to content

Commit e3022fd

Browse files
authored
Merge pull request #82 from permissionlesstech/fix-startup-2
Fix startup 2
2 parents 4545afb + a97e254 commit e3022fd

File tree

10 files changed

+454
-101
lines changed

10 files changed

+454
-101
lines changed

app/src/main/java/com/bitchat/android/MainActivity.kt

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import androidx.compose.runtime.*
1717
import androidx.compose.ui.Modifier
1818
import androidx.compose.ui.platform.LocalContext
1919
import androidx.lifecycle.lifecycleScope
20+
import androidx.lifecycle.ViewModelProvider
21+
import com.bitchat.android.mesh.BluetoothMeshService
2022
import com.bitchat.android.onboarding.*
2123
import com.bitchat.android.ui.ChatScreen
2224
import com.bitchat.android.ui.ChatViewModel
@@ -29,7 +31,17 @@ class MainActivity : ComponentActivity() {
2931
private lateinit var permissionManager: PermissionManager
3032
private lateinit var onboardingCoordinator: OnboardingCoordinator
3133
private lateinit var bluetoothStatusManager: BluetoothStatusManager
32-
private val chatViewModel: ChatViewModel by viewModels()
34+
35+
// Core mesh service - managed at app level
36+
private lateinit var meshService: BluetoothMeshService
37+
private val chatViewModel: ChatViewModel by viewModels {
38+
object : ViewModelProvider.Factory {
39+
override fun <T : androidx.lifecycle.ViewModel> create(modelClass: Class<T>): T {
40+
@Suppress("UNCHECKED_CAST")
41+
return ChatViewModel(application, meshService) as T
42+
}
43+
}
44+
}
3345

3446
// UI state for onboarding flow
3547
private var onboardingState by mutableStateOf(OnboardingState.CHECKING)
@@ -50,6 +62,9 @@ class MainActivity : ComponentActivity() {
5062
override fun onCreate(savedInstanceState: Bundle?) {
5163
super.onCreate(savedInstanceState)
5264

65+
// Initialize core mesh service first
66+
meshService = BluetoothMeshService(this)
67+
5368
// Initialize permission management
5469
permissionManager = PermissionManager(this)
5570
bluetoothStatusManager = BluetoothStatusManager(
@@ -289,7 +304,7 @@ class MainActivity : ComponentActivity() {
289304
// This solves the issue where app needs restart to work on first install
290305
delay(1000) // Give the system time to process permission grants
291306

292-
android.util.Log.d("MainActivity", "Permissions verified, starting mesh service")
307+
android.util.Log.d("MainActivity", "Permissions verified, initializing chat system")
293308

294309
// Ensure all permissions are still granted (user might have revoked in settings)
295310
if (!permissionManager.areAllPermissionsGranted()) {
@@ -299,8 +314,11 @@ class MainActivity : ComponentActivity() {
299314
return@launch
300315
}
301316

302-
// Initialize chat view model - this will start the mesh service
303-
chatViewModel.meshService.startServices()
317+
// Set up mesh service delegate and start services
318+
meshService.delegate = chatViewModel
319+
meshService.startServices()
320+
321+
android.util.Log.d("MainActivity", "Mesh service started successfully")
304322

305323
// Handle any notification intent
306324
handleNotificationIntent(intent)
@@ -330,6 +348,8 @@ class MainActivity : ComponentActivity() {
330348
super.onResume()
331349
// Check Bluetooth status on resume and handle accordingly
332350
if (onboardingState == OnboardingState.COMPLETE) {
351+
// Set app foreground state
352+
meshService.connectionManager.setAppBackgroundState(false)
333353
chatViewModel.setAppBackgroundState(false)
334354

335355
// Check if Bluetooth was disabled while app was backgrounded
@@ -347,6 +367,8 @@ class MainActivity : ComponentActivity() {
347367
super.onPause()
348368
// Only set background state if app is fully initialized
349369
if (onboardingState == OnboardingState.COMPLETE) {
370+
// Set app background state
371+
meshService.connectionManager.setAppBackgroundState(true)
350372
chatViewModel.setAppBackgroundState(true)
351373
}
352374
}
@@ -376,12 +398,32 @@ class MainActivity : ComponentActivity() {
376398
}
377399
}
378400

401+
/**
402+
* Restart mesh services (for debugging/troubleshooting)
403+
*/
404+
fun restartMeshServices() {
405+
if (onboardingState == OnboardingState.COMPLETE) {
406+
lifecycleScope.launch {
407+
try {
408+
android.util.Log.d("MainActivity", "Restarting mesh services")
409+
meshService.stopServices()
410+
delay(1000)
411+
meshService.startServices()
412+
android.util.Log.d("MainActivity", "Mesh services restarted successfully")
413+
} catch (e: Exception) {
414+
android.util.Log.e("MainActivity", "Error restarting mesh services: ${e.message}")
415+
}
416+
}
417+
}
418+
}
419+
379420
override fun onDestroy() {
380421
super.onDestroy()
381-
// Only stop mesh services if they were started
422+
// Stop mesh services if app was fully initialized
382423
if (onboardingState == OnboardingState.COMPLETE) {
383424
try {
384-
chatViewModel.meshService.stopServices()
425+
meshService.stopServices()
426+
android.util.Log.d("MainActivity", "Mesh services stopped successfully")
385427
} catch (e: Exception) {
386428
android.util.Log.w("MainActivity", "Error stopping mesh services in onDestroy: ${e.message}")
387429
}

app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,16 @@ class BluetoothConnectionManager(
129129
try {
130130
isActive = true
131131

132+
// Setup GATT server first
133+
setupGattServer()
134+
132135
// Start power manager and services
133136
connectionScope.launch {
134137
powerManager.start()
135-
136-
// Setup GATT server after power manager is ready
137-
setupGattServer()
138-
delay(500) // Ensure GATT server is ready
138+
delay(300) // Brief delay to ensure GATT server is ready
139139

140140
startAdvertising()
141-
delay(200)
141+
delay(100)
142142

143143
if (powerManager.shouldUseDutyCycle()) {
144144
Log.i(TAG, "Using power-aware duty cycling")
@@ -410,10 +410,15 @@ class BluetoothConnectionManager(
410410
}
411411

412412
if (characteristic.uuid == CHARACTERISTIC_UUID) {
413+
Log.d(TAG, "Server: Received packet from ${device.address}, size: ${value.size} bytes")
413414
val packet = BitchatPacket.fromBinaryData(value)
414415
if (packet != null) {
415416
val peerID = String(packet.senderID).replace("\u0000", "")
417+
Log.d(TAG, "Server: Parsed packet type ${packet.type} from $peerID")
416418
delegate?.onPacketReceived(packet, peerID, device)
419+
} else {
420+
Log.w(TAG, "Server: Failed to parse packet from ${device.address}, size: ${value.size} bytes")
421+
Log.w(TAG, "Server: Packet data: ${value.joinToString(" ") { "%02x".format(it) }}")
417422
}
418423

419424
if (responseNeeded) {
@@ -458,48 +463,47 @@ class BluetoothConnectionManager(
458463
// Proper cleanup sequencing to prevent race conditions
459464
gattServer?.let { server ->
460465
Log.d(TAG, "Cleaning up existing GATT server")
461-
connectionScope.launch {
462-
// Give time for pending callbacks to complete
463-
delay(100)
466+
try {
464467
server.close()
468+
} catch (e: Exception) {
469+
Log.w(TAG, "Error closing existing GATT server: ${e.message}")
465470
}
466471
}
467472

468-
// Create new server after cleanup delay
469-
connectionScope.launch {
470-
delay(200) // Allow previous server to fully close
471-
472-
if (!isActive) {
473-
Log.d(TAG, "Service inactive, skipping GATT server creation")
474-
return@launch
475-
}
476-
477-
gattServer = bluetoothManager.openGattServer(context, serverCallback)
478-
479-
// Create characteristic with notification support
480-
characteristic = BluetoothGattCharacteristic(
481-
CHARACTERISTIC_UUID,
482-
BluetoothGattCharacteristic.PROPERTY_READ or
483-
BluetoothGattCharacteristic.PROPERTY_WRITE or
484-
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or
485-
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
486-
BluetoothGattCharacteristic.PERMISSION_READ or
487-
BluetoothGattCharacteristic.PERMISSION_WRITE
488-
)
489-
490-
val descriptor = BluetoothGattDescriptor(
491-
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"),
492-
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
493-
)
494-
characteristic?.addDescriptor(descriptor)
495-
496-
val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
497-
service.addCharacteristic(characteristic)
498-
499-
gattServer?.addService(service)
500-
501-
Log.i(TAG, "GATT server setup complete")
473+
// Small delay to ensure cleanup is complete
474+
Thread.sleep(100)
475+
476+
if (!isActive) {
477+
Log.d(TAG, "Service inactive, skipping GATT server creation")
478+
return
502479
}
480+
481+
// Create new server
482+
gattServer = bluetoothManager.openGattServer(context, serverCallback)
483+
484+
// Create characteristic with notification support
485+
characteristic = BluetoothGattCharacteristic(
486+
CHARACTERISTIC_UUID,
487+
BluetoothGattCharacteristic.PROPERTY_READ or
488+
BluetoothGattCharacteristic.PROPERTY_WRITE or
489+
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or
490+
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
491+
BluetoothGattCharacteristic.PERMISSION_READ or
492+
BluetoothGattCharacteristic.PERMISSION_WRITE
493+
)
494+
495+
val descriptor = BluetoothGattDescriptor(
496+
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"),
497+
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
498+
)
499+
characteristic?.addDescriptor(descriptor)
500+
501+
val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
502+
service.addCharacteristic(characteristic)
503+
504+
gattServer?.addService(service)
505+
506+
Log.i(TAG, "GATT server setup complete")
503507
}
504508

505509
@Suppress("DEPRECATION")
@@ -836,10 +840,15 @@ class BluetoothConnectionManager(
836840

837841
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
838842
val value = characteristic.value
843+
Log.d(TAG, "Client: Received packet from ${gatt.device.address}, size: ${value.size} bytes")
839844
val packet = BitchatPacket.fromBinaryData(value)
840845
if (packet != null) {
841846
val peerID = String(packet.senderID).replace("\u0000", "")
847+
Log.d(TAG, "Client: Parsed packet type ${packet.type} from $peerID")
842848
delegate?.onPacketReceived(packet, peerID, gatt.device)
849+
} else {
850+
Log.w(TAG, "Client: Failed to parse packet from ${gatt.device.address}, size: ${value.size} bytes")
851+
Log.w(TAG, "Client: Packet data: ${value.joinToString(" ") { "%02x".format(it) }}")
843852
}
844853
}
845854
}

app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ class BluetoothMeshService(private val context: Context) {
6969
while (isActive) {
7070
try {
7171
delay(10000) // 10 seconds
72-
val debugInfo = getDebugStatus()
73-
Log.d(TAG, "=== PERIODIC DEBUG STATUS ===")
74-
Log.d(TAG, debugInfo)
75-
Log.d(TAG, "=== END DEBUG STATUS ===")
72+
if (isActive) { // Double-check before logging
73+
val debugInfo = getDebugStatus()
74+
Log.d(TAG, "=== PERIODIC DEBUG STATUS ===\n$debugInfo\n=== END DEBUG STATUS ===")
75+
}
7676
} catch (e: Exception) {
7777
Log.e(TAG, "Error in periodic debug logging: ${e.message}")
7878
}
@@ -282,7 +282,7 @@ class BluetoothMeshService(private val context: Context) {
282282
* Start the mesh service
283283
*/
284284
fun startServices() {
285-
// Prevent double starts
285+
// Prevent double starts (defensive programming)
286286
if (isActive) {
287287
Log.w(TAG, "Mesh service already active, ignoring duplicate start request")
288288
return
@@ -297,9 +297,7 @@ class BluetoothMeshService(private val context: Context) {
297297
// Send initial announcements after services are ready
298298
serviceScope.launch {
299299
delay(1000)
300-
if (isActive) { // Check if still active
301-
sendBroadcastAnnounce()
302-
}
300+
sendBroadcastAnnounce()
303301
}
304302
} else {
305303
Log.e(TAG, "Failed to start Bluetooth services")

0 commit comments

Comments
 (0)