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 0cb937b..d360cd1 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 @@ -7,7 +7,6 @@ import com.kkkk.core.state.UiState import com.kkkk.domain.entity.request.RecordRequestModel import com.kkkk.domain.repository.RhythmRepository import com.kkkk.domain.repository.UserRepository -import com.kkkk.presentation.onboarding.onbarding.OnboardingViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -45,7 +44,13 @@ constructor( private val _stepCount = MutableStateFlow(0) val stepCount: StateFlow = _stepCount - private val _firstStepTime = MutableStateFlow(0L) + private val _oddStepCount = MutableStateFlow(0) + private val _oddStepTime = MutableStateFlow(0L) + + private val _evenStepCount = MutableStateFlow(0) + private val _evenStepTime = MutableStateFlow(0L) + + private val _beforeStepTime = MutableStateFlow(0L) init { initRhythmLevelFromDataStore() @@ -60,7 +65,15 @@ constructor( } fun addStepCount(newStepCount: Int) { + if ((_oddStepCount.value + _evenStepCount.value) % 2 == 0) { + _oddStepCount.value += newStepCount + _oddStepTime.value = System.currentTimeMillis() - _beforeStepTime.value + } else { + _evenStepCount.value += newStepCount + _evenStepTime.value = System.currentTimeMillis() - _beforeStepTime.value + } _stepCount.value += newStepCount + _beforeStepTime.value = System.currentTimeMillis() } fun setTempRhythmLevel(level: Int) { @@ -102,7 +115,7 @@ constructor( rhythmRepository.getRhythmWav(url) .onSuccess { _downloadWavState.value = UiState.Success(it) - _firstStepTime.value = System.currentTimeMillis() + _beforeStepTime.value = System.currentTimeMillis() } .onFailure { _downloadWavState.value = UiState.Failure(it.message.toString()) @@ -111,11 +124,7 @@ constructor( } fun posRhythmRecordToSave() { - var accuracy = - (stepCount.value.toDouble() / (bpm / 60 * ((System.currentTimeMillis() - _firstStepTime.value) / 10000))) - if (accuracy > 1) { - accuracy = max(2 - accuracy, 0.0) - } + val accuracy = calculateAccuracy(_oddStepTime.value, _evenStepTime.value) viewModelScope.launch { rhythmRepository.postRhythmRecord( @@ -133,6 +142,16 @@ constructor( } } + private fun calculateAccuracy(time1: Long, time2: Long): Double { + val difference = kotlin.math.abs(time1 - time2) + + return when { + difference == 0L -> 1.0 + difference >= MAX_ALLOWED_DIFFERENCE -> 0.0 + else -> (1 - difference.toDouble() / MAX_ALLOWED_DIFFERENCE) + } + } + fun posRhythmRecordToSaveWatch( accuracy: Double, ) { @@ -153,8 +172,11 @@ constructor( } private fun resetStepInfo() { - _stepCount.value = 0 - _firstStepTime.value = 0L + _oddStepCount.value = 0 + _evenStepCount.value = 0 + _oddStepTime.value = 0L + _evenStepTime.value = 0L + _beforeStepTime.value = 0L } fun getBpmFromDataStore() = userRepository.getBpm() @@ -176,5 +198,6 @@ constructor( companion object { const val LEVEL_UNDEFINED = -1 + const val MAX_ALLOWED_DIFFERENCE = 360000L } } \ No newline at end of file diff --git a/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingMeasureFragment.kt b/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingMeasureFragment.kt index ef966b3..17fcc2e 100644 --- a/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingMeasureFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingMeasureFragment.kt @@ -29,19 +29,10 @@ class OnboardingMeasureFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initButtonListener() initializeSensor() checkAndRequestPermission() } - private fun initButtonListener() { - with(binding) { - btnOnboardingMeasureLater.setOnClickListener { - viewModel.setState(OnboardingState.DONE) - } - } - } - private fun initializeSensor() { sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR) diff --git a/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingStartFragment.kt b/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingStartFragment.kt index 0632a04..a394eb4 100644 --- a/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingStartFragment.kt +++ b/presentation/src/main/java/com/kkkk/presentation/onboarding/onbarding/OnboardingStartFragment.kt @@ -19,9 +19,6 @@ class OnboardingStartFragment : private fun initButtonListener() { with(binding) { - btnOnboardingStartLater.setOnClickListener { - viewModel.setState(OnboardingState.DONE) - } btnOnboardingStartMeasure.setOnClickListener { viewModel.setState(OnboardingState.MEASURE) } diff --git a/presentation/src/main/java/com/kkkk/presentation/onboarding/splash/SplashActivity.kt b/presentation/src/main/java/com/kkkk/presentation/onboarding/splash/SplashActivity.kt index 817f035..99e99a7 100644 --- a/presentation/src/main/java/com/kkkk/presentation/onboarding/splash/SplashActivity.kt +++ b/presentation/src/main/java/com/kkkk/presentation/onboarding/splash/SplashActivity.kt @@ -1,9 +1,21 @@ package com.kkkk.presentation.onboarding.splash +import android.Manifest import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.Settings +import android.view.View import androidx.activity.viewModels +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.kkkk.core.base.BaseActivity @@ -29,8 +41,15 @@ class SplashActivity : BaseActivity(R.layout.activity_spl setStatusBarColor() setNavigationBarColor() observeStates() + } - viewModel.checkTokenState() + override fun onResume() { + super.onResume() + if (isActivityRecognitionPermissionGranted(this)) { + viewModel.checkTokenState() + } else { + showDialog() + } } private fun setStatusBarColor() = setStatusBarColorFromResource(R.color.purple_50) @@ -65,6 +84,65 @@ class SplashActivity : BaseActivity(R.layout.activity_spl viewModel.setAndroidId(getDeviceTag()) } + private fun isActivityRecognitionPermissionGranted(context: Context): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACTIVITY_RECOGNITION + ) == PackageManager.PERMISSION_GRANTED + } else { + // Android Q 미만 버전에서는 이 권한이 필요하지 않으므로 항상 true 반환 + true + } + } + + private fun showDialog() { + val dialog = Dialog(this) + dialog.setContentView(R.layout.dialog_single_button) + dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog.setCanceledOnTouchOutside(false) + dialog.setCancelable(false) + dialog.show() + + dialog.findViewById(R.id.btn_onboarding_start_measure).setOnClickListener { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.ACTIVITY_RECOGNITION), + 200 + ) + } else { + navigateToSettings() + } + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray, + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + if (requestCode == 200) { + if (permissions.isNotEmpty() && permissions[0] == Manifest.permission.ACTIVITY_RECOGNITION) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + viewModel.checkTokenState() + } else { + navigateToSettings() + } + } + } + } + + private fun navigateToSettings() { + Intent().apply { + action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + data = Uri.fromParts("package", packageName, null) + startActivity(this) + } + } + @SuppressLint("HardwareIds") private fun getDeviceTag(): String = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) diff --git a/presentation/src/main/res/layout/dialog_single_button.xml b/presentation/src/main/res/layout/dialog_single_button.xml new file mode 100644 index 0000000..03a4f0f --- /dev/null +++ b/presentation/src/main/res/layout/dialog_single_button.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_onboarding_measure.xml b/presentation/src/main/res/layout/fragment_onboarding_measure.xml index 5e8bda1..dba5be2 100644 --- a/presentation/src/main/res/layout/fragment_onboarding_measure.xml +++ b/presentation/src/main/res/layout/fragment_onboarding_measure.xml @@ -84,7 +84,7 @@ android:background="@drawable/shape_purple10_fill_12_rect" android:gravity="center" android:paddingVertical="15dp" - android:text="@string/do_later" + android:text="@string/ing" android:textColor="@color/purple_50" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/presentation/src/main/res/layout/fragment_onboarding_start.xml b/presentation/src/main/res/layout/fragment_onboarding_start.xml index c76e6b2..c6ef539 100644 --- a/presentation/src/main/res/layout/fragment_onboarding_start.xml +++ b/presentation/src/main/res/layout/fragment_onboarding_start.xml @@ -40,8 +40,7 @@ android:src="@drawable/ic_logo_illustration" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/tv_onboarding_start_title" - /> + app:layout_constraintTop_toBottomOf="@id/tv_onboarding_start_title" /> - - diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 7ee673a..a4a7cdb 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -11,9 +11,10 @@ Unknown View Type : %d 시작하기 - 나중에 할래요 + 측정 중... 이전페이지 다음페이지 + 확인 리듬 기록