From 20793134061788d050041dda7c76a3d07cc425ba Mon Sep 17 00:00:00 2001 From: Dongmin Date: Thu, 29 Aug 2024 03:06:34 +0900 Subject: [PATCH 1/5] [FEAT/#49] feat something --- buildSrc/src/main/kotlin/Versions.kt | 2 +- stempo/src/main/AndroidManifest.xml | 1 + .../stempo/presentation/home/HomeScreen.kt | 34 +++++++++++++- .../presentation/home/HomeSideEffect.kt | 3 +- .../stempo/presentation/home/HomeState.kt | 1 + .../stempo/presentation/home/HomeViewModel.kt | 44 +++++++++++++++++-- 6 files changed, 78 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 66e803d..e592ad5 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -3,7 +3,7 @@ import org.gradle.api.JavaVersion object Versions { const val gradleVersion = "8.0.2" - const val kotlinVersion = "1.8.20" + const val kotlinVersion = "1.9.0" const val kotlinSerializationJsonVersion = "1.5.1" const val kotlinDateTimeVersion = "0.4.0" const val coreKtxVersion = "1.10.1" diff --git a/stempo/src/main/AndroidManifest.xml b/stempo/src/main/AndroidManifest.xml index ecfe4df..84c8ea5 100644 --- a/stempo/src/main/AndroidManifest.xml +++ b/stempo/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt index 98b84f2..b10aaa1 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt @@ -1,5 +1,9 @@ package com.kkkk.stempo.presentation.home +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import androidx.compose.foundation.Image @@ -19,10 +23,13 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import androidx.wear.compose.material.MaterialTheme +import androidx.wear.compose.material.Text import com.kkkk.stempo.R import com.kkkk.stempo.presentation.home.HomeViewModel.Companion.VIBRATION_DURATION @@ -30,10 +37,27 @@ import com.kkkk.stempo.presentation.home.HomeViewModel.Companion.VIBRATION_DURAT fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), ) { + val context = LocalContext.current val state by viewModel.state.collectAsStateWithLifecycle() val lifecycleOwner = LocalLifecycleOwner.current val vibrator = LocalContext.current.getSystemService(Vibrator::class.java) + LaunchedEffect(key1 = Unit) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACTIVITY_RECOGNITION + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions( + context as Activity, + arrayOf(Manifest.permission.ACTIVITY_RECOGNITION), + 200 + ) + } + } + } + LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { viewModel.sideEffect.flowWithLifecycle(lifecycleOwner.lifecycle).collect { sideEffect -> when (sideEffect) { @@ -46,6 +70,10 @@ fun HomeScreen( ) ) } + + HomeSideEffect.CountStep -> { + viewModel.startCounting(context) + } } } } @@ -54,11 +82,15 @@ fun HomeScreen( modifier = Modifier .fillMaxSize() .background(MaterialTheme.colors.background), - contentAlignment = Alignment.Center ) { MusicButton(isPlayingMusic = state.isPlayingMusic) { viewModel.controlMusic() } + + Text( + text = state.stepCount.toString(), + modifier = Modifier.align(Alignment.TopCenter) + ) } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt index eb2e090..9566c1a 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt @@ -1,5 +1,6 @@ package com.kkkk.stempo.presentation.home sealed class HomeSideEffect { - data object Vibrate : HomeSideEffect() + object Vibrate : HomeSideEffect() + object CountStep : HomeSideEffect() } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeState.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeState.kt index 2c48391..7cf121d 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeState.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeState.kt @@ -2,4 +2,5 @@ package com.kkkk.stempo.presentation.home data class HomeState( val isPlayingMusic: Boolean = false, + val stepCount: Int = 0, ) diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt index bfde5c5..ec1951f 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt @@ -1,6 +1,10 @@ package com.kkkk.stempo.presentation.home -import android.util.Log +import android.content.Context +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kkkk.domain.repository.UserRepository @@ -20,8 +24,8 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( - private val userRepository: UserRepository -) : ViewModel() { + private val userRepository: UserRepository, +) : ViewModel(), SensorEventListener { private val _state: MutableStateFlow = MutableStateFlow(HomeState()) val state: StateFlow @@ -31,9 +35,12 @@ constructor( val sideEffect: SharedFlow get() = _sideEffect.asSharedFlow() - private var vibrationJob: Job? = null + private var sensorManager: SensorManager? = null + private var stepSensor: Sensor? = null + + fun controlMusic() { _state.value = _state.value.copy(isPlayingMusic = !_state.value.isPlayingMusic) @@ -53,13 +60,42 @@ constructor( delay(VIBRATION_INTERVAL) } } + + viewModelScope.launch { + _sideEffect.emit(HomeSideEffect.CountStep) + } } private fun stopVibration() { vibrationJob?.cancel() vibrationJob = null + stopCounting() + } + + fun startCounting(context: Context) { + sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager + stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) + stepSensor?.let { + sensorManager?.registerListener(this, it, SensorManager.SENSOR_DELAY_UI) + } } + private fun stopCounting() { + sensorManager?.unregisterListener(this) + } + + override fun onSensorChanged(event: SensorEvent?) { + event?.let { + if (it.sensor.type == Sensor.TYPE_STEP_COUNTER) { + _state.value = _state.value.copy( + stepCount = it.values[0].toInt() + ) + } + } + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} + companion object { const val VIBRATION_INTERVAL = 500L // TODO: Server에서 받아올 예정 const val VIBRATION_DURATION = 100L From 24a6013eb040e67c47a4fefd703eb99289781e28 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Thu, 29 Aug 2024 04:19:20 +0900 Subject: [PATCH 2/5] [FEAT/#49] add sensor with wakeLock --- .../stempo/presentation/home/HomeScreen.kt | 48 +++++++++++++++++-- .../presentation/home/HomeSideEffect.kt | 3 +- .../stempo/presentation/home/HomeViewModel.kt | 39 ++------------- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt index b10aaa1..84600a6 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt @@ -2,10 +2,17 @@ package com.kkkk.stempo.presentation.home import android.Manifest import android.app.Activity +import android.content.Context import android.content.pm.PackageManager +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager import android.os.Build +import android.os.PowerManager import android.os.VibrationEffect import android.os.Vibrator +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -14,6 +21,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -37,11 +47,45 @@ import com.kkkk.stempo.presentation.home.HomeViewModel.Companion.VIBRATION_DURAT fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), ) { + lateinit var sensorManager: SensorManager + lateinit var sensorEventListener: SensorEventListener + val context = LocalContext.current val state by viewModel.state.collectAsStateWithLifecycle() val lifecycleOwner = LocalLifecycleOwner.current val vibrator = LocalContext.current.getSystemService(Vibrator::class.java) + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + val wakeLock = + powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "WearOS:KeepScreenOnWakeLock") + + sensorEventListener = object : SensorEventListener { + override fun onSensorChanged(event: SensorEvent) { + Log.e("TAG", "onSensorChanged: ") + if (event.sensor.type == Sensor.TYPE_STEP_DETECTOR) { + if (!state.isPlayingMusic) return + viewModel.addStep() + } + } + + override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { + // 정확도 변경 처리 (필요한 경우) + } + } + sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager + val stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR) + + sensorManager.registerListener( + sensorEventListener, + stepDetectorSensor, + SensorManager.SENSOR_DELAY_NORMAL + ) + + + LaunchedEffect(Unit) { // 화면 꺼짐 방지 + wakeLock.acquire() + } + LaunchedEffect(key1 = Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (ContextCompat.checkSelfPermission( @@ -70,10 +114,6 @@ fun HomeScreen( ) ) } - - HomeSideEffect.CountStep -> { - viewModel.startCounting(context) - } } } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt index 9566c1a..eb2e090 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt @@ -1,6 +1,5 @@ package com.kkkk.stempo.presentation.home sealed class HomeSideEffect { - object Vibrate : HomeSideEffect() - object CountStep : HomeSideEffect() + data object Vibrate : HomeSideEffect() } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt index ec1951f..1e41240 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt @@ -1,10 +1,5 @@ package com.kkkk.stempo.presentation.home -import android.content.Context -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kkkk.domain.repository.UserRepository @@ -25,7 +20,7 @@ class HomeViewModel @Inject constructor( private val userRepository: UserRepository, -) : ViewModel(), SensorEventListener { +) : ViewModel() { private val _state: MutableStateFlow = MutableStateFlow(HomeState()) val state: StateFlow @@ -37,9 +32,6 @@ constructor( private var vibrationJob: Job? = null - private var sensorManager: SensorManager? = null - private var stepSensor: Sensor? = null - fun controlMusic() { _state.value = _state.value.copy(isPlayingMusic = !_state.value.isPlayingMusic) @@ -60,42 +52,19 @@ constructor( delay(VIBRATION_INTERVAL) } } - - viewModelScope.launch { - _sideEffect.emit(HomeSideEffect.CountStep) - } } private fun stopVibration() { vibrationJob?.cancel() vibrationJob = null - stopCounting() - } - - fun startCounting(context: Context) { - sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager - stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) - stepSensor?.let { - sensorManager?.registerListener(this, it, SensorManager.SENSOR_DELAY_UI) - } - } - private fun stopCounting() { - sensorManager?.unregisterListener(this) + _state.value = _state.value.copy(stepCount = 0) } - override fun onSensorChanged(event: SensorEvent?) { - event?.let { - if (it.sensor.type == Sensor.TYPE_STEP_COUNTER) { - _state.value = _state.value.copy( - stepCount = it.values[0].toInt() - ) - } - } + fun addStep() { + _state.value = _state.value.copy(stepCount = _state.value.stepCount + 1) } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} - companion object { const val VIBRATION_INTERVAL = 500L // TODO: Server에서 받아올 예정 const val VIBRATION_DURATION = 100L From 3ef9bd4a374cbc5458aef79e7a05734e78f64d64 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Fri, 30 Aug 2024 03:54:41 +0900 Subject: [PATCH 3/5] [FEAT/#49] calculate accuracy --- .../main/rhythm/RhythmFragment.kt | 4 +- .../main/rhythm/RhythmViewModel.kt | 19 ++++++++ .../kkkk/stempo/presentation/WatchActivity.kt | 33 ++++++++----- .../stempo/presentation/home/HomeScreen.kt | 47 ++++++++++++------- .../presentation/home/HomeSideEffect.kt | 1 + .../stempo/presentation/home/HomeViewModel.kt | 33 ++++++++++--- .../manager/WearableDataManger.kt | 4 +- 7 files changed, 102 insertions(+), 39 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt index b5d08a0..91676ab 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt @@ -313,9 +313,9 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy event.dataItem.also { item -> if (item.uri.path?.compareTo(PATH_RECORD) == 0) { DataMapItem.fromDataItem(item).dataMap.apply { - val record = getInt(KEY_RECORD) + val record = getDouble(KEY_RECORD) Timber.tag("okhttp").d("LISTENER : DATA RECEIVED : $record") - // TODO 여기서 기록 받아서 서버통신으로 기록 + viewModel.posRhythmRecordToSaveWatch(record) } } } diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt index b004278..3db12b0 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt @@ -136,6 +136,25 @@ constructor( } } + fun posRhythmRecordToSaveWatch( + accuracy: Double, + ) { + viewModelScope.launch { + rhythmRepository.postRhythmRecord( + RecordRequestModel( + accuracy, + 0, + stepCount.value + ) + ).onSuccess { + resetStepInfo() + _isRecordSaved.emit(true) + }.onFailure { + _isRecordSaved.emit(false) + } + } + } + private fun resetStepInfo() { _stepCount.value = 0 _speed.value = 0.0 diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/WatchActivity.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/WatchActivity.kt index 78954ed..4e119d7 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/WatchActivity.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/WatchActivity.kt @@ -9,6 +9,8 @@ package com.kkkk.stempo.presentation import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.staticCompositionLocalOf import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.google.android.gms.wearable.DataClient import com.google.android.gms.wearable.DataEvent @@ -17,12 +19,15 @@ import com.google.android.gms.wearable.DataMapItem import com.google.android.gms.wearable.Wearable import com.kkkk.stempo.presentation.home.HomeScreen import com.kkkk.stempo.presentation.manager.WearableDataManager -import com.kkkk.stempo.presentation.manager.WearableDataManager.Companion.KEY_RECORD -import com.kkkk.stempo.presentation.manager.WearableDataManager.Companion.PATH_RECORD import com.kkkk.stempo.presentation.theme.StempoandroidTheme import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber +val LocalWearableDataManager = + staticCompositionLocalOf { + error("No DataClient provided") + } + @AndroidEntryPoint class WatchActivity : ComponentActivity(), DataClient.OnDataChangedListener { override fun onCreate(savedInstanceState: Bundle?) { @@ -33,17 +38,18 @@ class WatchActivity : ComponentActivity(), DataClient.OnDataChangedListener { setTheme(android.R.style.Theme_DeviceDefault) setContent { - StempoandroidTheme { - HomeScreen() + CompositionLocalProvider( + LocalWearableDataManager provides WearableDataManager( + Wearable.getDataClient( + this + ) + ) + ) { + StempoandroidTheme { + HomeScreen() + } } } - - // TODO 이 함수로 정지 시 결과값 전송 - WearableDataManager(Wearable.getDataClient(this)).sendIntToPhone( - PATH_RECORD, - KEY_RECORD, - 50 - ) } override fun onResume() { @@ -67,7 +73,7 @@ class WatchActivity : ComponentActivity(), DataClient.OnDataChangedListener { DataMapItem.fromDataItem(item).dataMap.apply { val bpm = getInt(KEY_BPM) Timber.tag("okhttp").d("LISTENER : DATA RECEIVED : $bpm") - // TODO 여기서 bpm 받아서 초기값으로 설정 + VIBRATION_INTERVAL = (bpm/60.0).toLong() } } } @@ -77,7 +83,8 @@ class WatchActivity : ComponentActivity(), DataClient.OnDataChangedListener { companion object { const val KEY_BPM = "KEY_BPM" - const val PATH_BPM = "/bpm" + + var VIBRATION_INTERVAL = 500L } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt index 84600a6..41b2fa6 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt @@ -12,7 +12,6 @@ import android.os.Build import android.os.PowerManager import android.os.VibrationEffect import android.os.Vibrator -import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -21,9 +20,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -41,12 +37,16 @@ import androidx.lifecycle.flowWithLifecycle import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.Text import com.kkkk.stempo.R +import com.kkkk.stempo.presentation.LocalWearableDataManager import com.kkkk.stempo.presentation.home.HomeViewModel.Companion.VIBRATION_DURATION +import com.kkkk.stempo.presentation.manager.WearableDataManager.Companion.KEY_RECORD +import com.kkkk.stempo.presentation.manager.WearableDataManager.Companion.PATH_RECORD @Composable fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), ) { + val wearableDataManager = LocalWearableDataManager.current lateinit var sensorManager: SensorManager lateinit var sensorEventListener: SensorEventListener @@ -61,9 +61,9 @@ fun HomeScreen( sensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { - Log.e("TAG", "onSensorChanged: ") if (event.sensor.type == Sensor.TYPE_STEP_DETECTOR) { if (!state.isPlayingMusic) return + viewModel.addStep() } } @@ -72,14 +72,17 @@ fun HomeScreen( // 정확도 변경 처리 (필요한 경우) } } - sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager - val stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR) - sensorManager.registerListener( - sensorEventListener, - stepDetectorSensor, - SensorManager.SENSOR_DELAY_NORMAL - ) + LaunchedEffect(key1 = Unit) { + sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager + val stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR) + + sensorManager.registerListener( + sensorEventListener, + stepDetectorSensor, + SensorManager.SENSOR_DELAY_NORMAL + ) + } LaunchedEffect(Unit) { // 화면 꺼짐 방지 @@ -114,6 +117,16 @@ fun HomeScreen( ) ) } + + is HomeSideEffect.EndCount -> { + wearableDataManager.sendDoubleToPhone( + PATH_RECORD, + KEY_RECORD, + sideEffect.accuracy + ) + + viewModel.resetStepInfo() + } } } } @@ -127,10 +140,12 @@ fun HomeScreen( viewModel.controlMusic() } - Text( - text = state.stepCount.toString(), - modifier = Modifier.align(Alignment.TopCenter) - ) + if (state.isPlayingMusic) { + Text( + text = state.stepCount.toString(), + modifier = Modifier.align(Alignment.TopCenter) + ) + } } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt index eb2e090..7a2d3e4 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeSideEffect.kt @@ -2,4 +2,5 @@ package com.kkkk.stempo.presentation.home sealed class HomeSideEffect { data object Vibrate : HomeSideEffect() + data class EndCount(val accuracy: Double) : HomeSideEffect() } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt index 1e41240..ef61379 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt @@ -3,6 +3,7 @@ package com.kkkk.stempo.presentation.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kkkk.domain.repository.UserRepository +import com.kkkk.stempo.presentation.WatchActivity.Companion.VIBRATION_INTERVAL import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -14,14 +15,12 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.math.max @HiltViewModel -class HomeViewModel -@Inject -constructor( +class HomeViewModel @Inject constructor( private val userRepository: UserRepository, ) : ViewModel() { - private val _state: MutableStateFlow = MutableStateFlow(HomeState()) val state: StateFlow get() = _state.asStateFlow() @@ -32,6 +31,9 @@ constructor( private var vibrationJob: Job? = null + private val _speed = MutableStateFlow(0.0) + + private val _firstStepTime = MutableStateFlow(0L) fun controlMusic() { _state.value = _state.value.copy(isPlayingMusic = !_state.value.isPlayingMusic) @@ -45,6 +47,7 @@ constructor( private fun startVibration() { if (vibrationJob?.isActive == true) return + _state.value = _state.value.copy(stepCount = 0) vibrationJob = viewModelScope.launch { while (true) { @@ -52,12 +55,31 @@ constructor( delay(VIBRATION_INTERVAL) } } + _firstStepTime.value = System.currentTimeMillis() + } + + fun resetStepInfo() { + _speed.value = 0.0 } private fun stopVibration() { vibrationJob?.cancel() vibrationJob = null + var accuracy = + (_state.value.stepCount.toDouble() / (VIBRATION_INTERVAL / 60 * ((System.currentTimeMillis() - _firstStepTime.value) / 10000))) + if (accuracy > 1) { + accuracy = max(2 - accuracy, 0.0) + } + + viewModelScope.launch { + _sideEffect.emit( + HomeSideEffect.EndCount( + accuracy + ) + ) + } + _state.value = _state.value.copy(stepCount = 0) } @@ -66,7 +88,6 @@ constructor( } companion object { - const val VIBRATION_INTERVAL = 500L // TODO: Server에서 받아올 예정 - const val VIBRATION_DURATION = 100L + const val VIBRATION_DURATION = 50L } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/manager/WearableDataManger.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/manager/WearableDataManger.kt index 234a3bf..63f8c7c 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/manager/WearableDataManger.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/manager/WearableDataManger.kt @@ -9,12 +9,12 @@ import timber.log.Timber class WearableDataManager(private val dataClient: DataClient) { - fun sendIntToPhone(path: String, key: String, value: Int): Task { + fun sendDoubleToPhone(path: String, key: String, value: Double): Task { Timber.tag("okhttp").d("START SENDING DATA TO PHONE") val putDataReq: PutDataRequest = PutDataMapRequest.create(path).run { - dataMap.putInt(key, value) + dataMap.putDouble(key, value) asPutDataRequest().setUrgent() } From eb9eeb8ca2e60fccbe4e28c3452e9dd2f1483b82 Mon Sep 17 00:00:00 2001 From: Dongmin Date: Fri, 30 Aug 2024 05:29:41 +0900 Subject: [PATCH 4/5] [FEAT/#49] change logic to get accuracy --- .../main/rhythm/RhythmFragment.kt | 16 ------------ .../main/rhythm/RhythmViewModel.kt | 26 ++++++++----------- .../stempo/presentation/home/HomeScreen.kt | 2 -- .../stempo/presentation/home/HomeViewModel.kt | 6 ----- 4 files changed, 11 insertions(+), 39 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt index 91676ab..8a48cc1 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt @@ -293,10 +293,6 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy override fun onSensorChanged(event: SensorEvent?) { if (event?.sensor?.type == Sensor.TYPE_STEP_DETECTOR) { viewModel.addStepCount(1) - - if (viewModel.stepCount.value % SPEED_CALC_INTERVAL == 0) { - calculateSpeed() - } } } @@ -323,18 +319,6 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy } } - private fun calculateSpeed() { - val currentTime = System.currentTimeMillis() - val lastStepTime = viewModel.lastStepTime.value - if (lastStepTime != 0L) { - val timeDiff = currentTime - lastStepTime - val speed = (SPEED_CALC_INTERVAL / (timeDiff / 1000.0)) * 60 // 분당 걸음 수 - - viewModel.setSpeed(speed) - } - viewModel.setLastStepTime(currentTime) - } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} companion object { diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt index 3db12b0..0cb937b 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmViewModel.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.math.max @HiltViewModel class RhythmViewModel @@ -44,10 +45,7 @@ constructor( private val _stepCount = MutableStateFlow(0) val stepCount: StateFlow = _stepCount - private val _speed = MutableStateFlow(0.0) - - private val _lastStepTime = MutableStateFlow(0L) - val lastStepTime: StateFlow = _lastStepTime + private val _firstStepTime = MutableStateFlow(0L) init { initRhythmLevelFromDataStore() @@ -65,14 +63,6 @@ constructor( _stepCount.value += newStepCount } - fun setSpeed(newSpeed: Double) { - _speed.value = newSpeed - } - - fun setLastStepTime(newLastStepTime: Long) { - _lastStepTime.value = newLastStepTime - } - fun setTempRhythmLevel(level: Int) { isSubmitted = false tempRhythmLevel.value = level @@ -112,6 +102,7 @@ constructor( rhythmRepository.getRhythmWav(url) .onSuccess { _downloadWavState.value = UiState.Success(it) + _firstStepTime.value = System.currentTimeMillis() } .onFailure { _downloadWavState.value = UiState.Failure(it.message.toString()) @@ -120,10 +111,16 @@ constructor( } fun posRhythmRecordToSave() { + var accuracy = + (stepCount.value.toDouble() / (bpm / 60 * ((System.currentTimeMillis() - _firstStepTime.value) / 10000))) + if (accuracy > 1) { + accuracy = max(2 - accuracy, 0.0) + } + viewModelScope.launch { rhythmRepository.postRhythmRecord( RecordRequestModel( - _speed.value / (_stepCount.value / OnboardingViewModel.SPEED_CALC_INTERVAL + 1), + accuracy, 0, stepCount.value ) @@ -157,8 +154,7 @@ constructor( private fun resetStepInfo() { _stepCount.value = 0 - _speed.value = 0.0 - _lastStepTime.value = 0L + _firstStepTime.value = 0L } fun getBpmFromDataStore() = userRepository.getBpm() diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt index 41b2fa6..4191b60 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeScreen.kt @@ -124,8 +124,6 @@ fun HomeScreen( KEY_RECORD, sideEffect.accuracy ) - - viewModel.resetStepInfo() } } } diff --git a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt index ef61379..a39a3f8 100644 --- a/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt +++ b/stempo/src/main/java/com/kkkk/stempo/presentation/home/HomeViewModel.kt @@ -31,8 +31,6 @@ class HomeViewModel @Inject constructor( private var vibrationJob: Job? = null - private val _speed = MutableStateFlow(0.0) - private val _firstStepTime = MutableStateFlow(0L) fun controlMusic() { @@ -58,10 +56,6 @@ class HomeViewModel @Inject constructor( _firstStepTime.value = System.currentTimeMillis() } - fun resetStepInfo() { - _speed.value = 0.0 - } - private fun stopVibration() { vibrationJob?.cancel() vibrationJob = null From 94a149c2ca187189d4c5fa2dee9cb2b1074f2c9b Mon Sep 17 00:00:00 2001 From: Dongmin Date: Tue, 10 Sep 2024 20:22:53 +0900 Subject: [PATCH 5/5] [FEAT/#49] connect observer and delete distinctUtilChanged --- .../kkkk/presentation/main/rhythm/RhythmFragment.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt index 8a48cc1..5b487a6 100644 --- a/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/main/rhythm/RhythmFragment.kt @@ -67,6 +67,7 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy initPlayBtnListener() initStopBtnListener() initWearableSyncBtnListener() + observeStepCount() observeRhythmLevel() observeRhythmUrlState() observeDownloadState() @@ -119,6 +120,12 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy } } + private fun observeStepCount() { + viewModel.stepCount.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { level -> + binding.tvRhythmStep.text = viewModel.stepCount.value.toString() + }.launchIn(lifecycleScope) + } + private fun observeRhythmLevel() { viewModel.rhythmLevel.flowWithLifecycle(lifecycle).distinctUntilChanged().onEach { level -> if (level == LEVEL_UNDEFINED) return@onEach @@ -237,8 +244,7 @@ class RhythmFragment : BaseFragment(R.layout.fragment_rhy } private fun observeRecordSaveState() { - viewModel.isRecordSaved.flowWithLifecycle(lifecycle).distinctUntilChanged() - .onEach { isSuccess -> + viewModel.isRecordSaved.flowWithLifecycle(lifecycle).onEach { isSuccess -> if (isSuccess) { toast(stringOf(R.string.rhythm_toast_save_success)) } else {