filter saving

This commit is contained in:
Vineyro 2024-09-27 14:59:49 +07:00
parent 5293604ee4
commit 666757922d
19 changed files with 552 additions and 117 deletions

View File

@ -1,9 +1,11 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id("org.jetbrains.kotlin.plugin.serialization")
id 'kotlin-kapt' id 'kotlin-kapt'
id 'dagger.hilt.android.plugin' id 'dagger.hilt.android.plugin'
id("kotlin-parcelize") id("kotlin-parcelize")
id("androidx.room") id("androidx.room")
} }
@ -17,8 +19,8 @@ android {
applicationId "llc.arma.ble" applicationId "llc.arma.ble"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 39 versionCode 41
versionName "1.4.9" versionName "1.4.12"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@ -28,11 +30,11 @@ android {
buildTypes { buildTypes {
debug { debug {
minifyEnabled false minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
release { release {
minifyEnabled false minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
@ -107,6 +109,9 @@ dependencies {
kapt("androidx.room:room-compiler:2.6.1") kapt("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx: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') implementation files('libs/poishadow-all.jar')
} }

187
app/proguard-rules.pro vendored
View File

@ -18,4 +18,189 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-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 <init>(...); }
-keep class org.apache.xmlbeans.impl.schema.SchemaTypeSystemCompiler { public static *** compile(...); }
-keep class org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl { public <init>(...); 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

View File

@ -8,11 +8,13 @@ import llc.arma.ble.data.repository.BleNameRepositoryImpl
import llc.arma.ble.data.repository.BleRepositoryImpl import llc.arma.ble.data.repository.BleRepositoryImpl
import llc.arma.ble.data.repository.EmailRepositoryImpl import llc.arma.ble.data.repository.EmailRepositoryImpl
import llc.arma.ble.data.repository.RotationsRepositoryImpl 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.data.repository.XlsxRepositoryImpl
import llc.arma.ble.domain.repository.BleNameRepository import llc.arma.ble.domain.repository.BleNameRepository
import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.repository.BleRepository
import llc.arma.ble.domain.repository.EmailRepository import llc.arma.ble.domain.repository.EmailRepository
import llc.arma.ble.domain.repository.RotationsRepository import llc.arma.ble.domain.repository.RotationsRepository
import llc.arma.ble.domain.repository.SettingsRepository
import llc.arma.ble.domain.repository.XlsxRepository import llc.arma.ble.domain.repository.XlsxRepository
@Module @Module
@ -34,4 +36,7 @@ interface RepositoryBinding {
@Binds @Binds
fun bindXlsxRepository(repository: XlsxRepositoryImpl): XlsxRepository fun bindXlsxRepository(repository: XlsxRepositoryImpl): XlsxRepository
@Binds
fun bindSettingsRepository(repository: SettingsRepositoryImpl): SettingsRepository
} }

View File

@ -5,6 +5,7 @@ import llc.arma.ble.app.ui.common.ViewSideEffect
import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.app.ui.common.ViewState
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.model.BleFilter
class BleListContract { class BleListContract {
@ -41,11 +42,11 @@ class BleListContract {
) : Event() ) : Event()
data class OnSortFieldChanged( data class OnSortFieldChanged(
val field: State.Filter.Field val field: BleFilter.Field
) : Event() ) : Event()
data class OnSortOrderChanged( data class OnSortOrderChanged(
val order: State.Filter.Order val order: BleFilter.Order
) : Event() ) : Event()
} }
@ -53,10 +54,10 @@ class BleListContract {
data class State( data class State(
val connectedBleList: List<ConnectedBleInfo>, val connectedBleList: List<ConnectedBleInfo>,
val bleList: List<BleInfo>, val bleList: List<BleInfo>,
val filter: Filter val bleFilter: BleFilter
) : ViewState { ) : ViewState {
data class Filter( /*data class Filter(
val sortField: Field = Field.Name, val sortField: Field = Field.Name,
val sortOrder: Order = Order.Asc, val sortOrder: Order = Order.Asc,
val name: String = "", val name: String = "",
@ -74,7 +75,7 @@ class BleListContract {
Asc, Desc Asc, Desc
} }
} }*/
} }

View File

@ -45,6 +45,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel 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.app.ui.common.rememberBottomDialogState
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.model.BleFilter
import kotlin.math.pow import kotlin.math.pow
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -79,7 +81,7 @@ fun BleListScreen(
is BleListContract.Effect.ShowFilter -> launch { is BleListContract.Effect.ShowFilter -> launch {
bottomDialog.show { bottomDialog.show {
Filter( Filter(
filter = viewModel.viewState.value.filter, filter = viewModel.viewState.value.bleFilter,
onEvent = { onEvent = {
viewModel.setEvent(it) 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 { state.bleList.filter {
(it.type == state.filter.bleType || state.filter.bleType == null) && (it.type == state.bleFilter.bleType || state.bleFilter.bleType == null) &&
it.name.contains(state.filter.name) && it.name.contains(state.bleFilter.name) &&
it.serial.contains(state.filter.mac) && it.serial.contains(state.bleFilter.mac) &&
state.filter.rssi.contains(it.rssi?.toFloat() ?: Float.MIN_VALUE) && state.bleFilter.rssi.contains(it.rssi?.toFloat() ?: Float.MIN_VALUE) &&
state.filter.battery.contains(it.batteryLevel.toFloat()) state.bleFilter.battery.contains(it.batteryLevel.toFloat())
}.let { }.let {
when (state.filter.sortField) { when (state.bleFilter.sortField) {
BleListContract.State.Filter.Field.Name -> it.sortedBy { it.name } BleFilter.Field.Name -> it.sortedBy { it.name }
BleListContract.State.Filter.Field.Mac -> it.sortedBy { it.serial } BleFilter.Field.Mac -> it.sortedBy { it.serial }
BleListContract.State.Filter.Field.Distance -> it.sortedBy { BleFilter.Field.Distance -> it.sortedBy {
10.0.pow( 10.0.pow(
(it.tx.toDouble() - (it.rssi?.toDouble() ?: 0.0) - 74) / 20 (it.tx.toDouble() - (it.rssi?.toDouble() ?: 0.0) - 74) / 20
).toFloat() ).toFloat()
} }
BleListContract.State.Filter.Field.Dbm -> it.sortedBy { it.rssi ?: 0 } BleFilter.Field.Dbm -> it.sortedBy { it.rssi ?: 0 }
BleListContract.State.Filter.Field.Battery -> it.sortedBy { it.batteryLevel } BleFilter.Field.Battery -> it.sortedBy { it.batteryLevel }
} }
}.let { }.let {
when (state.filter.sortOrder) { when (state.bleFilter.sortOrder) {
BleListContract.State.Filter.Order.Asc -> it BleFilter.Order.Asc -> it
BleListContract.State.Filter.Order.Desc -> it.reversed() BleFilter.Order.Desc -> it.reversed()
} }
} }
@ -317,7 +319,7 @@ fun BleItem(
) )
} }
if(ble.recordEnabled){ if(ble.tableStatus !== BleInfo.HistoryTableStatus.DISABLED){
Surface( Surface(
shape = CircleShape, shape = CircleShape,
@ -327,7 +329,11 @@ fun BleItem(
Surface( Surface(
shape = CircleShape, 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 modifier = Modifier
.size(12.dp) .size(12.dp)
.padding(2.dp) .padding(2.dp)

View File

@ -7,11 +7,16 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel 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.GetBleAroundFlow
import llc.arma.ble.domain.usecase.filter.GetFilterFlow
import llc.arma.ble.domain.usecase.filter.SaveFilter
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class BleListViewModel @Inject constructor( class BleListViewModel @Inject constructor(
private val getFilterFlow: GetFilterFlow,
private val saveFilter: SaveFilter,
getBleAroundFlow: GetBleAroundFlow getBleAroundFlow: GetBleAroundFlow
) : BaseViewModel<BleListContract.State, BleListContract.Event, BleListContract.Effect>() { ) : BaseViewModel<BleListContract.State, BleListContract.Event, BleListContract.Effect>() {
@ -37,6 +42,14 @@ class BleListViewModel @Inject constructor(
} }
) )
getFilterFlow.invoke().onEach {
setState {
copy(
bleFilter = it
)
}
}.launchIn(this)
/*while (true) { /*while (true) {
job?.cancel() job?.cancel()
@ -66,7 +79,7 @@ class BleListViewModel @Inject constructor(
} }
override fun setInitialState(): BleListContract.State = override fun setInitialState(): BleListContract.State =
BleListContract.State(emptyList(), emptyList(), BleListContract.State.Filter()) BleListContract.State(emptyList(), emptyList(), BleFilter())
override fun handleEvents(event: BleListContract.Event) { override fun handleEvents(event: BleListContract.Event) {
when(event){ when(event){
@ -106,9 +119,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnMacFilterChanged event: BleListContract.Event.OnMacFilterChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(mac = event.mac) bleFilter = state.bleFilter.copy(mac = event.mac)
) )
} }
} }
@ -117,9 +130,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnSortOrderChanged event: BleListContract.Event.OnSortOrderChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(sortOrder = event.order) bleFilter = state.bleFilter.copy(sortOrder = event.order)
) )
} }
} }
@ -128,9 +141,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnSortFieldChanged event: BleListContract.Event.OnSortFieldChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(sortField = event.field) bleFilter = state.bleFilter.copy(sortField = event.field)
) )
} }
} }
@ -139,9 +152,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnNameFilterChanged event: BleListContract.Event.OnNameFilterChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(name = event.name) bleFilter = state.bleFilter.copy(name = event.name)
) )
} }
} }
@ -151,14 +164,14 @@ class BleListViewModel @Inject constructor(
event: BleListContract.Event.OnResetFilter event: BleListContract.Event.OnResetFilter
) { ) {
setState { viewModelScope.launch {
copy(
filter = BleListContract.State.Filter() saveFilter(BleFilter())
)
} setEffect {
BleListContract.Effect.HideFilter
}
setEffect {
BleListContract.Effect.HideFilter
} }
} }
@ -167,9 +180,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnRssiRangeChanged event: BleListContract.Event.OnRssiRangeChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(rssi = event.rssi) bleFilter = state.bleFilter.copy(rssi = event.rssi)
) )
} }
} }
@ -178,9 +191,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnBatteryRangeChanged event: BleListContract.Event.OnBatteryRangeChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(battery = event.battery) bleFilter = state.bleFilter.copy(battery = event.battery)
) )
} }
} }
@ -189,9 +202,9 @@ class BleListViewModel @Inject constructor(
state: BleListContract.State, state: BleListContract.State,
event: BleListContract.Event.OnTypeChanged event: BleListContract.Event.OnTypeChanged
) { ) {
setState { viewModelScope.launch {
copy( saveFilter(
filter = this.filter.copy(bleType = event.type) bleFilter = state.bleFilter.copy(bleType = event.type)
) )
} }
} }

View File

@ -47,24 +47,25 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import llc.arma.ble.domain.model.BleFilter
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
val BleListContract.State.Filter.Order.localized: String val BleFilter.Order.localized: String
get() { get() {
return when(this){ return when(this){
BleListContract.State.Filter.Order.Asc -> "Прямой ↓" BleFilter.Order.Asc -> "Прямой ↓"
BleListContract.State.Filter.Order.Desc -> "Обратный ↑" BleFilter.Order.Desc -> "Обратный ↑"
} }
} }
val BleListContract.State.Filter.Field.localized: String val BleFilter.Field.localized: String
get() { get() {
return when(this){ return when(this){
BleListContract.State.Filter.Field.Name -> "Имя" BleFilter.Field.Name -> "Имя"
BleListContract.State.Filter.Field.Mac -> "MAC" BleFilter.Field.Mac -> "MAC"
BleListContract.State.Filter.Field.Distance -> "Расстояние" BleFilter.Field.Distance -> "Расстояние"
BleListContract.State.Filter.Field.Dbm -> "dBm" BleFilter.Field.Dbm -> "dBm"
BleListContract.State.Filter.Field.Battery -> "Заряд" BleFilter.Field.Battery -> "Заряд"
} }
} }
@ -93,7 +94,7 @@ val BleInfo.Type?.icon: ImageVector
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun Filter( fun Filter(
filter: BleListContract.State.Filter, filter: BleFilter,
onEvent: (BleListContract.Event) -> Unit onEvent: (BleListContract.Event) -> Unit
) { ) {
@ -164,7 +165,7 @@ fun Filter(
} }
) { ) {
BleListContract.State.Filter.Field.values().forEach { selectionOption -> BleFilter.Field.entries.forEach { selectionOption ->
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
onEvent( onEvent(
@ -233,7 +234,7 @@ fun Filter(
} }
) { ) {
BleListContract.State.Filter.Order.values().forEach { selectionOption -> BleFilter.Order.entries.forEach { selectionOption ->
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
onEvent( onEvent(
@ -303,7 +304,7 @@ fun Filter(
) { ) {
mutableListOf<BleInfo.Type?>(null).apply { mutableListOf<BleInfo.Type?>(null).apply {
addAll(BleInfo.Type.values()) addAll(BleInfo.Type.entries.toTypedArray())
}.forEach { selectionOption -> }.forEach { selectionOption ->
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {

View File

@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ContentAlpha import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons 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.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
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope 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.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
import com.patrykandpatrick.vico.compose.chart.Chart import com.patrykandpatrick.vico.compose.chart.Chart
import com.patrykandpatrick.vico.compose.chart.column.columnChart import com.patrykandpatrick.vico.compose.chart.column.columnChart
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec 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.AxisPosition
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp 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.LineComponent
import com.patrykandpatrick.vico.core.component.shape.Shapes.pillShape 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.ChartEntry
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer 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 timeFormatter = SimpleDateFormat("HH:mm", Locale.getDefault())
val colorsStack = listOf( 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(0xffa9a9a9), Color(0xff00fa9a), Color(0xff00ffff), Color(0xfff0e68c),
Color(0xff00bfff), Color(0xff0000ff), Color(0xfff08080), Color(0xffadff2f), Color(0xff00bfff), Color(0xff0000ff), Color(0xfff08080), Color(0xffadff2f),
Color(0xffff00ff), Color(0xff4169e1), Color(0xffff1493), Color(0xffee82ee), Color(0xffff00ff), Color(0xff4169e1), Color(0xffff1493), Color(0xffee82ee),
) )
val startAxisValueFormatter =
AxisValueFormatter<AxisPosition.Vertical.Start> { value, chartValues ->
" "
}
val axisValueFormatter = val axisValueFormatter =
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues -> AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
val first = (chartValues.chartEntryModel.entries.firstOrNull()?.firstOrNull() as? HostEntry) val first = (chartValues.chartEntryModel.entries.firstOrNull()?.firstOrNull() as? HostEntry)
@ -279,7 +295,7 @@ fun Display(
if(historyPoint.value.contains(serial)) { if(historyPoint.value.contains(serial)) {
HostEntry(historyPoint.date, index.toFloat(), 1f) HostEntry(historyPoint.date, index.toFloat(), 1f)
} else { } 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 producer = remember(entries) { ComposedChartEntryModelProducer(entries) }
val chart = columnChart( 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, innerSpacing = 2.dp,
columns = serials.map { LineComponent(color = colors[it]!!.toArgb(), thicknessDp = 7f, shape= pillShape) }, columns = serials.map { LineComponent(color = colors[it]!!.toArgb(), thicknessDp = 7f, shape= pillShape) },
spacing = 8.dp, spacing = 8.dp,
@ -340,8 +381,6 @@ fun Display(
horizontalArrangement = Arrangement.spacedBy(12.dp) horizontalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
Chart( Chart(
chart = chart, chart = chart,
chartModelProducer = producer, chartModelProducer = producer,
@ -350,6 +389,9 @@ fun Display(
valueFormatter = axisValueFormatter, valueFormatter = axisValueFormatter,
tickLength = 0.dp, tickLength = 0.dp,
), ),
startAxis = startAxis(
valueFormatter = startAxisValueFormatter
),
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.weight(1f), .weight(1f),
@ -400,6 +442,7 @@ fun Display(
HorizontalDivider() HorizontalDivider()
Chart( Chart(
chart = chart, chart = chart,
chartModelProducer = producer, chartModelProducer = producer,
bottomAxis = bottomAxis( bottomAxis = bottomAxis(

View File

@ -142,7 +142,7 @@ class BleRepositoryImpl @Inject constructor(
val tState = readAccelState( val tState = readAccelState(
result.serial, result.serial,
result.recordEnabled result.tableStatus
).fold( ).fold(
onFailure = { onFailure = {
return Result.failure(it) return Result.failure(it)
@ -227,7 +227,7 @@ class BleRepositoryImpl @Inject constructor(
} }
BleInfo.Type.THERMOMETER -> { BleInfo.Type.THERMOMETER -> {
val tState = readThermometerState(result.serial, result.recordEnabled).fold( val tState = readThermometerState(result.serial, result.tableStatus).fold(
onFailure = { onFailure = {
return Result.failure(it) return Result.failure(it)
}, },
@ -335,7 +335,7 @@ class BleRepositoryImpl @Inject constructor(
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
private suspend fun readThermometerState( private suspend fun readThermometerState(
address: String, address: String,
timer: Boolean timer: BleInfo.HistoryTableStatus
): Result<Ble.Thermometer.ThermometerState, BleException> { ): Result<Ble.Thermometer.ThermometerState, BleException> {
return if(app.checkPermission()) { return if(app.checkPermission()) {
@ -373,7 +373,7 @@ class BleRepositoryImpl @Inject constructor(
return Result.success( return Result.success(
Ble.Thermometer.ThermometerState( Ble.Thermometer.ThermometerState(
temperature = temperature, temperature = temperature,
saveHistory = timer, saveHistory = timer !== BleInfo.HistoryTableStatus.DISABLED,
historyInterval = interval historyInterval = interval
) )
) )
@ -410,9 +410,17 @@ class BleRepositoryImpl @Inject constructor(
.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse)
val characteristic = service.findCharacteristic(intervalReadUUID) 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 { val interval = characteristic.read().value.let {
if(it.size == 4){ if(it.size == 4){
@ -447,7 +455,7 @@ class BleRepositoryImpl @Inject constructor(
private suspend fun readAccelState( private suspend fun readAccelState(
address: String, address: String,
timer: Boolean timer: BleInfo.HistoryTableStatus
): Result<Ble.Accelerometer.AccelerometerState, BleException> { ): Result<Ble.Accelerometer.AccelerometerState, BleException> {
return if(app.checkPermission()) { return if(app.checkPermission()) {
@ -474,7 +482,8 @@ class BleRepositoryImpl @Inject constructor(
} }
val historySettingsParams = when(timer){ val historySettingsParams = when(timer){
true -> { BleInfo.HistoryTableStatus.EMPTY,
BleInfo.HistoryTableStatus.NOT_EMPTY -> {
characteristic = service.findCharacteristic(accelerometerReadUUID) characteristic = service.findCharacteristic(accelerometerReadUUID)
?: return Result.failure(BleException.UnexpectedResponse) ?: 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() connection.close()

View File

@ -1,6 +1,8 @@
package llc.arma.ble.data.repository package llc.arma.ble.data.repository
import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.util.Log
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay 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.common.core.DataByteArray
import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt
import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic
import okio.ByteString.Companion.decodeHex
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.BitSet import java.util.BitSet
import java.util.Locale import java.util.Locale
@SuppressLint("MissingPermission")
suspend fun readTable( suspend fun readTable(
characteristic: ClientBleGattCharacteristic, characteristic: ClientBleGattCharacteristic,
startRequest: ByteArray, startRequest: ByteArray,
@ -28,13 +32,11 @@ suspend fun readTable(
characteristic.write(DataByteArray(startRequest)) characteristic.write(DataByteArray(startRequest))
var value = characteristic.read().value var value = characteristic.read().value
var nextPackageDataCount = value.get2byteUIntAt(2)
val tableResult = mutableListOf<Byte>() val tableResult = mutableListOf<Byte>()
do { do {
nextPackageDataCount = value.get2byteUIntAt(2) val nextPackageDataCount = value.get2byteUIntAt(2)
tableResult.addAll(value.asList().subList(4, value.size)) tableResult.addAll(value.asList().subList(4, value.size))
@ -85,17 +87,14 @@ fun readHostHistory(
firstTablePackage.addAll( firstTablePackage.addAll(
readTable( readTable(
characteristic, characteristic,
mutableListOf( //Чтение без удаления
1.toByte(), byteArrayOf(1, 0, 0, -2, -1),
0.toByte(),
0.toByte()
).apply {
addAll(value.toList())
}.toByteArray(),
byteArrayOf(5) byteArrayOf(5)
) )
) )
Log.d("table", firstTablePackage.joinToString { it.toString() })
val bleMeasureInterval = firstTablePackage.toByteArray().get4byteUIntAt(0).toLong() val bleMeasureInterval = firstTablePackage.toByteArray().get4byteUIntAt(0).toLong()
val bleLastMeasureTime = firstTablePackage.toByteArray().get4byteUIntAt(4).toLong() val bleLastMeasureTime = firstTablePackage.toByteArray().get4byteUIntAt(4).toLong()
val bleRealTime = firstTablePackage.toByteArray().get4byteUIntAt(8).toLong() val bleRealTime = firstTablePackage.toByteArray().get4byteUIntAt(8).toLong()
@ -108,6 +107,7 @@ fun readHostHistory(
fun getBleIdIndex(bytes: ByteArray): UInt{ fun getBleIdIndex(bytes: ByteArray): UInt{
val bits = BitSet.valueOf(bytes) val bits = BitSet.valueOf(bytes)
Log.d("bits", bits.toByteArray().joinToString())
bits.clear(12, 16) bits.clear(12, 16)
val arr = bits.toByteArray() val arr = bits.toByteArray()
@ -141,7 +141,7 @@ fun readHostHistory(
fun getDevType(byte: Byte): Int{ fun getDevType(byte: Byte): Int{
var bits = BitSet.valueOf(byteArrayOf(byte)) val bits = BitSet.valueOf(byteArrayOf(byte))
bits.clear(5, 9) bits.clear(5, 9)
val arr = bits.toByteArray() val arr = bits.toByteArray()
@ -169,26 +169,29 @@ fun readHostHistory(
} }
var bleTableOffset = 12 var bleTableOffset = 12
var periods = mutableListOf<List<String>>() var periods = mutableListOf<Pair<Boolean, List<String>>>()
var periodBle = mutableListOf<String>()
var periodBle = mutableListOf<String>()
var hasHit = false
do { do {
val bleIdTableCell = firstTablePackage.drop(bleTableOffset).take(2).toByteArray() val bleIdTableCell = firstTablePackage.drop(bleTableOffset).take(2).toByteArray()
println("cell ${bleIdTableCell.toHexString()} ${bleIdTableCell.joinToString()}")
if(bleIdTableCell.contentEquals(byteArrayOf(-1, 15)).not()) { 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]) val innerIndex = getInnerIndex(bleIdTableCell[1])
println("inner index $innerIndex")
val bleTableIndex = getBleIdIndex(bleIdTableCell) * 8u val bleTableIndex = getBleIdIndex(bleIdTableCell) * 8u
println("table index $bleTableIndex")
val serial = val serial =
secondTablePackage.drop(bleTableIndex.toInt()).take(6).reversed() secondTablePackage.drop(bleTableIndex.toInt()).take(6).reversed()
.joinToString( .joinToString(
@ -197,13 +200,8 @@ fun readHostHistory(
.uppercase(Locale.getDefault()) .uppercase(Locale.getDefault())
val devTypeByte = secondTablePackage.drop(bleTableIndex.toInt() + 6)[0] val devTypeByte = secondTablePackage.drop(bleTableIndex.toInt() + 6)[0]
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) {
@ -222,8 +220,6 @@ fun readHostHistory(
} }
var nextIndex = 0 var nextIndex = 0
if(bleTableOffset <= firstTablePackage.size - 2){ if(bleTableOffset <= firstTablePackage.size - 2){
@ -231,22 +227,21 @@ fun readHostHistory(
} }
if(nextIndex == 0){ if(nextIndex == 0){
println("________________") periods.add(Pair(hasHit, periodBle))
periods.add(periodBle)
periodBle = mutableListOf() periodBle = mutableListOf()
hasHit = false
} }
} while (bleTableOffset < firstTablePackage.size) } while (bleTableOffset < firstTablePackage.size)
//periods.add(periodBle)
emit( emit(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
periods.withIndex().map { periods.withIndex().map {
Ble.Host.HistoryPoint( Ble.Host.HistoryPoint(
date = lastMeasureSystemTime - (((periods.size - 1) - it.index) * bleMeasureInterval), date = lastMeasureSystemTime - (((periods.size - 1) - it.index) * bleMeasureInterval),
value = it.value hit = it.value.first,
value = it.value.second
) )
} }
) )

View File

@ -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<Preferences> by preferencesDataStore(name = "settings")
override suspend fun saveFilter(
bleFilter: BleFilter
) {
app.dataStore.edit { settings ->
settings[FILTER] = Json.encodeToString(bleFilter)
}
}
override fun getFilterFlow(): Flow<BleFilter?> {
return app.dataStore.data.map { settings ->
try {
Json.decodeFromString<BleFilter>(settings[FILTER] ?: "")
} catch (e: Throwable){
e.printStackTrace()
null
}
}
}
}

View File

@ -3,9 +3,13 @@ package llc.arma.ble.data.repository.extensions
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult
val BleScanResult.timerEnabled: Boolean val BleScanResult.timerEnabled: BleInfo.HistoryTableStatus
get() { 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 val BleScanResult.info: BleInfo
@ -19,7 +23,7 @@ val BleScanResult.info: BleInfo
type = type, type = type,
scanTime = (data?.timestampNanos ?: 0) / 1_000_000, scanTime = (data?.timestampNanos ?: 0) / 1_000_000,
tx = data?.scanRecord?.txPowerLevel ?: 0, tx = data?.scanRecord?.txPowerLevel ?: 0,
recordEnabled = timerEnabled tableStatus = timerEnabled
) )
} }

View File

@ -116,6 +116,7 @@ sealed class Ble(
class HistoryPoint( class HistoryPoint(
val date: Long, val date: Long,
val hit: Boolean,
val value: List<String> val value: List<String>
) )

View File

@ -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<Float> = (0f)..(100f),
@Serializable(with = CFPRSerializer::class) val rssi: ClosedFloatingPointRange<Float> = (-100f)..(-10f),
val bleType: BleInfo.Type? = null
){
enum class Field {
Name, Mac, Distance, Dbm, Battery
}
enum class Order {
Asc, Desc
}
}
object CFPRSerializer : KSerializer<ClosedFloatingPointRange<Float>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.ranges.DoubleClosedFloatingPointRange") {
element<Float>("start")
element<Float>("endInclusive")
}
override fun serialize(encoder: Encoder, value: ClosedFloatingPointRange<Float>) {
encoder.encodeStructure(descriptor) {
encodeFloatElement(descriptor, 0, value.start)
encodeFloatElement(descriptor, 1, value.endInclusive)
}
}
override fun deserialize(decoder: Decoder): ClosedFloatingPointRange<Float> {
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
}
}
}

View File

@ -12,9 +12,13 @@ data class BleInfo(
val type: Type, val type: Type,
val scanTime: Long, val scanTime: Long,
val tx: Int, val tx: Int,
val recordEnabled: Boolean val tableStatus: HistoryTableStatus
) : Parcelable { ) : Parcelable {
enum class HistoryTableStatus {
DISABLED, EMPTY, NOT_EMPTY
}
enum class Type { enum class Type {
HOST, BEACON, THERMOMETER, ACCELEROMETER HOST, BEACON, THERMOMETER, ACCELEROMETER
} }

View File

@ -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<BleFilter?>
}

View File

@ -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<BleFilter> {
return settingsRepository.getFilterFlow().map {
it ?: BleFilter()
}
}
}

View File

@ -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)
}
}

View File

@ -17,5 +17,6 @@ plugins {
id 'com.android.application' version '8.1.1' apply false id 'com.android.application' version '8.1.1' apply false
id 'com.android.library' 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.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 id("androidx.room") version "2.6.1" apply false
} }