diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 120ff06..02c26aa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("com.google.devtools.ksp").version("1.6.10-1.0.4") + id("org.jlleitschuh.gradle.ktlint").version("12.0.3") } android { @@ -64,6 +65,7 @@ dependencies { implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1") implementation("androidx.navigation:navigation-compose:2.6.0") implementation("com.squareup.moshi:moshi-kotlin:1.14.0") + testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/ic_helicopter-playstore.png b/app/src/main/ic_helicopter-playstore.png new file mode 100644 index 0000000..b9d4e63 Binary files /dev/null and b/app/src/main/ic_helicopter-playstore.png differ diff --git a/app/src/main/java/com/helible/pilot/MainActivity.kt b/app/src/main/java/com/helible/pilot/MainActivity.kt index c9c9aa2..57e37c5 100644 --- a/app/src/main/java/com/helible/pilot/MainActivity.kt +++ b/app/src/main/java/com/helible/pilot/MainActivity.kt @@ -15,10 +15,11 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.helible.pilot.components.BluetoothScannerScreen -import com.helible.pilot.components.FlightControlScreen -import com.helible.pilot.components.AppPreferences -import com.helible.pilot.components.SavedPreferencesImpl +import com.helible.pilot.components.scannerScreen.BluetoothScannerScreen +import com.helible.pilot.components.deviceScreen.DeviceControlScreen +import com.helible.pilot.viewmodels.AppPreferences +import com.helible.pilot.viewmodels.SavedPreferencesImpl +import com.helible.pilot.components.deviceScreen.defaultDeviceActionsList import com.helible.pilot.permissions.PermissionsLauncher import com.helible.pilot.permissions.PermissionsRequest import com.helible.pilot.permissions.RequestHardwareFeatures @@ -31,8 +32,10 @@ import com.helible.pilot.viewmodels.PreferencesViewModel class MainActivity : ComponentActivity() { // TODO: device screen logic + // TODO: constrain text size // TODO: add Bluetooth telemetry... // TODO: move text strings to resources + // TODO: review permissions logic private val preferences by lazy { SavedPreferencesImpl(getSharedPreferences(packageName, MODE_PRIVATE)) @@ -127,32 +130,48 @@ class MainActivity : ComponentActivity() { } composable("device") { - FlightControlScreen( + DeviceControlScreen( bluetoothUiState = bluetoothState, getPreferences = { preferencesViewModel.preferences }, - navigateToScanner = { navController.navigate("scanner") }, + navigateToPage = { page -> navController.navigate(page) }, connectToDevice = { device -> bluetoothViewModel.connectToDevice( device ) }, - sendRotorsState = { message -> - bluetoothViewModel.sendRotorsDutySpeed( - message - ) - }, disconnectFromDevice = { bluetoothViewModel.disconnectFromDevice() }, - sendEmergStop = { bluetoothViewModel.sendEmergStop() }, - sendAlarm = { message -> bluetoothViewModel.sendAlarmState(message) }, - sendR3Duty = { duty -> bluetoothViewModel.sendR3Duty(duty) } + deviceActionsList = defaultDeviceActionsList() ) if (preferencesViewModel.preferences != null) BackHandler {} } + composable("console") + { + + } + composable("codeblocks") + { + + } + composable("imu_calibration") + { + + } + composable("motor_test") + { + + } + composable("pid_settings") + { + + } + composable("reports") + { + + } } } } } - } } diff --git a/app/src/main/java/com/helible/pilot/components/DeviceScreen.kt b/app/src/main/java/com/helible/pilot/components/DeviceScreen.kt deleted file mode 100644 index 89cd545..0000000 --- a/app/src/main/java/com/helible/pilot/components/DeviceScreen.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.helible.pilot.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun DeviceScreen() { - Scaffold( - topBar = { - Text(text = "") - } - ) { innerPadding -> - Column( - modifier = Modifier.padding(innerPadding) - ) { - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/FlightControlScreen.kt b/app/src/main/java/com/helible/pilot/components/FlightControlScreen.kt deleted file mode 100644 index 77c786e..0000000 --- a/app/src/main/java/com/helible/pilot/components/FlightControlScreen.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.helible.pilot.components - -import android.util.Log -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.FilledIconButton -import androidx.compose.material3.Icon -import androidx.compose.material3.Slider -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.helible.pilot.dataclasses.AlarmStateMessage -import com.helible.pilot.dataclasses.BluetoothUiState -import com.helible.pilot.dataclasses.RotorsSpeedMessage -import kotlin.math.roundToInt - - -@Composable -fun FlightControlScreen( - bluetoothUiState: BluetoothUiState, - getPreferences: () -> AppPreferences?, - navigateToScanner: () -> Unit, - connectToDevice: (String) -> Unit, - disconnectFromDevice: () -> Unit, - sendRotorsState: (RotorsSpeedMessage) -> Unit, - sendAlarm: (AlarmStateMessage) -> Unit, - sendEmergStop: () -> Unit, - sendR3Duty: (Int) -> Unit, -) { - LaunchedEffect(Unit) { - val preferences: AppPreferences? = getPreferences() - if (preferences == null) { - navigateToScanner() - } else { - connectToDevice(preferences.deviceAddress) - } - } - - var rotor1Duty by remember { mutableStateOf(0f) } - var rotor2Duty by remember { mutableStateOf(0f) } - var rotor3Duty by remember { mutableStateOf(0f) } - - BackHandler { - disconnectFromDevice() - Log.i("FlightScreen", "Disconnected from the device") - navigateToScanner() - } - when { - bluetoothUiState.isConnecting -> { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - CircularProgressIndicator() - Text(text = "Подключение...", textAlign = TextAlign.Center) - } - } - - else -> { - - Column(modifier = Modifier.fillMaxSize()) { - Text( - text = "Device name: ${getPreferences()?.deviceName ?: "(устройство отключено)"}", - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center - ) - Text(text = "Rotor 1 value: $rotor1Duty", textAlign = TextAlign.Center) - Slider( - value = rotor1Duty, - onValueChange = { rotor1Duty = it.roundToInt().toFloat() }, - valueRange = 0f..1000f, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - ) - Text(text = "Rotor 2 value: $rotor2Duty", textAlign = TextAlign.Center) - Slider( - value = rotor2Duty, - onValueChange = { rotor2Duty = it.roundToInt().toFloat() }, - valueRange = 0f..1000f, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - ) - Text(text = "Rotor 3 value: $rotor1Duty", textAlign = TextAlign.Center) - Slider( - value = rotor3Duty, - onValueChange = { rotor3Duty = it.roundToInt().toFloat() }, - valueRange = 0f..1000f, - - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - ) - FilledIconButton( - onClick = { sendEmergStop() }, - modifier = Modifier.padding(10.dp) - ) { - Icon( - Icons.Default.Warning, - contentDescription = null, - modifier = Modifier.padding(3.dp) - ) - Text( - text = "СТОП", - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(3.dp) - ) - } - } - } - } -} diff --git a/app/src/main/java/com/helible/pilot/components/Title.kt b/app/src/main/java/com/helible/pilot/components/Title.kt index 714fa00..c4d397b 100644 --- a/app/src/main/java/com/helible/pilot/components/Title.kt +++ b/app/src/main/java/com/helible/pilot/components/Title.kt @@ -14,6 +14,6 @@ fun Title(text: String, modifier: Modifier = Modifier) { textAlign = TextAlign.Center, modifier = modifier, fontSize = 23.sp, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.ExtraBold ) } \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceBadge.kt b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceBadge.kt new file mode 100644 index 0000000..d7490e4 --- /dev/null +++ b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceBadge.kt @@ -0,0 +1,96 @@ +package com.helible.pilot.components.deviceScreen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +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.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.helible.pilot.R +import com.helible.pilot.dataclasses.BluetoothUiState +import com.helible.pilot.viewmodels.AppPreferences + +@Composable +fun DeviceBadge( + bluetoothUiState: BluetoothUiState, + tryToReconnect: () -> Unit, + getPreferences: () -> AppPreferences? +) { + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp), + shape = RoundedCornerShape(15) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 15.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier + .size(60.dp) + .graphicsLayer { + clip = true + shape = RoundedCornerShape(15) + } + .fillMaxSize()) { + Image( + painter = painterResource(id = R.drawable.helicopter), + contentDescription = null, + modifier = Modifier.fillMaxSize() + ) + } + Column(modifier = Modifier.padding(horizontal = 8.dp)) { + Text( + text = getPreferences()?.deviceName ?: "null", + fontWeight = FontWeight.Bold + ) + DeviceConnectionStatus(bluetoothUiState) + Text(text = "Заряд батареи: 79%") + } + Box( + contentAlignment = Alignment.CenterEnd, + modifier = Modifier + .padding(2.dp) + .fillMaxWidth() + ) { + Icon( + Icons.Default.Refresh, + contentDescription = null, + modifier = Modifier + .requiredSize(Icons.Default.Refresh.defaultWidth) + .clickable { tryToReconnect() } + ) + } + } + } +} + +@Preview +@Composable +fun DeviceBadgePreview() { + DeviceBadge( + bluetoothUiState = BluetoothUiState(isConnected = true), + tryToReconnect = {}, + getPreferences = {AppPreferences("Helicopter", "AA:BB:CC:FF:DD")} + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceConnectionStatus.kt b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceConnectionStatus.kt new file mode 100644 index 0000000..037b87a --- /dev/null +++ b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceConnectionStatus.kt @@ -0,0 +1,66 @@ +package com.helible.pilot.components.deviceScreen + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.helible.pilot.R +import com.helible.pilot.dataclasses.BluetoothUiState + +@Composable +fun DeviceConnectionStatus(bluetoothState: BluetoothUiState) { + Row(verticalAlignment = Alignment.CenterVertically) { + if (bluetoothState.isConnected) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + tint = Color(56, 200, 35), + modifier = Modifier + .requiredSize(Icons.Default.CheckCircle.defaultWidth) + .padding(2.dp) + ) + Text ("На связи") + } + else if (bluetoothState.errorMessage != null) { + Icon( + painter = painterResource(id = R.drawable.cancel), + contentDescription = null, + tint = Color(255, 24, 35), + modifier = Modifier + .requiredSize(R.drawable.cancel.dp) + .padding(2.dp) + ) + Text ("Ошибка: ${bluetoothState.errorMessage}") + } + else if (bluetoothState.isConnecting) { + Icon( + painter = painterResource(id = R.drawable.sync), + contentDescription = null, + tint = Color(40, 123, 207), + modifier = Modifier + .requiredSize(R.drawable.sync.dp) + .padding(2.dp) + ) + Text ("Подключение...") + } + } +} + +@Preview +@Composable +fun DeviceConnectionStatusPreview() { + Surface { + DeviceConnectionStatus(bluetoothState = BluetoothUiState(isConnecting = true)) + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..9a62fcd --- /dev/null +++ b/app/src/main/java/com/helible/pilot/components/deviceScreen/DeviceControlScreen.kt @@ -0,0 +1,143 @@ +package com.helible.pilot.components.deviceScreen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +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.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +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.graphics.graphicsLayer +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.helible.pilot.R +import com.helible.pilot.components.Title +import com.helible.pilot.dataclasses.BluetoothUiState +import com.helible.pilot.viewmodels.AppPreferences + +@Composable +fun DeviceControlScreen( + bluetoothUiState: BluetoothUiState, + getPreferences: () -> AppPreferences?, + navigateToPage: (String) -> Unit, + connectToDevice: (String) -> Unit, + disconnectFromDevice: () -> Unit, + deviceActionsList: Map, String>>>>, + scannerPageName: String = "scanner", +) { + LaunchedEffect(Unit) { + val preferences: AppPreferences? = getPreferences() + if (preferences == null) { + navigateToPage(scannerPageName) + } else { + connectToDevice(preferences.deviceAddress) + } + } + + LaunchedEffect(key1 = bluetoothUiState.isEnabled) { + /* Trying to reconnect, when bluetooth is turned on */ + val preferences = getPreferences() + if(preferences != null && bluetoothUiState.isEnabled) + connectToDevice(preferences.deviceAddress) + } + + LaunchedEffect(key1 = bluetoothUiState.isLocationEnabled) { + /* Trying to reconnect, when location is turned on */ + val preferences = getPreferences() + if(preferences != null && bluetoothUiState.isLocationEnabled) + connectToDevice(preferences.deviceAddress) + } + + Column( + Modifier + .fillMaxSize() + .padding(5.dp) + ) { + Title( + text = "Ваше устройство", + modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp) + ) + DeviceBadge( + bluetoothUiState = bluetoothUiState, + tryToReconnect = { + /* Trying to reconnect, when error occurred */ + val preferences = getPreferences() + if(preferences != null) + connectToDevice(preferences.deviceAddress) + }, + getPreferences = getPreferences + ) + + Column(modifier = Modifier.padding(horizontal = 3.dp)) { + for (section in deviceActionsList) { + Text(section.key, + color = Color.Gray, + fontWeight = FontWeight.Light, + modifier = Modifier.padding(vertical = 15.dp, horizontal = 10.dp) + ) + for (action in section.value) { + TextButton(onClick = { /* TODO */}) { + 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) + ) + } + } + } + + TextButton(onClick = { + disconnectFromDevice() + navigateToPage(scannerPageName) + }, modifier = Modifier.padding(vertical = 10.dp)) { + Icon(painterResource(id = R.drawable.logout), contentDescription = null) + Text( + text = "Отвязать устройство", + color = MaterialTheme.colorScheme.inverseSurface, + modifier = Modifier.padding(horizontal = 4.dp) + ) + } + } + } +} + +@Preview +@Composable +fun DeviceControlScreenPreview() { + Surface { + DeviceControlScreen( + bluetoothUiState = BluetoothUiState(isConnected = true), + getPreferences = { AppPreferences("Helicopter", "AA:BB:CC:DD:FF") }, + navigateToPage = { /*TODO*/ }, + connectToDevice = {}, + disconnectFromDevice = { /*TODO*/ }, + deviceActionsList = defaultDeviceActionsList() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/deviceScreen/defaultDeviceActionsLinks.kt b/app/src/main/java/com/helible/pilot/components/deviceScreen/defaultDeviceActionsLinks.kt new file mode 100644 index 0000000..510ef43 --- /dev/null +++ b/app/src/main/java/com/helible/pilot/components/deviceScreen/defaultDeviceActionsLinks.kt @@ -0,0 +1,64 @@ +package com.helible.pilot.components.deviceScreen + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.helible.pilot.R + +@Composable +fun defaultDeviceActionsList(): Map, String>>>> { + return mapOf( + Pair( + "Управление", + arrayOf( + Pair( + "console", + Pair( + Pair(R.drawable.joystick, MaterialTheme.colorScheme.primary), + "Пульт управления" + ) + ), + Pair( + "codeblocks", + Pair( + Pair(R.drawable.code_blocks, MaterialTheme.colorScheme.primary), + "Палитра команд" + ) + ) + ) + ), + Pair( + "Настройки", + arrayOf( + Pair( + "imu_calibration", + Pair( + Pair(R.drawable.tune, MaterialTheme.colorScheme.primary), + "Калибровка гироскопа и акселерометра" + ) + ), + Pair( + "motor_test", + Pair( + Pair(R.drawable.helicopter_icon, MaterialTheme.colorScheme.primary), + "Тестирование двигателей" + ) + ), + Pair( + "pid_settings", + Pair( + Pair(R.drawable.controller_gen, MaterialTheme.colorScheme.primary), + "Настройки ПИД регуляторов" + ) + ), + Pair( + "reports", + Pair( + Pair(R.drawable.construction, MaterialTheme.colorScheme.primary), + "Отчеты о полётах" + ) + ) + ) + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/BluetoothScannerScreen.kt b/app/src/main/java/com/helible/pilot/components/scannerScreen/BluetoothScannerScreen.kt similarity index 97% rename from app/src/main/java/com/helible/pilot/components/BluetoothScannerScreen.kt rename to app/src/main/java/com/helible/pilot/components/scannerScreen/BluetoothScannerScreen.kt index 2de9511..8defc25 100644 --- a/app/src/main/java/com/helible/pilot/components/BluetoothScannerScreen.kt +++ b/app/src/main/java/com/helible/pilot/components/scannerScreen/BluetoothScannerScreen.kt @@ -1,4 +1,4 @@ -package com.helible.pilot.components +package com.helible.pilot.components.scannerScreen import android.annotation.SuppressLint import android.util.Log @@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension +import com.helible.pilot.components.Title import com.helible.pilot.dataclasses.BluetoothUiState import com.helible.pilot.dataclasses.BluetoothDevice @@ -103,7 +104,6 @@ fun BluetoothScannerScreen( Text(text = "Далее") } } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/DeviceItem.kt b/app/src/main/java/com/helible/pilot/components/scannerScreen/DeviceItem.kt similarity index 85% rename from app/src/main/java/com/helible/pilot/components/DeviceItem.kt rename to app/src/main/java/com/helible/pilot/components/scannerScreen/DeviceItem.kt index 531d630..3fa7381 100644 --- a/app/src/main/java/com/helible/pilot/components/DeviceItem.kt +++ b/app/src/main/java/com/helible/pilot/components/scannerScreen/DeviceItem.kt @@ -1,4 +1,4 @@ -package com.helible.pilot.components +package com.helible.pilot.components.scannerScreen import android.annotation.SuppressLint import androidx.compose.foundation.clickable @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon @@ -19,6 +20,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.helible.pilot.dataclasses.BluetoothDevice import com.helible.pilot.R @@ -42,7 +44,7 @@ fun DeviceItem( ) ) { Row(modifier = Modifier.padding(8.dp)) { - Column(verticalArrangement = Arrangement.Center) { + Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { Text( text = deviceInfo.name, fontWeight = FontWeight.Bold, @@ -74,4 +76,15 @@ fun getSignalIconForRssiValue(rssi: Short): Int { else if (rssi >= -90) return R.drawable.signal_icon3 else if (rssi >= -100) return R.drawable.signal_icon2 return R.drawable.signal_icon1 +} + +@Preview +@Composable +fun DeviceItemPreview() { + DeviceItem( + BluetoothDevice("Helicopter", "AA:BB:CC:DD:FF", -90, true), + null, + {_ -> }, + modifier = Modifier.size(500.dp, 60.dp) + ) } \ No newline at end of file diff --git a/app/src/main/java/com/helible/pilot/components/DiscoveredDevicesList.kt b/app/src/main/java/com/helible/pilot/components/scannerScreen/DiscoveredDevicesList.kt similarity index 97% rename from app/src/main/java/com/helible/pilot/components/DiscoveredDevicesList.kt rename to app/src/main/java/com/helible/pilot/components/scannerScreen/DiscoveredDevicesList.kt index 4ec4592..7a4acb4 100644 --- a/app/src/main/java/com/helible/pilot/components/DiscoveredDevicesList.kt +++ b/app/src/main/java/com/helible/pilot/components/scannerScreen/DiscoveredDevicesList.kt @@ -1,4 +1,4 @@ -package com.helible.pilot.components +package com.helible.pilot.components.scannerScreen import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.helible.pilot.components.scannerScreen.DeviceItem import com.helible.pilot.dataclasses.BluetoothUiState import com.helible.pilot.dataclasses.BluetoothDevice 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 12c3355..96cbb37 100644 --- a/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt +++ b/app/src/main/java/com/helible/pilot/viewmodels/BluetoothViewModel.kt @@ -57,24 +57,24 @@ class BluetoothViewModel( it.copy(errorMessage = error) } }.launchIn(viewModelScope) - bluetoothController.isScanning.onEach { result -> + bluetoothController.isScanning.onEach { isDiscovering -> _state.update { it.copy( - isDiscovering = result, + isDiscovering = isDiscovering, ) } }.launchIn(viewModelScope) - bluetoothController.isEnabled.onEach { result -> + bluetoothController.isEnabled.onEach { isEnabled -> _state.update { it.copy( - isEnabled = result, + isEnabled = isEnabled, ) } }.launchIn(viewModelScope) - bluetoothController.isLocationEnabled.onEach { result -> + bluetoothController.isLocationEnabled.onEach { isLocationEnabled -> _state.update { it.copy( - isLocationEnabled = result + isLocationEnabled = isLocationEnabled ) } }.launchIn(viewModelScope) @@ -123,6 +123,9 @@ class BluetoothViewModel( private var deviceConnectionJob: Job? = null fun connectToDevice(device: String) { + if(_state.value.isConnected) { + return + } _state.update { it.copy(isConnecting = true) } deviceConnectionJob = bluetoothController .connectToDevice(device) diff --git a/app/src/main/java/com/helible/pilot/viewmodels/PermissionsViewModel.kt b/app/src/main/java/com/helible/pilot/viewmodels/PermissionsViewModel.kt index 9f36fda..441d212 100644 --- a/app/src/main/java/com/helible/pilot/viewmodels/PermissionsViewModel.kt +++ b/app/src/main/java/com/helible/pilot/viewmodels/PermissionsViewModel.kt @@ -2,8 +2,6 @@ package com.helible.pilot.viewmodels import androidx.compose.runtime.mutableStateListOf import androidx.lifecycle.ViewModel -import com.helible.pilot.components.AppPreferences -import com.helible.pilot.components.SavedPreferences class PermissionDialogViewModel : ViewModel() { val visiblePermissionDialogQueue = mutableStateListOf() diff --git a/app/src/main/java/com/helible/pilot/components/SavedPreferences.kt b/app/src/main/java/com/helible/pilot/viewmodels/SavedPreferences.kt similarity index 97% rename from app/src/main/java/com/helible/pilot/components/SavedPreferences.kt rename to app/src/main/java/com/helible/pilot/viewmodels/SavedPreferences.kt index c9c6f4c..668abe0 100644 --- a/app/src/main/java/com/helible/pilot/components/SavedPreferences.kt +++ b/app/src/main/java/com/helible/pilot/viewmodels/SavedPreferences.kt @@ -1,4 +1,4 @@ -package com.helible.pilot.components +package com.helible.pilot.viewmodels import android.content.SharedPreferences import com.squareup.moshi.JsonAdapter diff --git a/app/src/main/res/drawable/cancel.xml b/app/src/main/res/drawable/cancel.xml new file mode 100644 index 0000000..dae6d0f --- /dev/null +++ b/app/src/main/res/drawable/cancel.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/code_blocks.xml b/app/src/main/res/drawable/code_blocks.xml new file mode 100644 index 0000000..746f928 --- /dev/null +++ b/app/src/main/res/drawable/code_blocks.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/construction.xml b/app/src/main/res/drawable/construction.xml new file mode 100644 index 0000000..e779597 --- /dev/null +++ b/app/src/main/res/drawable/construction.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/controller_gen.xml b/app/src/main/res/drawable/controller_gen.xml new file mode 100644 index 0000000..0fb3858 --- /dev/null +++ b/app/src/main/res/drawable/controller_gen.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/energy.xml b/app/src/main/res/drawable/energy.xml new file mode 100644 index 0000000..73f35b3 --- /dev/null +++ b/app/src/main/res/drawable/energy.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/helicopter.png b/app/src/main/res/drawable/helicopter.png new file mode 100644 index 0000000..6363155 Binary files /dev/null and b/app/src/main/res/drawable/helicopter.png differ diff --git a/app/src/main/res/drawable/helicopter_icon.xml b/app/src/main/res/drawable/helicopter_icon.xml new file mode 100644 index 0000000..c1a58b3 --- /dev/null +++ b/app/src/main/res/drawable/helicopter_icon.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/instant_mix.xml b/app/src/main/res/drawable/instant_mix.xml new file mode 100644 index 0000000..9ef2e8c --- /dev/null +++ b/app/src/main/res/drawable/instant_mix.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/joystick.xml b/app/src/main/res/drawable/joystick.xml new file mode 100644 index 0000000..f8954f8 --- /dev/null +++ b/app/src/main/res/drawable/joystick.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/logout.xml b/app/src/main/res/drawable/logout.xml new file mode 100644 index 0000000..99cdf28 --- /dev/null +++ b/app/src/main/res/drawable/logout.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/sync.xml b/app/src/main/res/drawable/sync.xml new file mode 100644 index 0000000..7d55533 --- /dev/null +++ b/app/src/main/res/drawable/sync.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/tune.xml b/app/src/main/res/drawable/tune.xml new file mode 100644 index 0000000..45ca5af --- /dev/null +++ b/app/src/main/res/drawable/tune.xml @@ -0,0 +1,10 @@ + + +