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 {
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')
}

187
app/proguard-rules.pro vendored
View File

@ -18,4 +18,189 @@
# If you keep the line number information, uncomment this to
# 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.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
}

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.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<ConnectedBleInfo>,
val bleList: List<BleInfo>,
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
}
}
}*/
}

View File

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

View File

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

View File

@ -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<BleInfo.Type?>(null).apply {
addAll(BleInfo.Type.values())
addAll(BleInfo.Type.entries.toTypedArray())
}.forEach { selectionOption ->
DropdownMenuItem(
onClick = {

View File

@ -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<AxisPosition.Vertical.Start> { value, chartValues ->
" "
}
val axisValueFormatter =
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { 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(

View File

@ -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<Ble.Thermometer.ThermometerState, BleException> {
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<Ble.Accelerometer.AccelerometerState, BleException> {
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()

View File

@ -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<Byte>()
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<List<String>>()
var periodBle = mutableListOf<String>()
var periods = mutableListOf<Pair<Boolean, List<String>>>()
var periodBle = mutableListOf<String>()
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
)
}
)

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

View File

@ -116,6 +116,7 @@ sealed class Ble(
class HistoryPoint(
val date: Long,
val hit: Boolean,
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 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
}

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.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
}