xlsx export

This commit is contained in:
Vineyro 2024-08-06 17:18:20 +07:00
parent eca21cb3bf
commit 5293604ee4
7 changed files with 145 additions and 24 deletions

View File

@ -17,8 +17,8 @@ android {
applicationId "llc.arma.ble" applicationId "llc.arma.ble"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 35 versionCode 39
versionName "1.4.4" versionName "1.4.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@ -25,6 +25,7 @@ import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material.icons.rounded.Save
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
@ -38,10 +39,12 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.VerticalDivider import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -75,6 +78,7 @@ import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.BleName import llc.arma.ble.domain.model.BleName
import llc.arma.ble.domain.usecase.ExportToXlsx
import llc.arma.ble.domain.usecase.GetBleBySerial import llc.arma.ble.domain.usecase.GetBleBySerial
import llc.arma.ble.domain.usecase.GetBleNamesFlow import llc.arma.ble.domain.usecase.GetBleNamesFlow
import llc.arma.ble.domain.usecase.GetHostHistoryBySerial import llc.arma.ble.domain.usecase.GetHostHistoryBySerial
@ -148,6 +152,21 @@ fun HostHistory(
}, },
actions = { actions = {
IconButton(
onClick = {
viewModel.setEvent(HostHistoryContract.Event.OnExportHistory(ble.serial))
},
enabled = when(state){
is HostHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
HostHistoryContract.State.Exception -> true
}
) {
Icon(
imageVector = Icons.Rounded.Save,
contentDescription = null
)
}
IconButton( IconButton(
onClick = { onClick = {
viewModel.setEvent(HostHistoryContract.Event.OnRefreshHistory(ble.name, ble.serial)) viewModel.setEvent(HostHistoryContract.Event.OnRefreshHistory(ble.name, ble.serial))
@ -183,11 +202,10 @@ val dateFormatter = SimpleDateFormat("dd.MM", Locale.getDefault())
val timeFormatter = SimpleDateFormat("HH:mm", Locale.getDefault()) val timeFormatter = SimpleDateFormat("HH:mm", Locale.getDefault())
val colorsStack = listOf( val colorsStack = listOf(
-0x63d850, -0x98c549, -0xc0ae4b, -0xde690d, Color(0xffffd700), Color(0xff2f4f4f), Color(0xff7f0000), Color(0xFFFF0000),
-0xfc560c, -0xff432c, -0xff6978, -0xb350b0, Color(0xffa9a9a9), Color(0xff00fa9a), Color(0xff00ffff), Color(0xfff0e68c),
-0x743cb6, -0x3223c7, -0x14c5, -0x3ef9, Color(0xff00bfff), Color(0xff0000ff), Color(0xfff08080), Color(0xffadff2f),
-0x6800, -0xa8de, -0x86aab8, -0x616162, Color(0xffff00ff), Color(0xff4169e1), Color(0xffff1493), Color(0xffee82ee),
-0x9f8275, -0xcccccd, -0xbbcca
) )
val axisValueFormatter = val axisValueFormatter =
@ -246,8 +264,8 @@ fun Display(
}.toMap() }.toMap()
} }
var selectedSerials by remember { var selectedSerials by rememberSaveable {
mutableStateOf(allSerials) mutableStateOf(allSerials.take(1))
} }
val serials = remember(selectedSerials) { allSerials.filter { selectedSerials.contains(it) } } val serials = remember(selectedSerials) { allSerials.filter { selectedSerials.contains(it) } }
@ -274,7 +292,7 @@ fun Display(
val chart = columnChart( val chart = columnChart(
innerSpacing = 2.dp, innerSpacing = 2.dp,
columns = serials.map { LineComponent(color = colors[it]!!, thicknessDp = 7f, shape= pillShape) }, columns = serials.map { LineComponent(color = colors[it]!!.toArgb(), thicknessDp = 7f, shape= pillShape) },
spacing = 8.dp, spacing = 8.dp,
) )
@ -297,7 +315,7 @@ fun Display(
leadingIcon = { leadingIcon = {
Surface( Surface(
shape = CircleShape, shape = CircleShape,
color = Color(colors[s]!!), color = colors[s]!!,
modifier = Modifier.size(28.dp) modifier = Modifier.size(28.dp)
) {} ) {}
}, },
@ -464,7 +482,7 @@ class HostHistoryContract {
sealed class Event : ViewEvent { sealed class Event : ViewEvent {
object StopMeasure : Event() data object StopMeasure : Event()
data class OnStart( data class OnStart(
val bleName: String, val bleName: String,
@ -476,6 +494,10 @@ class HostHistoryContract {
val serial: String, val serial: String,
) : Event() ) : Event()
data class OnExportHistory(
val serial: String,
) : Event()
} }
sealed class State : ViewState { sealed class State : ViewState {
@ -502,7 +524,8 @@ class HostHistoryContract {
class HostHistoryViewModel @Inject constructor( class HostHistoryViewModel @Inject constructor(
private val getHostHistoryBySerial: GetHostHistoryBySerial, private val getHostHistoryBySerial: GetHostHistoryBySerial,
private val getBleBySerial: GetBleBySerial, private val getBleBySerial: GetBleBySerial,
private val getBleNamesFlow: GetBleNamesFlow private val getBleNamesFlow: GetBleNamesFlow,
private val exportToXlsx: ExportToXlsx
) : BaseViewModel<HostHistoryContract.State, HostHistoryContract.Event, HostHistoryContract.Effect>() { ) : BaseViewModel<HostHistoryContract.State, HostHistoryContract.Event, HostHistoryContract.Effect>() {
var measureJob: Job? = null var measureJob: Job? = null
@ -520,9 +543,27 @@ class HostHistoryViewModel @Inject constructor(
is HostHistoryContract.Event.OnStart -> reduce(viewState.value, event) is HostHistoryContract.Event.OnStart -> reduce(viewState.value, event)
is HostHistoryContract.Event.OnRefreshHistory -> reduce(viewState.value, event) is HostHistoryContract.Event.OnRefreshHistory -> reduce(viewState.value, event)
is HostHistoryContract.Event.StopMeasure -> reduce(viewState.value, event) is HostHistoryContract.Event.StopMeasure -> reduce(viewState.value, event)
is HostHistoryContract.Event.OnExportHistory -> reduce(viewState.value, event)
} }
} }
private fun reduce(
state: HostHistoryContract.State,
event: HostHistoryContract.Event.OnExportHistory
) {
if(state is HostHistoryContract.State.Display) {
if(state.loadingHistoryState is ProgressState.Finished) {
exportToXlsx.invoke(event.serial, state.loadingHistoryState.data)
}
}
}
private fun reduce( private fun reduce(
state: HostHistoryContract.State, state: HostHistoryContract.State,
event: HostHistoryContract.Event.StopMeasure event: HostHistoryContract.Event.StopMeasure

View File

@ -126,10 +126,6 @@ fun readHostHistory(
fun getInnerIndex(byte: Byte): Int{ fun getInnerIndex(byte: Byte): Int{
if(byte != 0.toByte()){
println(byte)
}
var bits = BitSet.valueOf(byteArrayOf(byte)) var bits = BitSet.valueOf(byteArrayOf(byte))
bits.clear(0, 4) bits.clear(0, 4)
bits = bits.get(4, 8) bits = bits.get(4, 8)
@ -146,7 +142,7 @@ fun readHostHistory(
fun getDevType(byte: Byte): Int{ fun getDevType(byte: Byte): Int{
var bits = BitSet.valueOf(byteArrayOf(byte)) var bits = BitSet.valueOf(byteArrayOf(byte))
bits.clear(5, 8) bits.clear(5, 9)
val arr = bits.toByteArray() val arr = bits.toByteArray()
if(arr.isEmpty()){ if(arr.isEmpty()){
@ -161,7 +157,7 @@ fun readHostHistory(
var bits = BitSet.valueOf(byteArrayOf(byte)) var bits = BitSet.valueOf(byteArrayOf(byte))
bits.clear(0, 5) bits.clear(0, 5)
bits = bits.get(4, 8) bits = bits.get(5, 8)
val arr = bits.toByteArray() val arr = bits.toByteArray()
if(arr.isEmpty()){ if(arr.isEmpty()){
@ -204,8 +200,10 @@ fun readHostHistory(
println("table serial $serial") println("table serial $serial")
val devType = getDevType(devTypeByte) val devType = getDevType(devTypeByte)
println("devType $devType")
val devDataSize = getDevDataSize(devTypeByte) val devDataSize = getDevDataSize(devTypeByte)
println("payload $devDataSize")
bleTableOffset += 2 bleTableOffset += 2
if (devDataSize != 0) { if (devDataSize != 0) {

View File

@ -3,7 +3,13 @@ package llc.arma.ble.data.repository
import android.app.Application import android.app.Application
import android.icu.text.SimpleDateFormat import android.icu.text.SimpleDateFormat
import android.os.Environment import android.os.Environment
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import llc.arma.ble.R import llc.arma.ble.R
import llc.arma.ble.app.ui.screen.inspection.host.view.HostEntry
import llc.arma.ble.app.ui.screen.inspection.host.view.colorsStack
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.XlsxRepository import llc.arma.ble.domain.repository.XlsxRepository
import org.apache.poi.ss.usermodel.WorkbookFactory import org.apache.poi.ss.usermodel.WorkbookFactory
@ -20,13 +26,13 @@ class XlsxRepositoryImpl @Inject constructor(
private val application: Application private val application: Application
) : XlsxRepository { ) : XlsxRepository {
override fun exportToXls( override fun exportAccelDataToXls(
bleName: String, bleName: String,
data: List<Ble.Accelerometer.HistoryPoint> data: List<Ble.Accelerometer.HistoryPoint>
): File { ): File {
val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm") val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd_HH-mm")
val fileName = "${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx".replace(' ', '_') val fileName = "${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx"
val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm") val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm")
@ -105,4 +111,64 @@ class XlsxRepositoryImpl @Inject constructor(
return mailFile return mailFile
} }
override fun exportHostDataToXls(bleName: String, data: List<Ble.Host.HistoryPoint>): File {
val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd_HH-mm")
val fileName = "${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx"
val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm")
File(application.filesDir.absolutePath).mkdirs()
val mailFile = File(application.filesDir, fileName)
mailFile.createNewFile()
IOUtils.copy(application.resources.openRawResource(R.raw.host_history), FileOutputStream(mailFile))
val fileIn = FileInputStream(mailFile)
val workbook = WorkbookFactory.create(fileIn)
val worksheet = workbook.getSheetAt(0) as XSSFSheet
val row = worksheet.createRow(0)
row.createCell(0).setCellValue("Date")
data.forEachIndexed { index, historyPoint ->
row.createCell(index).setCellValue(formatter.format(Date(historyPoint.date)))
}
data.flatMap { it.value }.distinct().forEachIndexed { rowIndex, serial ->
val row = worksheet.createRow(rowIndex + 1)
row.createCell(0).setCellValue(serial)
data.forEachIndexed { index, historyPoint ->
row.createCell(index + 1)
.setCellValue(
if(historyPoint.value.contains(serial)) 1.0 else 0.0
)
}
}
fileIn.close()
val saveFos = FileOutputStream(mailFile)
workbook.write(saveFos)
saveFos.close()
println(fileName)
val sharedFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
sharedFile.createNewFile()
val sharedSaveFos = FileOutputStream(sharedFile)
workbook.write(sharedSaveFos)
sharedSaveFos.close()
workbook.close()
return mailFile
}
} }

View File

@ -5,9 +5,14 @@ import java.io.File
interface XlsxRepository { interface XlsxRepository {
fun exportToXls( fun exportAccelDataToXls(
bleName: String, bleName: String,
data: List<Ble.Accelerometer.HistoryPoint> data: List<Ble.Accelerometer.HistoryPoint>
): File ): File
fun exportHostDataToXls(
bleName: String,
data: List<Ble.Host.HistoryPoint>
): File
} }

View File

@ -15,7 +15,18 @@ class ExportToXlsx @Inject constructor(
data: List<Ble.Accelerometer.HistoryPoint> data: List<Ble.Accelerometer.HistoryPoint>
){ ){
val file = xlsxRepository.exportToXls(bleName, data) val file = xlsxRepository.exportAccelDataToXls(bleName, data)
emailRepository.sendFile(file)
}
@JvmName("invokeHost")
operator fun invoke(
bleName: String,
data: List<Ble.Host.HistoryPoint>
){
val file = xlsxRepository.exportHostDataToXls(bleName, data)
emailRepository.sendFile(file) emailRepository.sendFile(file)
} }

Binary file not shown.