filter saving
This commit is contained in:
parent
5293604ee4
commit
666757922d
|
|
@ -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')
|
||||
|
||||
}
|
||||
|
|
@ -19,3 +19,188 @@
|
|||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-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
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ sealed class Ble(
|
|||
|
||||
class HistoryPoint(
|
||||
val date: Long,
|
||||
val hit: Boolean,
|
||||
val value: List<String>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?>
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue