mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-23 17:34:28 +01:00
Add views to DFU module
This commit is contained in:
@@ -60,7 +60,9 @@ dependencies {
|
||||
implementation project(':profile_hts')
|
||||
implementation project(':profile_prx')
|
||||
implementation project(':profile_rscs')
|
||||
|
||||
implementation project(':profile_uart')
|
||||
implementation project(':profile_dfu')
|
||||
|
||||
implementation project(":lib_theme")
|
||||
implementation project(":lib_utils")
|
||||
|
||||
@@ -30,6 +30,7 @@ import no.nordicsemi.android.prx.view.PRXScreen
|
||||
import no.nordicsemi.android.rscs.view.RSCSScreen
|
||||
import no.nordicsemi.android.theme.view.CloseIconAppBar
|
||||
import no.nordicsemi.android.uart.view.UARTScreen
|
||||
import no.nordicsemi.dfu.view.DFUScreen
|
||||
import no.nordicsemi.ui.scanner.navigation.view.FindDeviceScreen
|
||||
|
||||
@Composable
|
||||
@@ -115,6 +116,12 @@ internal fun HomeScreen() {
|
||||
UARTScreen { goHome() }
|
||||
}
|
||||
}
|
||||
composable(NavDestination.DFU.id) {
|
||||
FindDeviceScreen(ParcelUuid(NavDestination.DFU.uuid)) {
|
||||
deviceHolder.onDeviceSelected(it)
|
||||
DFUScreen { goHome() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +268,22 @@ fun HomeView(callback: (NavDestination) -> Unit) {
|
||||
) { callback(NavDestination.UART) }
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
FeatureButton(
|
||||
R.drawable.ic_uart, R.string.uart_module,
|
||||
R.string.uart_module_full
|
||||
) { callback(NavDestination.DFU) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,5 +23,6 @@ enum class NavDestination(val id: String, val uuid: UUID?, val pairingRequired:
|
||||
PRX("prx-screen", PRX_SERVICE_UUID, true),
|
||||
RSCS("rscs-screen", RSCS_SERVICE_UUID, false),
|
||||
CGMS("cgms-screen", CGMS_SERVICE_UUID, false),
|
||||
UART("uart-screen", UART_SERVICE_UUID, false);
|
||||
UART("uart-screen", UART_SERVICE_UUID, false),
|
||||
DFU("dfu-screen", null, false); //todo check characteristic
|
||||
}
|
||||
|
||||
21
app/src/main/res/drawable/ic_dfu.xml
Normal file
21
app/src/main/res/drawable/ic_dfu.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="80dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#00B3DC"
|
||||
android:pathData="M316.8,389.3c-2.2,0 -4,1.8 -4,4v238.3c0,2.2 1.8,4 4,4h49.5c40.1,0 61.9,-21.8 61.9,-62.3V451.5c0,-40.5 -21.8,-62.3 -61.9,-62.3H316.8zM390.9,454v116.8c0,20.8 -8.1,30.3 -26,30.3h-10.8c-2.2,0 -4,-1.8 -4,-4V427.8c0,-2.2 1.8,-4 4,-4h10.8C382.8,423.8 390.9,433.3 390.9,454z" />
|
||||
<path
|
||||
android:fillColor="#00B3DC"
|
||||
android:pathData="M565.2,389.3h-95.1c-2.2,0 -4,1.8 -4,4v238.3c0,2.2 1.8,4 4,4h29.3c2.2,0 4,-1.8 4,-4v-99.6c0,-2.2 1.8,-4 4,-4h44.7c2.2,0 4,-1.8 4,-4v-26.4c0,-2.2 -1.8,-4 -4,-4h-44.7c-2.2,0 -4,-1.8 -4,-4v-61.6c0,-2.2 1.8,-4 4,-4h57.8c2.2,0 4,-1.8 4,-4v-26.4C569.2,391.1 567.4,389.3 565.2,389.3z" />
|
||||
<path
|
||||
android:fillColor="#00B3DC"
|
||||
android:pathData="M714,580.3v-187c0,-2.2 -1.8,-4 -4,-4h-29.3c-2.2,0 -4,1.8 -4,4v186c0,17.9 -8.1,24.6 -21.1,24.6c-13,0 -21.1,-6.7 -21.1,-24.6v-186c0,-2.2 -1.8,-4 -4,-4h-27.8c-2.2,0 -4,1.8 -4,4v187c0,38.7 22.2,58.8 57.4,58.8C691.8,639.1 714,619 714,580.3z" />
|
||||
<path
|
||||
android:fillColor="#00B3DC"
|
||||
android:pathData="M410.7,821.8c-3.4,-4.6 -12,-5.4 -13.5,-1.3l-13.9,38.5C191.3,787.7 92,574.1 161.8,381.2c34,-93.9 102.5,-169 192.9,-211.4c14.3,-6.7 20.4,-23.7 13.7,-37.9c-6.7,-14.3 -23.7,-20.4 -37.9,-13.7c-50.7,23.7 -95.6,56.5 -133.4,97.4c-39.2,42.3 -69.1,91.5 -88.9,146.2C88.4,416.5 79.9,473.5 83,531.1c3,55.6 16.5,109.5 40.2,160.2c23.7,50.7 56.5,95.6 97.4,133.4c41.6,38.5 89.8,68.1 143.3,87.8l-12.8,35.4c-1.5,4.1 5.8,9 11.3,7.6l119.7,-29.7c3.3,-0.8 4.2,-3.6 2.2,-6.3L410.7,821.8z" />
|
||||
<path
|
||||
android:fillColor="#00B3DC"
|
||||
android:pathData="M943.8,485.8c-3,-55.6 -16.5,-109.5 -40.2,-160.2c-23.7,-50.7 -56.5,-95.6 -97.4,-133.4c-40.7,-37.7 -87.8,-66.8 -140,-86.6l10.7,-29.5c1.5,-4.1 -5.8,-9 -11.3,-7.6L545.8,98.1c-3.3,0.8 -4.2,3.6 -2.2,6.3l73.6,97.8c3.4,4.6 12,5.4 13.5,1.3l16,-44.4C836.6,231.6 934.3,443.8 865,635.6C831,729.5 762.5,804.6 672,847c-14.3,6.7 -20.4,23.7 -13.7,37.9c4.8,10.3 15.1,16.4 25.8,16.4c4.1,0 8.2,-0.9 12.1,-2.7c50.7,-23.7 95.6,-56.5 133.4,-97.4c39.2,-42.3 69.1,-91.5 88.9,-146.2S946.8,543.4 943.8,485.8z" />
|
||||
</vector>
|
||||
@@ -17,4 +17,6 @@
|
||||
<string name="cgm_module_full">Continuous Glucose</string>
|
||||
<string name="uart_module">UART</string>
|
||||
<string name="uart_module_full">Serial port over BLE</string>
|
||||
</resources>
|
||||
<string name="dfu_module">DFU</string>
|
||||
<string name="dfu_module_full">Device Firmware Update</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
package no.nordicsemi.dfu.data
|
||||
|
||||
class DFUData {
|
||||
}
|
||||
import java.io.File
|
||||
|
||||
internal sealed class DFUData
|
||||
|
||||
internal object NoFileSelectedState : DFUData()
|
||||
|
||||
internal data class FileReadyState(val file: File, val isUploading: Boolean) : DFUData()
|
||||
|
||||
internal object UploadSuccessState : DFUData()
|
||||
|
||||
internal object UploadFailureState : DFUData()
|
||||
|
||||
@@ -8,7 +8,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
internal class DFUDataHolder @Inject constructor() {
|
||||
|
||||
private val _data = MutableStateFlow(DFUData())
|
||||
private val _data = MutableStateFlow(NoFileSelectedState)
|
||||
val data: StateFlow<DFUData> = _data
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import no.nordicsemi.dfu.data.DFUData
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import no.nordicsemi.dfu.data.*
|
||||
|
||||
@Composable
|
||||
internal fun DFUContentView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) {
|
||||
when (state) {
|
||||
NoFileSelectedState -> DFUSelectFileView()
|
||||
is FileReadyState -> FileReadyView(state, onEvent)
|
||||
UploadFailureState -> DFUErrorView(onEvent)
|
||||
UploadSuccessState -> DFUSuccessView(onEvent)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FileReadyView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) {
|
||||
when (state.isUploading) {
|
||||
true -> DFUInstallingView(onEvent)
|
||||
false -> DFUSummaryView(onEvent)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import no.nordicsemi.dfu.R
|
||||
|
||||
@Composable
|
||||
fun DFUErrorView() {
|
||||
internal fun DFUErrorView(onEvent: (DFUViewEvent) -> Unit) {
|
||||
|
||||
Column {
|
||||
Button(onClick = { onEvent(OnPauseButtonClick) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_done))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import no.nordicsemi.android.material.you.CircularProgressIndicator
|
||||
import no.nordicsemi.dfu.R
|
||||
|
||||
@Composable
|
||||
fun DFUInstallingView() {
|
||||
internal fun DFUInstallingView(onEvent: (DFUViewEvent) -> Unit) {
|
||||
|
||||
Column {
|
||||
CircularProgressIndicator()
|
||||
|
||||
//todo add percentage indicator
|
||||
|
||||
Button(onClick = { onEvent(OnPauseButtonClick) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_pause))
|
||||
}
|
||||
|
||||
Button(onClick = { onEvent(OnPauseButtonClick) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_stop))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,103 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import no.nordicsemi.android.dfu.DfuBaseService
|
||||
import no.nordicsemi.android.utils.EMPTY
|
||||
import no.nordicsemi.dfu.R
|
||||
|
||||
@Composable
|
||||
fun DFUSelectFileView() {
|
||||
internal fun DFUSelectFileView() {
|
||||
|
||||
val result = remember { mutableStateOf<Uri?>(null) }
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
result.value = it
|
||||
}
|
||||
|
||||
Row {
|
||||
Button(onClick = { launcher.launch(DfuBaseService.MIME_TYPE_ZIP) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_select_zip))
|
||||
}
|
||||
|
||||
Button(onClick = { launcher.launch(DfuBaseService.MIME_TYPE_OCTET_STREAM) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_select_hex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChooseFileMangerDialog(onDismiss: () -> Unit) {
|
||||
val alias = remember { mutableStateOf(String.EMPTY) }
|
||||
val command = remember { mutableStateOf(String.EMPTY) }
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.dfu_macro_dialog_title))
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(stringResource(id = R.string.dfu_macro_dialog_info))
|
||||
|
||||
FileManagerOption.values().forEach {
|
||||
FileManagerItem(item = it) {
|
||||
openFileMangerPlayStore(context, it.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = { onDismiss() }
|
||||
) {
|
||||
Text(stringResource(id = R.string.dfu_macro_dialog_dismiss))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun openFileMangerPlayStore(context: Context, url: String) {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FileManagerItem(item: FileManagerOption, onItemSelected: (FileManagerOption) -> Unit) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onItemSelected(item) }
|
||||
) {
|
||||
Text(text = item.title)
|
||||
}
|
||||
}
|
||||
|
||||
enum class FileManagerOption(val title: String, val url: String) {
|
||||
DRIVE("Drive", "market://details?id=com.google.android.apps.docs"),
|
||||
FILE_MANAGER("File Manager", "market://details?id=com.rhmsoft.fm"),
|
||||
TOTAL_COMMANDER("Total Commander", "market://details?id=com.ghisler.android.TotalCommander"),
|
||||
OTHERS("Search for others", "market://search?q=file manager"),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import no.nordicsemi.dfu.R
|
||||
|
||||
@Composable
|
||||
fun DFUSuccessView() {
|
||||
internal fun DFUSuccessView(onEvent: (DFUViewEvent) -> Unit) {
|
||||
|
||||
Column {
|
||||
Button(onClick = { onEvent(OnPauseButtonClick) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_done))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package no.nordicsemi.dfu.view
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import no.nordicsemi.android.material.you.CircularProgressIndicator
|
||||
import no.nordicsemi.dfu.R
|
||||
|
||||
@Composable
|
||||
internal fun DFUSummaryView(onEvent: (DFUViewEvent) -> Unit) {
|
||||
|
||||
Column {
|
||||
CircularProgressIndicator()
|
||||
|
||||
//todo add percentage indicator
|
||||
|
||||
Button(onClick = { onEvent(OnPauseButtonClick) }) {
|
||||
Text(text = stringResource(id = R.string.dfu_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,10 @@ package no.nordicsemi.dfu.view
|
||||
|
||||
internal sealed class DFUViewEvent
|
||||
|
||||
internal data class OnFileSelected(val uri: String) : DFUViewEvent()
|
||||
|
||||
internal object OnPauseButtonClick : DFUViewEvent()
|
||||
|
||||
internal object OnStopButtonClick : DFUViewEvent()
|
||||
|
||||
internal object OnDisconnectButtonClick : DFUViewEvent()
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="dfu_title">DFU</string>
|
||||
|
||||
<string name="dfu_done">Done</string>
|
||||
<string name="dfu_pause">Pause</string>
|
||||
<string name="dfu_stop">Stop</string>
|
||||
<string name="dfu_install">Install</string>
|
||||
|
||||
<string name="dfu_select_zip">Select .zip</string>
|
||||
<string name="dfu_select_hex">Select .hex</string>
|
||||
|
||||
<string name="dfu_macro_dialog_title">File managers</string>
|
||||
<string name="dfu_macro_dialog_info">Please select </string>
|
||||
<string name="dfu_macro_dialog_confirm">Confirm</string>
|
||||
<string name="dfu_macro_dialog_dismiss">Dismiss</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user