Remove hex & bin files from DFU.

This commit is contained in:
Sylwester Zieliński
2022-01-11 11:00:30 +01:00
parent 0993799e42
commit 27e71dc0a3
16 changed files with 61 additions and 232 deletions

View File

@@ -9,15 +9,10 @@ internal data class NoFileSelectedState(
) : DFUData()
internal data class FileReadyState(
val file: DFUFile,
val file: ZipFile,
val device: DiscoveredBluetoothDevice
) : DFUData()
internal data class HexFileLoadedState(
val file: PartialHexFile,
val isDatFileError: Boolean = false
) : DFUData()
internal data class FileInstallingState(
val status: DFUServiceStatus = Idle
) : DFUData()

View File

@@ -2,26 +2,7 @@ package no.nordicsemi.dfu.data
import android.net.Uri
sealed class DFUFile {
abstract val fileType: DFUFileType
}
data class ZipFile(val data: FileData) : DFUFile() {
override val fileType: DFUFileType = DFUFileType.TYPE_AUTO
}
data class PartialHexFile(
val data: FileData,
val fileType: DFUFileType
)
data class FullHexFile(
val data: FileData,
val datFileData: FileData,
override val fileType: DFUFileType
) : DFUFile()
data class FileData(
data class ZipFile(
val uri: Uri,
val name: String,
val path: String?,

View File

@@ -15,7 +15,7 @@ class DFUFileManager @Inject constructor(
private val TAG = "DFU_FILE_MANAGER"
fun createFile(uri: Uri): FileData? {
fun createFile(uri: Uri): ZipFile? {
return try {
createFromFile(uri)
} catch (e: Exception) {
@@ -29,12 +29,12 @@ class DFUFileManager @Inject constructor(
}
}
private fun createFromFile(uri: Uri): FileData {
private fun createFromFile(uri: Uri): ZipFile {
val file = uri.toFile()
return FileData(uri, file.name, file.path, file.length())
return ZipFile(uri, file.name, file.path, file.length())
}
private fun createFromContentResolver(uri: Uri): FileData? {
private fun createFromContentResolver(uri: Uri): ZipFile? {
val data = context.contentResolver.query(uri, null, null, null, null)
return if (data != null && data.moveToNext()) {
@@ -53,7 +53,7 @@ class DFUFileManager @Inject constructor(
data.close()
FileData(uri, fileName, filePath, fileSize.toLong())
ZipFile(uri, fileName, filePath, fileSize.toLong())
} else {
Log.d(TAG, "Data loaded from ContentResolver is empty.")
null

View File

@@ -1,16 +0,0 @@
package no.nordicsemi.dfu.data
import no.nordicsemi.android.dfu.DfuBaseService
enum class DFUFileType(val id: Int) {
TYPE_AUTO(DfuBaseService.TYPE_AUTO),
TYPE_SOFT_DEVICE(DfuBaseService.TYPE_SOFT_DEVICE),
TYPE_BOOTLOADER(DfuBaseService.TYPE_BOOTLOADER),
TYPE_APPLICATION(DfuBaseService.TYPE_APPLICATION);
companion object {
fun create(id: Int): DFUFileType? {
return values().find { it.id == id }
}
}
}

View File

@@ -5,7 +5,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import no.nordicsemi.android.dfu.DfuServiceInitiator
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.dfu.repository.DFUService
import no.nordicsemi.ui.scanner.ui.exhaustive
import javax.inject.Inject
class DFUManager @Inject constructor(
@@ -14,7 +13,7 @@ class DFUManager @Inject constructor(
private val deviceHolder: SelectedBluetoothDeviceHolder
) {
fun install(file: DFUFile) {
fun install(file: ZipFile) {
val device = deviceHolder.device!!
val starter = DfuServiceInitiator(device.address())
@@ -26,12 +25,7 @@ class DFUManager @Inject constructor(
.setPrepareDataObjectDelay(400)
.setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true)
when (file) {
is ZipFile -> starter.setZip(file.data.uri, file.data.path)
is FullHexFile -> starter.setBinOrHex(file.fileType.id, file.data.uri, file.data.path)
.setInitFile(file.datFileData.uri, file.datFileData.path)
}.exhaustive
starter.setZip(file.uri, file.path)
starter.start(context, DFUService::class.java)
}
}

View File

@@ -20,24 +20,10 @@ internal class DFURepository @Inject constructor(
fun setZipFile(file: Uri) {
val currentState = _data.value as NoFileSelectedState
_data.value = fileManger.createFile(file)?.let {
FileReadyState(ZipFile(it), requireNotNull(deviceHolder.device))
FileReadyState(it, requireNotNull(deviceHolder.device))
} ?: currentState.copy(isError = true)
}
fun setHexFile(file: Uri) {
val currentState = _data.value as NoFileSelectedState
_data.value = fileManger.createFile(file)?.let {
HexFileLoadedState(PartialHexFile(it, DFUFileType.TYPE_APPLICATION))
} ?: currentState.copy(isError = true)
}
fun setDatFile(file: Uri) {
val currentState = _data.value as HexFileLoadedState
_data.value = fileManger.createFile(file)?.let {
FileReadyState(FullHexFile(it, currentState.file.data, DFUFileType.TYPE_APPLICATION), requireNotNull(deviceHolder.device))
} ?: currentState.copy(isDatFileError = true)
}
fun setSuccess() {
_data.value = UploadSuccessState
}

View File

@@ -14,7 +14,6 @@ internal fun DFUContentView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) {
when (state) {
is NoFileSelectedState -> DFUSelectMainFileView(state, onEvent)
is FileReadyState -> DFUSummaryView(state, onEvent)
is HexFileLoadedState -> DFUSelectDatFileView(state, onEvent)
UploadSuccessState -> DFUSuccessView(onEvent)
is UploadFailureState -> DFUErrorView(state, onEvent)
is FileInstallingState -> DFUInstallingView(state, onEvent)

View File

@@ -21,18 +21,22 @@ import no.nordicsemi.dfu.data.UploadFailureState
internal fun DFUErrorView(state: UploadFailureState, onEvent: (DFUViewEvent) -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
ScreenSection {
val errorColor = MaterialTheme.colorScheme.error
Icon(
painter = painterResource(id = R.drawable.ic_fail_circle),
contentDescription = stringResource(id = R.string.dfu_failure_icon_description),
tint = MaterialTheme.colorScheme.error
tint = errorColor
)
Spacer(modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.size(8.dp))
val error = state.message ?: stringResource(id = R.string.dfu_unknown_error)
Text(
text = error,
color = MaterialTheme.colorScheme.error
color = errorColor,
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.size(16.dp))

View File

@@ -1,19 +1,13 @@
package no.nordicsemi.dfu.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.material.you.CircularProgressIndicator
import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.dfu.R
import no.nordicsemi.dfu.data.FileInstallingState
@Composable
@@ -24,19 +18,5 @@ internal fun DFUInstallingView(state: FileInstallingState, onEvent: (DFUViewEven
Spacer(modifier = Modifier.height(16.dp))
Text(text = state.status.toDisplayString())
Spacer(modifier = Modifier.height(16.dp))
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
Button(onClick = { onEvent(OnPauseButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_pause))
}
Spacer(modifier = Modifier.size(16.dp))
Button(onClick = { onEvent(OnPauseButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_stop))
}
}
}
}

View File

@@ -1,58 +0,0 @@
package no.nordicsemi.dfu.view
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.dfu.DfuBaseService
import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.android.theme.view.SectionTitle
import no.nordicsemi.dfu.R
import no.nordicsemi.dfu.data.HexFileLoadedState
@Composable
internal fun DFUSelectDatFileView(state: HexFileLoadedState, onEvent: (DFUViewEvent) -> Unit) {
ScreenSection {
SectionTitle(
icon = Icons.Default.Settings,
title = stringResource(id = R.string.dfu_choose_file)
)
Spacer(modifier = Modifier.size(8.dp))
Text(
text = stringResource(id = R.string.dfu_choose_dat_info),
style = MaterialTheme.typography.bodyMedium
)
if (state.isDatFileError) {
Spacer(modifier = Modifier.size(8.dp))
Text(
text = stringResource(id = R.string.dfu_load_file_error),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.size(8.dp))
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let { onEvent(OnDatFileSelected(it)) }
}
Button(onClick = { launcher.launch(DfuBaseService.MIME_TYPE_OCTET_STREAM) }) {
Text(text = stringResource(id = R.string.dfu_select_dat))
}
}
}

View File

@@ -2,11 +2,7 @@ package no.nordicsemi.dfu.view
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
@@ -62,31 +58,13 @@ private fun ButtonsRow(onEvent: (DFUViewEvent) -> Unit) {
val fileType = rememberSaveable { mutableStateOf(DfuBaseService.MIME_TYPE_ZIP) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let {
if (fileType.value == DfuBaseService.MIME_TYPE_ZIP) {
onEvent(OnZipFileSelected(it))
} else {
onEvent(OnHexFileSelected(it))
}
}
uri?.let { onEvent(OnZipFileSelected(it)) }
}
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth()
) {
Button(onClick = {
fileType.value = DfuBaseService.MIME_TYPE_ZIP
launcher.launch(fileType.value)
}) {
Text(text = stringResource(id = R.string.dfu_select_zip))
}
Button(onClick = {
fileType.value = DfuBaseService.MIME_TYPE_OCTET_STREAM
launcher.launch(fileType.value)
}) {
Text(text = stringResource(id = R.string.dfu_select_hex))
}
Button(onClick = {
fileType.value = DfuBaseService.MIME_TYPE_ZIP
launcher.launch(fileType.value)
}) {
Text(text = stringResource(id = R.string.dfu_select_zip))
}
}

View File

@@ -21,22 +21,25 @@ import no.nordicsemi.dfu.R
internal fun DFUSuccessView(onEvent: (DFUViewEvent) -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
ScreenSection {
val successColor = colorResource(id = no.nordicsemi.android.material.you.R.color.nordicGrass)
Icon(
painter = painterResource(id = R.drawable.ic_success_circle),
contentDescription = stringResource(id = R.string.dfu_success_icon_description),
tint = colorResource(id = no.nordicsemi.android.material.you.R.color.nordicGrass)
tint = successColor
)
Spacer(modifier = Modifier.size(8.dp))
Text(
text = stringResource(id = R.string.dfu_success),
color = successColor,
style = MaterialTheme.typography.titleLarge
)
}
Spacer(modifier = Modifier.size(16.dp))
Text(
text = stringResource(id = R.string.dfu_success),
color = MaterialTheme.colorScheme.error
)
Spacer(modifier = Modifier.size(16.dp))
Button(onClick = { onEvent(OnPauseButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_done))
}

View File

@@ -27,22 +27,17 @@ import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.android.theme.view.SectionTitle
import no.nordicsemi.dfu.R
import no.nordicsemi.dfu.data.FileReadyState
import no.nordicsemi.dfu.data.FullHexFile
import no.nordicsemi.dfu.data.ZipFile
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import no.nordicsemi.ui.scanner.ui.exhaustive
@Composable
internal fun DFUSummaryView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
DeviceDetailsView(state.device)
Spacer(modifier = Modifier.height(16.dp))
when (state.file) {
is FullHexFile -> FileDetailsView(state.file)
is ZipFile -> FileDetailsView(state.file)
}.exhaustive
FileDetailsView(state.file)
Spacer(modifier = Modifier.height(16.dp))
@@ -73,9 +68,11 @@ internal fun DeviceDetailsView(device: DiscoveredBluetoothDevice) {
Spacer(modifier = Modifier.size(8.dp))
Column(modifier = Modifier
.fillMaxWidth()
.weight(1f)) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
Text(
text = device.displayName(),
style = MaterialTheme.typography.titleMedium
@@ -88,36 +85,26 @@ internal fun DeviceDetailsView(device: DiscoveredBluetoothDevice) {
@Composable
private fun FileDetailsView(file: ZipFile) {
val fileName = file.data.name
val fileLength = file.data.size
val fileName = file.name
val fileLength = file.size
ScreenSection {
SectionTitle(icon = Icons.Default.Notifications, title = stringResource(id = R.string.dfu_zip_file_details))
SectionTitle(
icon = Icons.Default.Notifications,
title = stringResource(id = R.string.dfu_zip_file_details)
)
Spacer(modifier = Modifier.size(16.dp))
Text(text = fileName)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
) {
Text(text = stringResource(id = R.string.dfu_file_name, fileName))
Spacer(modifier = Modifier.size(4.dp))
Text(text = stringResource(id = R.string.dfu_file_size, fileLength))
}
}
@Composable
private fun FileDetailsView(file: FullHexFile) {
val fileName = file.data.name
val fileLength = file.data.size
ScreenSection {
SectionTitle(icon = Icons.Default.Notifications, title = stringResource(id = R.string.dfu_hex_file_details))
Spacer(modifier = Modifier.size(16.dp))
Text(text = fileName)
Spacer(modifier = Modifier.size(4.dp))
Text(text = stringResource(id = R.string.dfu_file_size, fileLength))
Spacer(modifier = Modifier.size(4.dp))
Text(text = stringResource(id = R.string.dfu_file_size, fileLength))
}
}
}

View File

@@ -5,8 +5,6 @@ import android.net.Uri
internal sealed class DFUViewEvent
internal data class OnZipFileSelected(val file: Uri) : DFUViewEvent()
internal data class OnHexFileSelected(val file: Uri) : DFUViewEvent()
internal data class OnDatFileSelected(val file: Uri) : DFUViewEvent()
internal object OnInstallButtonClick : DFUViewEvent()

View File

@@ -9,7 +9,6 @@ import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.dfu.data.Completed
import no.nordicsemi.dfu.data.DFUFile
import no.nordicsemi.dfu.data.DFUManager
import no.nordicsemi.dfu.data.DFUProgressManager
import no.nordicsemi.dfu.data.DFURepository
@@ -18,10 +17,9 @@ import no.nordicsemi.dfu.data.Error
import no.nordicsemi.dfu.data.FileInstallingState
import no.nordicsemi.dfu.data.FileReadyState
import no.nordicsemi.dfu.data.NoFileSelectedState
import no.nordicsemi.dfu.data.ZipFile
import no.nordicsemi.dfu.view.DFUViewEvent
import no.nordicsemi.dfu.view.OnDatFileSelected
import no.nordicsemi.dfu.view.OnDisconnectButtonClick
import no.nordicsemi.dfu.view.OnHexFileSelected
import no.nordicsemi.dfu.view.OnInstallButtonClick
import no.nordicsemi.dfu.view.OnPauseButtonClick
import no.nordicsemi.dfu.view.OnStopButtonClick
@@ -55,9 +53,7 @@ internal class DFUViewModel @Inject constructor(
}
OnPauseButtonClick -> closeScreen()
OnStopButtonClick -> closeScreen()
is OnHexFileSelected -> repository.setHexFile(event.file)
is OnZipFileSelected -> repository.setZipFile(event.file)
is OnDatFileSelected -> repository.setDatFile(event.file)
}.exhaustive
}
@@ -67,7 +63,7 @@ internal class DFUViewModel @Inject constructor(
finish()
}
private fun requireFile(): DFUFile {
private fun requireFile(): ZipFile {
return (repository.data.value as FileReadyState).file
}

View File

@@ -17,7 +17,6 @@
<string name="dfu_zip_file_details">Zip file details</string>
<string name="dfu_hex_file_details">Hex file details</string>
<string name="dfu_file_size">%d bytes</string>
<string name="dfu_file_type_zip">Distribution packet (ZIP)</string>
<string name="dfu_file_type_soft_device">Soft Device</string>
<string name="dfu_file_type_bootloader">Bootloader</string>
@@ -52,4 +51,7 @@
<string name="dfu_success">Success!</string>
<string name="dfu_unknown_error">Unknown error.</string>
<string name="dfu_file_name">File name: <b>%s</b></string>
<string name="dfu_file_size">File size: <b>%d</b> bytes</string>
</resources>