diff --git a/app/build.gradle b/app/build.gradle index b1d199d..9bf6b73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,9 +1,11 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id("org.jetbrains.kotlin.plugin.serialization") id 'kotlin-kapt' id 'dagger.hilt.android.plugin' id("kotlin-parcelize") + id("androidx.room") } @@ -17,8 +19,8 @@ android { applicationId "llc.arma.ble" minSdk 26 targetSdk 34 - versionCode 39 - versionName "1.4.9" + versionCode 41 + versionName "1.4.12" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -28,11 +30,11 @@ android { buildTypes { debug { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -107,6 +109,9 @@ dependencies { kapt("androidx.room:room-compiler:2.6.1") implementation("androidx.room:room-ktx:2.6.1") + implementation("androidx.datastore:datastore-preferences:1.1.1") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3") + implementation files('libs/poishadow-all.jar') } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb43..76ef6a3 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,189 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-dontwarn org.apache.** +-dontwarn org.openxmlformats.schemas.** +-dontwarn org.etsi.** +-dontwarn org.w3.** +-dontwarn com.microsoft.schemas.** +-dontwarn com.graphbuilder.** +-dontnote org.apache.** +-dontnote org.openxmlformats.schemas.** +-dontnote org.etsi.** +-dontnote org.w3.** +-dontnote com.microsoft.schemas.** +-dontnote com.graphbuilder.** + +-keeppackagenames org.apache.poi.ss.formula.function + +-keep class com.fasterxml.aalto.stax.InputFactoryImpl +-keep class com.fasterxml.aalto.stax.OutputFactoryImpl +-keep class com.fasterxml.aalto.stax.EventFactoryImpl + +-keep class schemaorg_apache_xmlbeans.system.sF1327CCA741569E70F9CA8C9AF9B44B2.TypeSystemHolder { public final static *** typeSystem; } + +-keep class org.apache.xmlbeans.impl.schema.BuiltinSchemaTypeSystem { public static *** get(...); public static *** getNoType(...); } +-keep class org.apache.xmlbeans.impl.schema.PathResourceLoader { public (...); } +-keep class org.apache.xmlbeans.impl.schema.SchemaTypeSystemCompiler { public static *** compile(...); } +-keep class org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl { public (...); public static *** get(...); public static *** getNoType(...); } +-keep class org.apache.xmlbeans.impl.schema.SchemaTypeLoaderImpl { public static *** getContextTypeLoader(...); public static *** build(...); } +-keep class org.apache.xmlbeans.impl.store.Locale { public static *** streamToNode(...); public static *** nodeTo*(...); } +-keep class org.apache.xmlbeans.impl.store.Path { public static *** compilePath(...); } +-keep class org.apache.xmlbeans.impl.store.Query { public static *** compileQuery(...); } + +-keep class com.google.errorprone.annotations.MustBeClosed { *; } + +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAuthors { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBooleanProperty { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorders { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorderPr { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellAlignment { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellStyleXfs { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellXfs { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFills { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFonts { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontName { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontScheme { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontSize { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTIntProperty { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTLegacyDrawing { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmts { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPatternFill { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPane { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSelection { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetDimension { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSst { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType$Enum { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType$Enum { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.STXstring { *; } + +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CommentsDocumentImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTAuthorsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBooleanPropertyImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBookViewImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBookViewsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBorderImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBordersImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTBorderPrImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellAlignmentImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellFormulaImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellStyleXfsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellXfsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTColorImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTColImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTColsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCommentImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCommentsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCommentListImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTDrawingImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFillImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFillsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontNameImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontSchemeImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontSizeImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTIntPropertyImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTLegacyDrawingImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTNumFmtsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTPatternFillImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTPageMarginsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTPaneImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRowImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSelectionImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetDataImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetDimensionImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetFormatPrImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetViewImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetViewsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSheetsImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTSstImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTStylesheetImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRstImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTWorkbookImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTWorkbookPrImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTWorksheetImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTXfImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.SstDocumentImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.StyleSheetDocumentImpl { *; } +-keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.STXstringImpl { *; } + +-keep class org.openxmlformats.schemas.officeDocument.x2006.customProperties.impl.CTPropertiesImpl { *; } +-keep class org.openxmlformats.schemas.officeDocument.x2006.customProperties.impl.PropertiesDocumentImpl { *; } +-keep class org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.impl.CTPropertiesImpl { *; } +-keep class org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.impl.PropertiesDocumentImpl { *; } +-keep class org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.impl.CTDrawingImpl { *; } +-keep class org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.impl.CTMarkerImpl { *; } +-keep class com.microsoft.schemas.office.office.impl.CTIdMapImpl { *; } +-keep class com.microsoft.schemas.office.office.impl.CTShapeLayoutImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTShadowImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTFillImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTPathImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTShapeImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTShapetypeImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTStrokeImpl { *; } +-keep class com.microsoft.schemas.vml.impl.CTTextboxImpl { *; } +-keep class com.microsoft.schemas.office.excel.impl.CTClientDataImpl { *; } +-keep class com.microsoft.schemas.office.excel.impl.STTrueFalseBlankImpl { *; } + +# Keep `Companion` object fields of serializable classes. +# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. +-if @kotlinx.serialization.Serializable class ** +-keepclassmembers class <1> { + static <1>$Companion Companion; +} + +# Keep `serializer()` on companion objects (both default and named) of serializable classes. +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class <2>$<3> { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} + +# @Serializable and @Polymorphic are used at runtime for polymorphic serialization. +-keepattributes RuntimeVisibleAnnotations,AnnotationDefault \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/framework/di/RepositoryBinding.kt b/app/src/main/java/llc/arma/ble/app/framework/di/RepositoryBinding.kt index 152f83f..b4e80f9 100644 --- a/app/src/main/java/llc/arma/ble/app/framework/di/RepositoryBinding.kt +++ b/app/src/main/java/llc/arma/ble/app/framework/di/RepositoryBinding.kt @@ -8,11 +8,13 @@ import llc.arma.ble.data.repository.BleNameRepositoryImpl import llc.arma.ble.data.repository.BleRepositoryImpl import llc.arma.ble.data.repository.EmailRepositoryImpl import llc.arma.ble.data.repository.RotationsRepositoryImpl +import llc.arma.ble.data.repository.SettingsRepositoryImpl import llc.arma.ble.data.repository.XlsxRepositoryImpl import llc.arma.ble.domain.repository.BleNameRepository import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.repository.EmailRepository import llc.arma.ble.domain.repository.RotationsRepository +import llc.arma.ble.domain.repository.SettingsRepository import llc.arma.ble.domain.repository.XlsxRepository @Module @@ -34,4 +36,7 @@ interface RepositoryBinding { @Binds fun bindXlsxRepository(repository: XlsxRepositoryImpl): XlsxRepository + @Binds + fun bindSettingsRepository(repository: SettingsRepositoryImpl): SettingsRepository + } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListContract.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListContract.kt index 25e12c4..ac95054 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListContract.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListContract.kt @@ -5,6 +5,7 @@ import llc.arma.ble.app.ui.common.ViewSideEffect import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.ConnectedBleInfo +import llc.arma.ble.domain.model.BleFilter class BleListContract { @@ -41,11 +42,11 @@ class BleListContract { ) : Event() data class OnSortFieldChanged( - val field: State.Filter.Field + val field: BleFilter.Field ) : Event() data class OnSortOrderChanged( - val order: State.Filter.Order + val order: BleFilter.Order ) : Event() } @@ -53,10 +54,10 @@ class BleListContract { data class State( val connectedBleList: List, val bleList: List, - val filter: Filter + val bleFilter: BleFilter ) : ViewState { - data class Filter( + /*data class Filter( val sortField: Field = Field.Name, val sortOrder: Order = Order.Asc, val name: String = "", @@ -74,7 +75,7 @@ class BleListContract { Asc, Desc } - } + }*/ } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt index e0b0d91..fb92350 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -56,6 +57,7 @@ import llc.arma.ble.app.ui.common.SignalLevel import llc.arma.ble.app.ui.common.rememberBottomDialogState import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.ConnectedBleInfo +import llc.arma.ble.domain.model.BleFilter import kotlin.math.pow @OptIn(ExperimentalMaterial3Api::class) @@ -79,7 +81,7 @@ fun BleListScreen( is BleListContract.Effect.ShowFilter -> launch { bottomDialog.show { Filter( - filter = viewModel.viewState.value.filter, + filter = viewModel.viewState.value.bleFilter, onEvent = { viewModel.setEvent(it) } @@ -144,32 +146,32 @@ fun BleListScreen( } ) - val filteredData = remember(state.bleList, state.filter) { + val filteredData = remember(state.bleList, state.bleFilter) { state.bleList.filter { - (it.type == state.filter.bleType || state.filter.bleType == null) && - it.name.contains(state.filter.name) && - it.serial.contains(state.filter.mac) && - state.filter.rssi.contains(it.rssi?.toFloat() ?: Float.MIN_VALUE) && - state.filter.battery.contains(it.batteryLevel.toFloat()) + (it.type == state.bleFilter.bleType || state.bleFilter.bleType == null) && + it.name.contains(state.bleFilter.name) && + it.serial.contains(state.bleFilter.mac) && + state.bleFilter.rssi.contains(it.rssi?.toFloat() ?: Float.MIN_VALUE) && + state.bleFilter.battery.contains(it.batteryLevel.toFloat()) }.let { - when (state.filter.sortField) { - BleListContract.State.Filter.Field.Name -> it.sortedBy { it.name } - BleListContract.State.Filter.Field.Mac -> it.sortedBy { it.serial } - BleListContract.State.Filter.Field.Distance -> it.sortedBy { + when (state.bleFilter.sortField) { + BleFilter.Field.Name -> it.sortedBy { it.name } + BleFilter.Field.Mac -> it.sortedBy { it.serial } + BleFilter.Field.Distance -> it.sortedBy { 10.0.pow( (it.tx.toDouble() - (it.rssi?.toDouble() ?: 0.0) - 74) / 20 ).toFloat() } - BleListContract.State.Filter.Field.Dbm -> it.sortedBy { it.rssi ?: 0 } - BleListContract.State.Filter.Field.Battery -> it.sortedBy { it.batteryLevel } + BleFilter.Field.Dbm -> it.sortedBy { it.rssi ?: 0 } + BleFilter.Field.Battery -> it.sortedBy { it.batteryLevel } } }.let { - when (state.filter.sortOrder) { - BleListContract.State.Filter.Order.Asc -> it - BleListContract.State.Filter.Order.Desc -> it.reversed() + when (state.bleFilter.sortOrder) { + BleFilter.Order.Asc -> it + BleFilter.Order.Desc -> it.reversed() } } @@ -317,7 +319,7 @@ fun BleItem( ) } - if(ble.recordEnabled){ + if(ble.tableStatus !== BleInfo.HistoryTableStatus.DISABLED){ Surface( shape = CircleShape, @@ -327,7 +329,11 @@ fun BleItem( Surface( shape = CircleShape, - color = MaterialTheme.colorScheme.error, + color = when(ble.tableStatus){ + BleInfo.HistoryTableStatus.EMPTY -> Color(0xff009116) + BleInfo.HistoryTableStatus.NOT_EMPTY -> MaterialTheme.colorScheme.error + else -> Color.Transparent + }, modifier = Modifier .size(12.dp) .padding(2.dp) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt index 25f0cad..469be9c 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt @@ -7,11 +7,16 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import llc.arma.ble.app.ui.common.BaseViewModel +import llc.arma.ble.domain.model.BleFilter import llc.arma.ble.domain.usecase.GetBleAroundFlow +import llc.arma.ble.domain.usecase.filter.GetFilterFlow +import llc.arma.ble.domain.usecase.filter.SaveFilter import javax.inject.Inject @HiltViewModel class BleListViewModel @Inject constructor( + private val getFilterFlow: GetFilterFlow, + private val saveFilter: SaveFilter, getBleAroundFlow: GetBleAroundFlow ) : BaseViewModel() { @@ -37,6 +42,14 @@ class BleListViewModel @Inject constructor( } ) + getFilterFlow.invoke().onEach { + setState { + copy( + bleFilter = it + ) + } + }.launchIn(this) + /*while (true) { job?.cancel() @@ -66,7 +79,7 @@ class BleListViewModel @Inject constructor( } override fun setInitialState(): BleListContract.State = - BleListContract.State(emptyList(), emptyList(), BleListContract.State.Filter()) + BleListContract.State(emptyList(), emptyList(), BleFilter()) override fun handleEvents(event: BleListContract.Event) { when(event){ @@ -106,9 +119,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnMacFilterChanged ) { - setState { - copy( - filter = this.filter.copy(mac = event.mac) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(mac = event.mac) ) } } @@ -117,9 +130,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnSortOrderChanged ) { - setState { - copy( - filter = this.filter.copy(sortOrder = event.order) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(sortOrder = event.order) ) } } @@ -128,9 +141,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnSortFieldChanged ) { - setState { - copy( - filter = this.filter.copy(sortField = event.field) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(sortField = event.field) ) } } @@ -139,9 +152,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnNameFilterChanged ) { - setState { - copy( - filter = this.filter.copy(name = event.name) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(name = event.name) ) } } @@ -151,14 +164,14 @@ class BleListViewModel @Inject constructor( event: BleListContract.Event.OnResetFilter ) { - setState { - copy( - filter = BleListContract.State.Filter() - ) - } + viewModelScope.launch { + + saveFilter(BleFilter()) + + setEffect { + BleListContract.Effect.HideFilter + } - setEffect { - BleListContract.Effect.HideFilter } } @@ -167,9 +180,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnRssiRangeChanged ) { - setState { - copy( - filter = this.filter.copy(rssi = event.rssi) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(rssi = event.rssi) ) } } @@ -178,9 +191,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnBatteryRangeChanged ) { - setState { - copy( - filter = this.filter.copy(battery = event.battery) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(battery = event.battery) ) } } @@ -189,9 +202,9 @@ class BleListViewModel @Inject constructor( state: BleListContract.State, event: BleListContract.Event.OnTypeChanged ) { - setState { - copy( - filter = this.filter.copy(bleType = event.type) + viewModelScope.launch { + saveFilter( + bleFilter = state.bleFilter.copy(bleType = event.type) ) } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/Filter.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/Filter.kt index 58c1920..9eaa717 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/Filter.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/Filter.kt @@ -47,24 +47,25 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import llc.arma.ble.domain.model.BleFilter import llc.arma.ble.domain.model.BleInfo -val BleListContract.State.Filter.Order.localized: String +val BleFilter.Order.localized: String get() { return when(this){ - BleListContract.State.Filter.Order.Asc -> "Прямой ↓" - BleListContract.State.Filter.Order.Desc -> "Обратный ↑" + BleFilter.Order.Asc -> "Прямой ↓" + BleFilter.Order.Desc -> "Обратный ↑" } } -val BleListContract.State.Filter.Field.localized: String +val BleFilter.Field.localized: String get() { return when(this){ - BleListContract.State.Filter.Field.Name -> "Имя" - BleListContract.State.Filter.Field.Mac -> "MAC" - BleListContract.State.Filter.Field.Distance -> "Расстояние" - BleListContract.State.Filter.Field.Dbm -> "dBm" - BleListContract.State.Filter.Field.Battery -> "Заряд" + BleFilter.Field.Name -> "Имя" + BleFilter.Field.Mac -> "MAC" + BleFilter.Field.Distance -> "Расстояние" + BleFilter.Field.Dbm -> "dBm" + BleFilter.Field.Battery -> "Заряд" } } @@ -93,7 +94,7 @@ val BleInfo.Type?.icon: ImageVector @OptIn(ExperimentalMaterial3Api::class) @Composable fun Filter( - filter: BleListContract.State.Filter, + filter: BleFilter, onEvent: (BleListContract.Event) -> Unit ) { @@ -164,7 +165,7 @@ fun Filter( } ) { - BleListContract.State.Filter.Field.values().forEach { selectionOption -> + BleFilter.Field.entries.forEach { selectionOption -> DropdownMenuItem( onClick = { onEvent( @@ -233,7 +234,7 @@ fun Filter( } ) { - BleListContract.State.Filter.Order.values().forEach { selectionOption -> + BleFilter.Order.entries.forEach { selectionOption -> DropdownMenuItem( onClick = { onEvent( @@ -303,7 +304,7 @@ fun Filter( ) { mutableListOf(null).apply { - addAll(BleInfo.Type.values()) + addAll(BleInfo.Type.entries.toTypedArray()) }.forEach { selectionOption -> DropdownMenuItem( onClick = { diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/HostHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/HostHistory.kt index a0e6988..8cf5250 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/HostHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/HostHistory.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.ContentAlpha import androidx.compose.material.icons.Icons @@ -48,17 +49,27 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.viewModelScope +import com.patrykandpatrick.vico.compose.axis.ChartShape +import com.patrykandpatrick.vico.compose.axis.axisGuidelineComponent +import com.patrykandpatrick.vico.compose.axis.axisLineComponent import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis +import com.patrykandpatrick.vico.compose.axis.vertical.startAxis import com.patrykandpatrick.vico.compose.chart.Chart import com.patrykandpatrick.vico.compose.chart.column.columnChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec +import com.patrykandpatrick.vico.compose.component.lineComponent +import com.patrykandpatrick.vico.compose.component.shapeComponent +import com.patrykandpatrick.vico.compose.component.textComponent import com.patrykandpatrick.vico.core.axis.AxisPosition import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp +import com.patrykandpatrick.vico.core.component.marker.MarkerComponent import com.patrykandpatrick.vico.core.component.shape.LineComponent import com.patrykandpatrick.vico.core.component.shape.Shapes.pillShape +import com.patrykandpatrick.vico.core.dimensions.MutableDimensions import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer @@ -202,12 +213,17 @@ val dateFormatter = SimpleDateFormat("dd.MM", Locale.getDefault()) val timeFormatter = SimpleDateFormat("HH:mm", Locale.getDefault()) val colorsStack = listOf( - Color(0xffffd700), Color(0xff2f4f4f), Color(0xff7f0000), Color(0xFFFF0000), + Color(0xff2f4f4f), Color(0xff7f0000), Color(0xFFFF0000), Color(0xffffd700), Color(0xffa9a9a9), Color(0xff00fa9a), Color(0xff00ffff), Color(0xfff0e68c), Color(0xff00bfff), Color(0xff0000ff), Color(0xfff08080), Color(0xffadff2f), Color(0xffff00ff), Color(0xff4169e1), Color(0xffff1493), Color(0xffee82ee), ) +val startAxisValueFormatter = + AxisValueFormatter { value, chartValues -> + " " + } + val axisValueFormatter = AxisValueFormatter { value, chartValues -> val first = (chartValues.chartEntryModel.entries.firstOrNull()?.firstOrNull() as? HostEntry) @@ -279,7 +295,7 @@ fun Display( if(historyPoint.value.contains(serial)) { HostEntry(historyPoint.date, index.toFloat(), 1f) } else { - HostEntry(historyPoint.date, index.toFloat(), 0f) + HostEntry(historyPoint.date, index.toFloat(), 0.0001f) } } ) @@ -291,6 +307,31 @@ fun Display( val producer = remember(entries) { ComposedChartEntryModelProducer(entries) } val chart = columnChart( + persistentMarkers = state.loadingHistoryState.data.mapIndexedNotNull { index, historyPoint -> + if(historyPoint.hit) { + Pair( + index.toFloat(), + MarkerComponent( + label = textComponent( + textSize = 0.sp, + padding = MutableDimensions(10f, 10f, 10f, 10f), + margins = MutableDimensions(10f, 10f, 10f, 10f), + color = Color.Red, + background = shapeComponent( + color = Color.Red, + shape = pillShape + ) + ), + indicator = null, + guideline = axisLineComponent( + thickness = 0.dp + ) + ) + ) + }else{ + null + } + }.toMap(), innerSpacing = 2.dp, columns = serials.map { LineComponent(color = colors[it]!!.toArgb(), thicknessDp = 7f, shape= pillShape) }, spacing = 8.dp, @@ -340,8 +381,6 @@ fun Display( horizontalArrangement = Arrangement.spacedBy(12.dp) ) { - - Chart( chart = chart, chartModelProducer = producer, @@ -350,6 +389,9 @@ fun Display( valueFormatter = axisValueFormatter, tickLength = 0.dp, ), + startAxis = startAxis( + valueFormatter = startAxisValueFormatter + ), modifier = Modifier .fillMaxHeight() .weight(1f), @@ -400,6 +442,7 @@ fun Display( HorizontalDivider() Chart( + chart = chart, chartModelProducer = producer, bottomAxis = bottomAxis( diff --git a/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt index 8d04ca1..d3b9449 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt @@ -142,7 +142,7 @@ class BleRepositoryImpl @Inject constructor( val tState = readAccelState( result.serial, - result.recordEnabled + result.tableStatus ).fold( onFailure = { return Result.failure(it) @@ -227,7 +227,7 @@ class BleRepositoryImpl @Inject constructor( } BleInfo.Type.THERMOMETER -> { - val tState = readThermometerState(result.serial, result.recordEnabled).fold( + val tState = readThermometerState(result.serial, result.tableStatus).fold( onFailure = { return Result.failure(it) }, @@ -335,7 +335,7 @@ class BleRepositoryImpl @Inject constructor( @OptIn(ExperimentalUnsignedTypes::class) private suspend fun readThermometerState( address: String, - timer: Boolean + timer: BleInfo.HistoryTableStatus ): Result { return if(app.checkPermission()) { @@ -373,7 +373,7 @@ class BleRepositoryImpl @Inject constructor( return Result.success( Ble.Thermometer.ThermometerState( temperature = temperature, - saveHistory = timer, + saveHistory = timer !== BleInfo.HistoryTableStatus.DISABLED, historyInterval = interval ) ) @@ -410,9 +410,17 @@ class BleRepositoryImpl @Inject constructor( .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) val characteristic = service.findCharacteristic(intervalReadUUID) - ?: return Result.failure(BleException.UnexpectedResponse) - characteristic.write(DataByteArray.from(3, 0, 0, 0, )) + if(characteristic == null){ + service.characteristics.forEach { + Log.d("ble", "characteristic ${it.uuid} ") + } + Log.d("ble", "${intervalReadUUID} not found") + return Result.failure(BleException.UnexpectedResponse) + } + + + characteristic.write(DataByteArray.from(3, 0, 0, 0)) val interval = characteristic.read().value.let { if(it.size == 4){ @@ -447,7 +455,7 @@ class BleRepositoryImpl @Inject constructor( private suspend fun readAccelState( address: String, - timer: Boolean + timer: BleInfo.HistoryTableStatus ): Result { return if(app.checkPermission()) { @@ -474,7 +482,8 @@ class BleRepositoryImpl @Inject constructor( } val historySettingsParams = when(timer){ - true -> { + BleInfo.HistoryTableStatus.EMPTY, + BleInfo.HistoryTableStatus.NOT_EMPTY -> { characteristic = service.findCharacteristic(accelerometerReadUUID) ?: return Result.failure(BleException.UnexpectedResponse) @@ -494,7 +503,7 @@ class BleRepositoryImpl @Inject constructor( ) } } - false -> Ble.Accelerometer.HistorySettings.Disabled + BleInfo.HistoryTableStatus.DISABLED -> Ble.Accelerometer.HistorySettings.Disabled } connection.close() diff --git a/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt b/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt index c903029..e41981f 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt @@ -1,6 +1,8 @@ package llc.arma.ble.data.repository +import android.annotation.SuppressLint import android.app.Application +import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -16,10 +18,12 @@ import llc.arma.ble.domain.model.Ble import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic +import okio.ByteString.Companion.decodeHex import java.nio.ByteBuffer import java.util.BitSet import java.util.Locale +@SuppressLint("MissingPermission") suspend fun readTable( characteristic: ClientBleGattCharacteristic, startRequest: ByteArray, @@ -28,13 +32,11 @@ suspend fun readTable( characteristic.write(DataByteArray(startRequest)) var value = characteristic.read().value - var nextPackageDataCount = value.get2byteUIntAt(2) - val tableResult = mutableListOf() do { - nextPackageDataCount = value.get2byteUIntAt(2) + val nextPackageDataCount = value.get2byteUIntAt(2) tableResult.addAll(value.asList().subList(4, value.size)) @@ -85,17 +87,14 @@ fun readHostHistory( firstTablePackage.addAll( readTable( characteristic, - mutableListOf( - 1.toByte(), - 0.toByte(), - 0.toByte() - ).apply { - addAll(value.toList()) - }.toByteArray(), + //Чтение без удаления + byteArrayOf(1, 0, 0, -2, -1), byteArrayOf(5) ) ) + Log.d("table", firstTablePackage.joinToString { it.toString() }) + val bleMeasureInterval = firstTablePackage.toByteArray().get4byteUIntAt(0).toLong() val bleLastMeasureTime = firstTablePackage.toByteArray().get4byteUIntAt(4).toLong() val bleRealTime = firstTablePackage.toByteArray().get4byteUIntAt(8).toLong() @@ -108,6 +107,7 @@ fun readHostHistory( fun getBleIdIndex(bytes: ByteArray): UInt{ val bits = BitSet.valueOf(bytes) + Log.d("bits", bits.toByteArray().joinToString()) bits.clear(12, 16) val arr = bits.toByteArray() @@ -141,7 +141,7 @@ fun readHostHistory( fun getDevType(byte: Byte): Int{ - var bits = BitSet.valueOf(byteArrayOf(byte)) + val bits = BitSet.valueOf(byteArrayOf(byte)) bits.clear(5, 9) val arr = bits.toByteArray() @@ -169,26 +169,29 @@ fun readHostHistory( } var bleTableOffset = 12 - var periods = mutableListOf>() - var periodBle = mutableListOf() + var periods = mutableListOf>>() + var periodBle = mutableListOf() + var hasHit = false do { val bleIdTableCell = firstTablePackage.drop(bleTableOffset).take(2).toByteArray() + println("cell ${bleIdTableCell.toHexString()} ${bleIdTableCell.joinToString()}") + if(bleIdTableCell.contentEquals(byteArrayOf(-1, 15)).not()) { - println("offset $bleTableOffset/${firstTablePackage.size}") + if(bleIdTableCell.contentEquals(byteArrayOf(-2, 15))){ + bleTableOffset += 2 + hasHit = true + continue + } val innerIndex = getInnerIndex(bleIdTableCell[1]) - println("inner index $innerIndex") - val bleTableIndex = getBleIdIndex(bleIdTableCell) * 8u - println("table index $bleTableIndex") - val serial = secondTablePackage.drop(bleTableIndex.toInt()).take(6).reversed() .joinToString( @@ -197,13 +200,8 @@ fun readHostHistory( .uppercase(Locale.getDefault()) val devTypeByte = secondTablePackage.drop(bleTableIndex.toInt() + 6)[0] - println("table serial $serial") - val devType = getDevType(devTypeByte) - println("devType $devType") val devDataSize = getDevDataSize(devTypeByte) - - println("payload $devDataSize") bleTableOffset += 2 if (devDataSize != 0) { @@ -222,8 +220,6 @@ fun readHostHistory( } - - var nextIndex = 0 if(bleTableOffset <= firstTablePackage.size - 2){ @@ -231,22 +227,21 @@ fun readHostHistory( } if(nextIndex == 0){ - println("________________") - periods.add(periodBle) + periods.add(Pair(hasHit, periodBle)) periodBle = mutableListOf() + hasHit = false } } while (bleTableOffset < firstTablePackage.size) - //periods.add(periodBle) - emit( Result.success( ProgressState.Finished( periods.withIndex().map { Ble.Host.HistoryPoint( date = lastMeasureSystemTime - (((periods.size - 1) - it.index) * bleMeasureInterval), - value = it.value + hit = it.value.first, + value = it.value.second ) } ) diff --git a/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt new file mode 100644 index 0000000..c99d5b9 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt @@ -0,0 +1,51 @@ +package llc.arma.ble.data.repository + +import android.app.Application +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import com.google.gson.Gson +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import llc.arma.ble.domain.model.BleFilter +import llc.arma.ble.domain.repository.SettingsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SettingsRepositoryImpl @Inject constructor( + private val app: Application +) : SettingsRepository { + + val FILTER = stringPreferencesKey("ble_filter") + + val Context.dataStore: DataStore by preferencesDataStore(name = "settings") + + override suspend fun saveFilter( + bleFilter: BleFilter + ) { + app.dataStore.edit { settings -> + + settings[FILTER] = Json.encodeToString(bleFilter) + } + } + + override fun getFilterFlow(): Flow { + return app.dataStore.data.map { settings -> + try { + + Json.decodeFromString(settings[FILTER] ?: "") + } catch (e: Throwable){ + e.printStackTrace() + null + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/repository/extensions/BleScanResultExtensions.kt b/app/src/main/java/llc/arma/ble/data/repository/extensions/BleScanResultExtensions.kt index 3d28dab..ea8534d 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/extensions/BleScanResultExtensions.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/extensions/BleScanResultExtensions.kt @@ -3,9 +3,13 @@ package llc.arma.ble.data.repository.extensions import llc.arma.ble.domain.model.BleInfo import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult -val BleScanResult.timerEnabled: Boolean +val BleScanResult.timerEnabled: BleInfo.HistoryTableStatus get() { - return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(2) == 1.toByte() + return when(data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(2)){ + 1.toByte() -> BleInfo.HistoryTableStatus.NOT_EMPTY + 2.toByte() -> BleInfo.HistoryTableStatus.EMPTY + else -> BleInfo.HistoryTableStatus.DISABLED + } } val BleScanResult.info: BleInfo @@ -19,7 +23,7 @@ val BleScanResult.info: BleInfo type = type, scanTime = (data?.timestampNanos ?: 0) / 1_000_000, tx = data?.scanRecord?.txPowerLevel ?: 0, - recordEnabled = timerEnabled + tableStatus = timerEnabled ) } diff --git a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt index 22a1ed4..c2c45b8 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt @@ -116,6 +116,7 @@ sealed class Ble( class HistoryPoint( val date: Long, + val hit: Boolean, val value: List ) diff --git a/app/src/main/java/llc/arma/ble/domain/model/BleFilter.kt b/app/src/main/java/llc/arma/ble/domain/model/BleFilter.kt new file mode 100644 index 0000000..8d5724e --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/model/BleFilter.kt @@ -0,0 +1,63 @@ +package llc.arma.ble.domain.model + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.element +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.encoding.decodeStructure +import kotlinx.serialization.encoding.encodeStructure + +@Serializable +data class BleFilter( + val sortField: Field = Field.Name, + val sortOrder: Order = Order.Asc, + val name: String = "", + val mac: String = "", + @Serializable(with = CFPRSerializer::class) val battery: ClosedFloatingPointRange = (0f)..(100f), + @Serializable(with = CFPRSerializer::class) val rssi: ClosedFloatingPointRange = (-100f)..(-10f), + val bleType: BleInfo.Type? = null +){ + + enum class Field { + Name, Mac, Distance, Dbm, Battery + } + + enum class Order { + Asc, Desc + } + +} + +object CFPRSerializer : KSerializer> { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.ranges.DoubleClosedFloatingPointRange") { + element("start") + element("endInclusive") + } + + override fun serialize(encoder: Encoder, value: ClosedFloatingPointRange) { + encoder.encodeStructure(descriptor) { + encodeFloatElement(descriptor, 0, value.start) + encodeFloatElement(descriptor, 1, value.endInclusive) + } + } + + override fun deserialize(decoder: Decoder): ClosedFloatingPointRange { + return decoder.decodeStructure(descriptor) { + var start: Float? = null + var end: Float? = null + while (true) { + val index = decodeElementIndex(descriptor) + if (index == CompositeDecoder.DECODE_DONE) break + if (index == 0) start = decodeFloatElement(descriptor, index) + else end = decodeFloatElement(descriptor, index) + } + if (start == null || end == null) throw SerializationException("...") + start..end + } + } +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt b/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt index ed47373..8e84c46 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt @@ -12,9 +12,13 @@ data class BleInfo( val type: Type, val scanTime: Long, val tx: Int, - val recordEnabled: Boolean + val tableStatus: HistoryTableStatus ) : Parcelable { + enum class HistoryTableStatus { + DISABLED, EMPTY, NOT_EMPTY + } + enum class Type { HOST, BEACON, THERMOMETER, ACCELEROMETER } diff --git a/app/src/main/java/llc/arma/ble/domain/repository/SettingsRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/SettingsRepository.kt new file mode 100644 index 0000000..539f7f6 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/repository/SettingsRepository.kt @@ -0,0 +1,12 @@ +package llc.arma.ble.domain.repository + +import kotlinx.coroutines.flow.Flow +import llc.arma.ble.domain.model.BleFilter + +interface SettingsRepository { + + suspend fun saveFilter(bleFilter: BleFilter) + + fun getFilterFlow(): Flow + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/filter/GetFilterFlow.kt b/app/src/main/java/llc/arma/ble/domain/usecase/filter/GetFilterFlow.kt new file mode 100644 index 0000000..c11aa42 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/usecase/filter/GetFilterFlow.kt @@ -0,0 +1,19 @@ +package llc.arma.ble.domain.usecase.filter + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import llc.arma.ble.domain.model.BleFilter +import llc.arma.ble.domain.repository.SettingsRepository +import javax.inject.Inject + +class GetFilterFlow @Inject constructor( + private val settingsRepository: SettingsRepository +) { + + operator fun invoke(): Flow { + return settingsRepository.getFilterFlow().map { + it ?: BleFilter() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/filter/SaveFilter.kt b/app/src/main/java/llc/arma/ble/domain/usecase/filter/SaveFilter.kt new file mode 100644 index 0000000..7cdbd37 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/usecase/filter/SaveFilter.kt @@ -0,0 +1,17 @@ +package llc.arma.ble.domain.usecase.filter + +import llc.arma.ble.domain.model.BleFilter +import llc.arma.ble.domain.repository.SettingsRepository +import javax.inject.Inject + +class SaveFilter @Inject constructor( + private val settingsRepository: SettingsRepository +) { + + suspend operator fun invoke(bleFilter: BleFilter){ + + settingsRepository.saveFilter(bleFilter) + + } + +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index cff2060..3cabb82 100644 --- a/build.gradle +++ b/build.gradle @@ -17,5 +17,6 @@ plugins { id 'com.android.application' version '8.1.1' apply false id 'com.android.library' version '8.1.1' apply false id 'org.jetbrains.kotlin.android' version '1.9.22' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' id("androidx.room") version "2.6.1" apply false } \ No newline at end of file