Skip to content

Commit f2d2cb0

Browse files
authored
Merge pull request #34 from victory316/develop
Develop
2 parents 3772673 + 27cd541 commit f2d2cb0

File tree

7 files changed

+222
-13
lines changed

7 files changed

+222
-13
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
applicationId = "com.choidev.latesteffort"
1515
minSdk = 28
1616
targetSdk = 33
17-
versionCode = 5
18-
versionName = "1.0.5"
17+
versionCode = 6
18+
versionName = "1.0.6"
1919

2020
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2121
}

core/util/build.gradle.kts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,30 @@ android {
3333
kotlinOptions {
3434
jvmTarget = JavaVersion.VERSION_17.toString()
3535
}
36+
composeOptions {
37+
kotlinCompilerExtensionVersion = "1.4.3"
38+
}
39+
buildFeatures {
40+
compose = true
41+
}
3642
}
3743

3844
dependencies {
3945

4046
implementation(libs.core.ktx)
4147
implementation(libs.appcompat)
4248
implementation(libs.material)
49+
implementation(platform(libs.androidx.compose.bom))
50+
implementation(libs.androidx.lifecycle.runtimeCompose)
51+
implementation(libs.androidx.compose.runtime.tracing)
52+
implementation(libs.androidx.compose.material3)
53+
implementation(libs.androidx.compose.material3.windowSizeClass)
54+
implementation(libs.androidx.activity.compose)
55+
implementation(libs.androidx.compose.ui.tooling)
56+
implementation(libs.androidx.compose.ui.tooling.preview)
57+
implementation(libs.vico.core)
58+
implementation(libs.vico.compose)
59+
implementation(libs.vico.compose.m3)
4360
implementation(libs.hilt.android)
4461
implementation(libs.androidx.hilt.navigation.compose)
4562
kapt(libs.hilt.compiler)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package com.choidev.latesteffort.core.util.chart
2+
3+
import android.graphics.Typeface
4+
import androidx.compose.foundation.isSystemInDarkTheme
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.ui.graphics.Brush
8+
import androidx.compose.ui.graphics.Color
9+
import androidx.compose.ui.graphics.toArgb
10+
import androidx.compose.ui.unit.dp
11+
import androidx.compose.ui.unit.sp
12+
import com.choidev.latesteffort.core.util.motion.CachedAccelerometerData
13+
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
14+
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
15+
import com.patrykandpatrick.vico.compose.chart.Chart
16+
import com.patrykandpatrick.vico.compose.chart.line.lineChart
17+
import com.patrykandpatrick.vico.compose.component.shape.shader.fromBrush
18+
import com.patrykandpatrick.vico.compose.component.shapeComponent
19+
import com.patrykandpatrick.vico.compose.component.textComponent
20+
import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf
21+
import com.patrykandpatrick.vico.compose.legend.legendItem
22+
import com.patrykandpatrick.vico.compose.legend.verticalLegend
23+
import com.patrykandpatrick.vico.compose.style.ChartStyle
24+
import com.patrykandpatrick.vico.compose.style.ProvideChartStyle
25+
import com.patrykandpatrick.vico.compose.style.currentChartStyle
26+
import com.patrykandpatrick.vico.core.DefaultAlpha
27+
import com.patrykandpatrick.vico.core.DefaultColors
28+
import com.patrykandpatrick.vico.core.DefaultDimens
29+
import com.patrykandpatrick.vico.core.chart.copy
30+
import com.patrykandpatrick.vico.core.chart.line.LineChart
31+
import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider
32+
import com.patrykandpatrick.vico.core.component.shape.LineComponent
33+
import com.patrykandpatrick.vico.core.component.shape.Shapes
34+
import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders
35+
import com.patrykandpatrick.vico.core.entry.entriesOf
36+
import com.patrykandpatrick.vico.core.entry.entryModelOf
37+
38+
private const val COLOR_1_CODE = 0xffb983ff
39+
private const val COLOR_2_CODE = 0xff91b1fd
40+
private const val COLOR_3_CODE = 0xff8fdaff
41+
private val color1 = Color(COLOR_1_CODE)
42+
private val color2 = Color(COLOR_2_CODE)
43+
private val color3 = Color(COLOR_3_CODE)
44+
private val chartColors = listOf(color1, color2, color3)
45+
46+
private val legendItemLabelTextSize = 12.sp
47+
48+
private val legendStrings = listOf("ACC X", "ACC Y", "ACC Z")
49+
private val legendItemIconSize = 8.dp
50+
private val legendItemIconPaddingValue = 10.dp
51+
private val legendItemSpacing = 4.dp
52+
private val legendTopPaddingValue = 8.dp
53+
private val legendPadding = dimensionsOf(top = legendTopPaddingValue)
54+
55+
@Composable
56+
fun DynamicAccelerometerChart(data: CachedAccelerometerData) {
57+
val axisValueOverrider = AxisValuesOverrider.fixed(minY = -20f, maxY = 20f)
58+
59+
val chartEntryModel = entryModelOf(
60+
entriesOf(*data.accelerationX.toNumberPairs()),
61+
entriesOf(*data.accelerationY.toNumberPairs()),
62+
entriesOf(*data.accelerationZ.toNumberPairs()),
63+
)
64+
65+
ProvideChartStyle(rememberChartStyle(chartColors)) {
66+
val defaultLines = currentChartStyle.lineChart.lines
67+
Chart(
68+
chart = lineChart(
69+
remember(defaultLines) {
70+
defaultLines.map { defaultLine -> defaultLine.copy(lineBackgroundShader = null) }
71+
},
72+
axisValuesOverrider = axisValueOverrider
73+
),
74+
model = chartEntryModel,
75+
startAxis = startAxis(),
76+
bottomAxis = bottomAxis(),
77+
legend = rememberLegend()
78+
)
79+
}
80+
}
81+
82+
@Composable
83+
private fun rememberLegend() = verticalLegend(
84+
items = chartColors.mapIndexed { index, chartColor ->
85+
legendItem(
86+
icon = shapeComponent(Shapes.pillShape, chartColor),
87+
label = textComponent(
88+
color = currentChartStyle.axis.axisLabelColor,
89+
textSize = legendItemLabelTextSize,
90+
typeface = Typeface.MONOSPACE,
91+
),
92+
labelText = legendStrings[index],
93+
)
94+
},
95+
iconSize = legendItemIconSize,
96+
iconPadding = legendItemIconPaddingValue,
97+
spacing = legendItemSpacing,
98+
padding = legendPadding,
99+
)
100+
101+
@Composable
102+
internal fun rememberChartStyle(
103+
columnChartColors: List<Color>,
104+
lineChartColors: List<Color>
105+
): ChartStyle {
106+
val isSystemInDarkTheme = isSystemInDarkTheme()
107+
return remember(columnChartColors, lineChartColors, isSystemInDarkTheme) {
108+
val defaultColors = if (isSystemInDarkTheme) DefaultColors.Dark else DefaultColors.Light
109+
ChartStyle(
110+
ChartStyle.Axis(
111+
axisLabelColor = Color(defaultColors.axisLabelColor),
112+
axisGuidelineColor = Color(defaultColors.axisGuidelineColor),
113+
axisLineColor = Color(defaultColors.axisLineColor),
114+
),
115+
ChartStyle.ColumnChart(
116+
columnChartColors.map { columnChartColor ->
117+
LineComponent(
118+
columnChartColor.toArgb(),
119+
DefaultDimens.COLUMN_WIDTH,
120+
Shapes.roundedCornerShape(DefaultDimens.COLUMN_ROUNDNESS_PERCENT),
121+
)
122+
},
123+
),
124+
ChartStyle.LineChart(
125+
lineChartColors.map { lineChartColor ->
126+
LineChart.LineSpec(
127+
lineColor = lineChartColor.toArgb(),
128+
lineBackgroundShader = DynamicShaders.fromBrush(
129+
Brush.verticalGradient(
130+
listOf(
131+
lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START),
132+
lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END),
133+
),
134+
),
135+
),
136+
)
137+
},
138+
),
139+
ChartStyle.Marker(),
140+
Color(defaultColors.elevationOverlayColor),
141+
)
142+
}
143+
}
144+
145+
@Composable
146+
internal fun rememberChartStyle(chartColors: List<Color>) =
147+
rememberChartStyle(columnChartColors = chartColors, lineChartColors = chartColors)
148+
149+
private fun List<Float>.toNumberPairs(): Array<Pair<Number, Number>> {
150+
return this.mapIndexed { index, value -> index to value }.toTypedArray()
151+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.choidev.latesteffort.core.util.motion
2+
3+
data class CachedAccelerometerData(
4+
val accelerationX: List<Float> = listOf(0f),
5+
val accelerationY: List<Float> = listOf(0f),
6+
val accelerationZ: List<Float> = listOf(0f)
7+
)

feature/motion/src/main/java/com/supergene/loki/feature/motion/MotionViewModel.kt

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import androidx.lifecycle.viewModelScope
55
import com.choidev.core.actions.VibrateAction
66
import com.choidev.core.actions.mapToId
77
import com.choidev.latesteffort.core.util.motion.AccelerometerData
8+
import com.choidev.latesteffort.core.util.motion.CachedAccelerometerData
89
import com.choidev.latesteffort.core.util.motion.MotionManager
910
import com.choidev.latesteffort.core.util.motion.SensorRate
1011
import com.choidev.latesteffort.core.util.vibration.VibrationManager
1112
import dagger.hilt.android.lifecycle.HiltViewModel
1213
import kotlinx.coroutines.flow.MutableStateFlow
1314
import kotlinx.coroutines.flow.SharingStarted
15+
import kotlinx.coroutines.flow.asStateFlow
1416
import kotlinx.coroutines.flow.stateIn
17+
import kotlinx.coroutines.flow.update
1518
import kotlinx.coroutines.launch
1619
import javax.inject.Inject
1720
import kotlin.math.absoluteValue
@@ -41,6 +44,9 @@ class MotionViewModel @Inject constructor(
4144
started = SharingStarted.WhileSubscribed(5_000),
4245
)
4346

47+
private val _cachedAccelerometerData = MutableStateFlow(CachedAccelerometerData())
48+
val cachedAccelerometerData = _cachedAccelerometerData.asStateFlow()
49+
4450
init {
4551
observeAccelerometer()
4652
observeThreshold()
@@ -50,15 +56,35 @@ class MotionViewModel @Inject constructor(
5056
fun observeAccelerometer(rate: SensorRate = SensorRate.NORMAL) {
5157
currentRate.value = rate
5258

53-
motionManager.observeAccelerometer(rate) {
54-
_accelerometerData.value = it.copy(
55-
gravityX = it.gravityX.roundTo(fractionDigit.value),
56-
gravityY = it.gravityY.roundTo(fractionDigit.value),
57-
gravityZ = it.gravityZ.roundTo(fractionDigit.value),
58-
accelerationX = it.accelerationX.roundTo(fractionDigit.value),
59-
accelerationY = it.accelerationY.roundTo(fractionDigit.value),
60-
accelerationZ = it.accelerationZ.roundTo(fractionDigit.value)
59+
motionManager.observeAccelerometer(rate) { data ->
60+
_accelerometerData.value = data.copy(
61+
gravityX = data.gravityX.roundTo(fractionDigit.value),
62+
gravityY = data.gravityY.roundTo(fractionDigit.value),
63+
gravityZ = data.gravityZ.roundTo(fractionDigit.value),
64+
accelerationX = data.accelerationX.roundTo(fractionDigit.value),
65+
accelerationY = data.accelerationY.roundTo(fractionDigit.value),
66+
accelerationZ = data.accelerationZ.roundTo(fractionDigit.value)
6167
)
68+
69+
_cachedAccelerometerData.update {
70+
it.copy(
71+
accelerationX = it.accelerationX.toMutableList().apply {
72+
add(data.accelerationX)
73+
74+
if (this.size > 6) removeFirst()
75+
},
76+
accelerationY = it.accelerationY.toMutableList().apply {
77+
add(data.accelerationY)
78+
79+
if (this.size > 6) removeFirst()
80+
},
81+
accelerationZ = it.accelerationZ.toMutableList().apply {
82+
add(data.accelerationZ)
83+
84+
if (this.size > 6) removeFirst()
85+
}
86+
)
87+
}
6288
}
6389
}
6490

feature/motion/src/main/java/com/supergene/loki/feature/motion/ui/MotionScreen.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
2828
import com.choidev.core.actions.presenter.ActionPresenter
2929
import com.choidev.latesteffort.core.design.compose.DividerPaddingVertical
3030
import com.choidev.latesteffort.core.design.compose.ScreenPaddingHorizontal
31+
import com.choidev.latesteffort.core.util.chart.DynamicAccelerometerChart
3132
import com.choidev.latesteffort.core.util.motion.AccelerometerData
3233
import com.choidev.latesteffort.core.util.motion.SensorRate
3334
import com.supergene.loki.feature.motion.MotionViewModel
@@ -73,6 +74,7 @@ fun AccelerometerUi(
7374
var openRateDialog by remember { mutableStateOf(false) }
7475
val shakeThreshold by viewModel.shakeThreshold.collectAsStateWithLifecycle()
7576
val fractionDigit by viewModel.fractionDigit.collectAsStateWithLifecycle()
77+
val cachedAccelerometerData by viewModel.cachedAccelerometerData.collectAsStateWithLifecycle()
7678

7779
Column(
7880
verticalArrangement = Arrangement.spacedBy(12.dp),
@@ -141,6 +143,8 @@ fun AccelerometerUi(
141143
steps = 10,
142144
modifier = Modifier.fillMaxWidth()
143145
)
146+
147+
DynamicAccelerometerChart(cachedAccelerometerData)
144148
}
145149

146150
if (openRateDialog) {

gradle/libs.versions.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ rxJava = "3.1.5"
4242
roomVersion = "2.5.2"
4343
retrofit = "2.9.0"
4444
retrofitKotlinxSerializationJson = "1.0.0"
45+
vico = "1.8.1"
4546

4647
[libraries]
4748
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
@@ -78,11 +79,11 @@ hilt-ext-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.r
7879
hilt-ext-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltExt" }
7980
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
8081
ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
81-
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
82+
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
83+
lifecycleKtx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
8284
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
8385
junit = { group = "junit", name = "junit", version.ref = "junit" }
8486
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
85-
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
8687
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
8788
retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
8889
retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" }
@@ -92,15 +93,18 @@ okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor",
9293
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
9394
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
9495
volley = { group = "com.android.volley", name = "volley", version = "1.2.1" }
95-
lifecycleKtx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
9696
rxAndroid = { group = "io.reactivex.rxjava3", name = "rxandroid", version.ref = "rxAndroid" }
9797
rxJava = { group = "io.reactivex.rxjava3", name = "rxjava", version.ref = "rxJava" }
9898
coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" }
9999
coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
100100
coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" }
101+
vico-core = { group = "com.patrykandpatrick.vico", name = "core", version.ref = "vico" }
102+
vico-compose = { group = "com.patrykandpatrick.vico", name = "compose", version.ref = "vico" }
103+
vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" }
101104

102105
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }
103106
androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" }
107+
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
104108
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" }
105109
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" }
106110
androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version = "1.1.5" }

0 commit comments

Comments
 (0)