diff --git a/app/src/main/java/com/helible/pilot/MainActivity.kt b/app/src/main/java/com/helible/pilot/MainActivity.kt index d1db83a..2e5e3f3 100644 --- a/app/src/main/java/com/helible/pilot/MainActivity.kt +++ b/app/src/main/java/com/helible/pilot/MainActivity.kt @@ -6,8 +6,10 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent +import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -139,7 +141,10 @@ class MainActivity : ComponentActivity() { device ) }, - disconnectFromDevice = { bluetoothViewModel.disconnectFromDevice() }, + disconnectFromDevice = { + preferencesViewModel.clearPreferences() + bluetoothViewModel.disconnectFromDevice() + }, deviceActionsList = defaultDeviceActionsList() ) if (preferencesViewModel.preferences != null) BackHandler {} @@ -150,6 +155,9 @@ class MainActivity : ComponentActivity() { title = backStackEntry.arguments?.getString("title") ?: "null", navigateBack = { navController.popBackStack() } ) + Button(onClick = { bluetoothViewModel.sendHelloWorld() }) { + Text("Click me!") + } } composable("codeblocks/{title}") { backStackEntry -> diff --git a/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceControlScreen.kt b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceControlScreen.kt index d37c163..b2fe099 100644 --- a/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceControlScreen.kt +++ b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceControlScreen.kt @@ -1,7 +1,10 @@ package com.helible.pilot.components.deviceScreen +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon @@ -11,6 +14,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource @@ -33,7 +37,7 @@ fun DeviceControlScreen( scannerPageName: String = "scanner", ) { LaunchedEffect(Unit) { - val preferences: AppPreferences? = getPreferences() + val preferences = getPreferences() if (preferences == null) { navigateToPage(scannerPageName) } else { @@ -85,19 +89,24 @@ fun DeviceControlScreen( ) for (action in section.value) { TextButton( - onClick = { navigateToPage(action.first + '/' + action.second.second) } + onClick = { navigateToPage(action.first + '/' + action.second.second) }, + modifier = Modifier.fillMaxWidth() ) { - Icon( - painter = painterResource(id = action.second.first.first), - tint = action.second.first.second, - contentDescription = null, - modifier = Modifier.size(25.dp) - ) - Text( - text = action.second.second, - color = MaterialTheme.colorScheme.inverseSurface, - modifier = Modifier.padding(horizontal = 5.dp) - ) + Row(modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(5.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = action.second.first.first), + tint = action.second.first.second, + contentDescription = null, + modifier = Modifier.size(25.dp) + ) + Text( + text = action.second.second, + color = MaterialTheme.colorScheme.inverseSurface + ) + } } } } diff --git a/app/src/main/java/com/helible/pilot/controllers/BluetoothController.kt b/app/src/main/java/com/helible/pilot/controllers/BluetoothController.kt index 219896f..9e5a628 100644 --- a/app/src/main/java/com/helible/pilot/controllers/BluetoothController.kt +++ b/app/src/main/java/com/helible/pilot/controllers/BluetoothController.kt @@ -14,7 +14,7 @@ import android.os.Build import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity -import com.helible.pilot.BluetoothDataTransferService +import com.helible.pilot.viewmodels.BluetoothDataTransferService import com.helible.pilot.KMessage import com.helible.pilot.dataclasses.BluetoothDeviceDomain import com.helible.pilot.receivers.BluetoothAdapterStateReceiver @@ -40,7 +40,7 @@ import java.util.UUID sealed interface ConnectionResult { object ConnectionEstablished : ConnectionResult - data class TransferSucceded(val message: KMessage) : ConnectionResult + data class TransferSucceded(val message: String) : ConnectionResult data class Error(val message: String) : ConnectionResult } @@ -219,6 +219,7 @@ class AndroidBluetoothController(private val context: Context) : BluetoothContro return flow {} } return flow { + Log.i("BluetoothController", "Connecting to device...") currentClientSocket = bluetoothAdapter.getRemoteDevice(device).createRfcommSocketToServiceRecord( UUID.fromString(SERVICE_UUID) @@ -235,7 +236,9 @@ class AndroidBluetoothController(private val context: Context) : BluetoothContro ) } } catch (e: IOException) { - closeConnection() + socket.close() + currentClientSocket = null + Log.e("BluetoothController", e.toString()) emit(ConnectionResult.Error("Connection was interrupted")) } } @@ -256,6 +259,7 @@ class AndroidBluetoothController(private val context: Context) : BluetoothContro override fun closeConnection() { currentClientSocket?.close() currentClientSocket = null + Log.i("BluetoothController", "Connection closed") } override fun onDestroy() { diff --git a/app/src/main/java/com/helible/pilot/controllers/DeviceState.kt b/app/src/main/java/com/helible/pilot/controllers/DeviceState.kt new file mode 100644 index 0000000..73b9a87 --- /dev/null +++ b/app/src/main/java/com/helible/pilot/controllers/DeviceState.kt @@ -0,0 +1,9 @@ +package com.helible.pilot.controllers + +data class DeviceState( + val isHandshakeWaiting: Boolean = true, + val isIMUCalibrating: Boolean = false, + val flightMode: Boolean = false, + val batteryCharge: Int?, + val flightHeight: Float? +) \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/BluetoothDataTransferService.kt b/app/src/main/java/com/helible/pilot/viewmodels/BluetoothDataTransferService.kt similarity index 76% rename from app/src/main/java/com/helible/pilot/BluetoothDataTransferService.kt rename to app/src/main/java/com/helible/pilot/viewmodels/BluetoothDataTransferService.kt index 3f8ce69..2bc7eb7 100644 --- a/app/src/main/java/com/helible/pilot/BluetoothDataTransferService.kt +++ b/app/src/main/java/com/helible/pilot/viewmodels/BluetoothDataTransferService.kt @@ -1,6 +1,9 @@ -package com.helible.pilot +package com.helible.pilot.viewmodels import android.bluetooth.BluetoothSocket +import android.util.Log +import com.helible.pilot.KMessage +import com.helible.pilot.toKMessage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -14,7 +17,7 @@ class TransferFailedException : IOException("Reading incoming data failed") class BluetoothDataTransferService( private val socket: BluetoothSocket, ) { - fun listenForIncomingMessages(): Flow { + fun listenForIncomingMessages(): Flow { return flow { if (!socket.isConnected) return@flow @@ -25,11 +28,11 @@ class BluetoothDataTransferService( } catch (e: IOException) { throw TransferFailedException() } + val strData: String = buffer.decodeToString(endIndex = byteCount) emit( - buffer.decodeToString( - endIndex = byteCount - ).toKMessage() + strData ) + Log.i("BluetoothController", "Received: ${strData.dropLast(2)}") } }.flowOn(Dispatchers.IO) } diff --git a/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt b/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt index e914c7f..aef50e7 100644 --- a/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt +++ b/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt @@ -94,7 +94,9 @@ class BluetoothViewModel( } is ConnectionResult.TransferSucceded -> { - TODO("Telemetry not implemented") + _state.update { it.copy( + + ) } } is ConnectionResult.Error -> { @@ -108,14 +110,12 @@ class BluetoothViewModel( } } } - .catch { _ -> + .catch { throwable -> bluetoothController.closeConnection() - _state.update { - it.copy( - isConnected = false, - isConnecting = false - ) - } + _state.update { it.copy( + isConnected = false, + isConnecting = false + ) } } .launchIn(viewModelScope) } @@ -123,7 +123,7 @@ class BluetoothViewModel( private var deviceConnectionJob: Job? = null fun connectToDevice(device: String) { - if (_state.value.isConnected) { + if (_state.value.isConnected and _state.value.isConnecting) { return } _state.update { it.copy(isConnecting = true) } @@ -162,36 +162,12 @@ class BluetoothViewModel( super.onCleared() } - fun sendRotorsDutySpeed(rotorsState: RotorsSpeedMessage) { + fun sendHelloWorld() { viewModelScope.launch { bluetoothController.trySendMessage( - rotorsStateMessegeAdapter.toJson(rotorsState).plus("\r").toByteArray() + "{\"p1\": {\"p\": 1.5, \"i\": 1.5, \"d\": 1.5}}\n\r".toByteArray() + //"{\"p1\": [1.5, 1.5, 1.5]}\n\r".toByteArray() ) } } - - fun sendAlarmState(alarmStateMessage: AlarmStateMessage) { - viewModelScope.launch { - bluetoothController.trySendMessage( - alarmStateMessageAdapter.toJson(alarmStateMessage).plus("\r").toByteArray() - ) - } - } - - fun sendEmergStop() { - viewModelScope.launch { - bluetoothController.trySendMessage( - emergStopMessageAdapter.toJson(EmergStopMessage(true)).plus("\r").toByteArray() - ) - } - } - - fun sendR3Duty(r3: Int) { - viewModelScope.launch { - bluetoothController.trySendMessage( - "R3$r3\n\r".toByteArray() - ) - delay(30) - } - } } \ No newline at end of file