[Android Compose] SensorManager를 사용하여 걸음 수 측정하기

: ) YOUNG·2024년 7월 17일
1

안드로이드

목록 보기
27/30
post-thumbnail
post-custom-banner

Compose 걸음 수 측정하기

Compose SensorManager




개요

원래 걸음 수 정보를 가져오기 위해서는 몇 가지 방법이 있지만, 그 중 SensorManager를 통해서 걸음 수 정보를 가져오려고 합니다.
사실 정확한 방법은 아니지만, 공식 페이지에서도 다루기 때문에 나름 괜찮다고 생각합니다.


걸음 수 정보를 구하려고 많은 자료를 찾아봤는데, Activity에서 구하거나, Compose가 아니거나 등등... 마음에 들지 않는 방식이 많더군요. 저 같은 경우는 Activity가 아닌 다른 Screen에서 해당 정보를 받아와야 했기 때문에 고생을 쪼금 했습니다.
그래서 다른 분들은 그런 과정이 없으셨으면 하여 한번 포스팅해 보려고 합니다.


sensorManager에서 나오는 걸음 수는 휴대폰이 부팅된 이후 누적된 걸음 수 정보가 나오게 됩니다.

저 같은 경우는 처음에 0이 나와야 한다고 생각했는데 8000단위가 나와서 당연히 걸음 수라고는 생각을 못 했습니다.
알고 보니 부팅 이후 누적 걸음 수여서 그랬던 것...

아무튼 그래서 앱이 켜지고 걸음 수를 구하기 위해서는 누적 걸음 수 - 현재 업데이트되는 걸음 수를 통해서 구해주면 됩니다.




전개



먼저 XML에 신체 활동 정보를 가져오기 위해서 permission을 추가해 줍니다.

Manifest.XML


 <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> 

⭐⭐
퍼미션을 가져오는 코드까지 정리하면 내용이 너무 길어질 것 같아서 따로 추가하지 않겠습니다.!
설정에서 따로 권한을 추가하여 주셔야 정상적으로 걸음 수 정보가 제대로 출력됩니다!
⭐⭐



@Composable SensorManager Function


@Composable
fun StepCounterSensorManager(
    walkingRecordViewModel: WalkingRecordViewModel
) {
    val context = LocalContext.current
    val sensorManager = remember {
        context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    var stepCount by remember { mutableFloatStateOf(0f) }

    val stepCounterSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
    val sensorEventListener = remember {
        object : SensorEventListener {
            override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
                // 정확도 변경 처리
            }

            override fun onSensorChanged(event: SensorEvent) {
                // 걸음 수 업데이트 처리
                stepCount = event.values[0]
                walkingRecordViewModel.setStepCount(stepCount.toInt()) // 현재 걸음 수 ViewModel에 저장
            }
        }
    }

    DisposableEffect(Unit) {
        stepCounterSensor?.let {
            sensorManager.registerListener(
                sensorEventListener, it, SensorManager.SENSOR_DELAY_FASTEST
            )
        }

        onDispose {
            sensorManager.unregisterListener(sensorEventListener)
        }
    }
} // End of rememberStepCounterSensorState()

위에서 말씀드린대로 앱이 켜진 후 부터 걸음 수를 구하기 위해서는 SensorManager에서 주는 걸음 수가 부팅 이후 누적 된 걸음 수 정보이기 때문에 누적 걸음 수 - 현재 업데이트되는 걸음 수 로 계산을 해주어야 합니다.

따라서 누적 걸음 수 정보는 한번만 호출해서 데이터를 가져와야 하고, 현재 걸음 수지속적으로 가져와야 하므로

현재 걸음 수override fun onSensorChanged(event: SensorEvent) { 안에 넣어주었습니다.

누적 걸음 수는 Screen이 호출되는 과정에서 LaunchedEffect()에서 호출하여 한번만 저장되도록 하였습니다.

        LaunchedEffect(Unit) {
            walkingRecordViewModel.setTodayStepCount(walkingRecordViewModel.moveStepCount.value)
        }


ViewModel

@HiltViewModel
class WalkingRecordViewModel @Inject constructor() : ViewModel() {

    private val _stepCount = MutableStateFlow<Int>(0) // 현재 걸음 수
    val moveStepCount = _stepCount.asStateFlow()

    private val _todayStepCount = MutableStateFlow<Int>(0) // 누적 걸음 수
    val todayStepCount = _todayStepCount.asStateFlow()

    fun setTodayStepCount(newStepCount: Int) {
        _todayStepCount.value = newStepCount
    } // End of setStepCount()

    fun setStepCount(newStepCount: Int) {
        _stepCount.value = newStepCount
    } // End of setStepCount()
} // End of WalkingRecordViewModel()




EventListener를 통해서 걸음 수 증가가 동작하기 때문에 부분적으로 Composition의 업데이트를 위해서는 Component 단위로 분리가 필요하여 걸음 수가 보이는 부분을 따로 만들었습니다.

@Composable WalkRecordingBox 컴포넌트


@Composable
fun WalkRecordingBox(
	walkingRecordViewModel: WalkingRecordViewModel = hiltViewModel()
) {

	val todayStepCount = walkingRecordViewModel.todayStepCount.value // 누적 걸음 수
    val moveStepCount = walkingRecordViewModel.moveStepCount.collectAsStateWithLifecycle() // 현재 걸음 수

    Box(
        Modifier.clip(RoundedCornerShape(8.dp)).fillMaxWidth().height(92.dp)
            .background(color = BrightGray), contentAlignment = Alignment.Center
    ) {
        Row(
            modifier = Modifier.matchParentSize(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
        	Text(
            	textAlign = TextAlign.Center,
                text = "${moveStepCount.value - todayStepCount.value}",
                fontSize = 16.sp,
                fontWeight = FontWeight.Bold,
                )
        }
    }
} // End of WalkRecordingBox


Screen에 붙이기


@Composable
fun WalkScreen(
    walkingRecordViewModel: WalkingRecordViewModel = hiltViewModel()
) {

    ...

    StepCounterSensorManager(walkingRecordViewModel) // SensorManager
    
    ...
    
	LaunchedEffect(Unit) {
    	walkingRecordViewModel.setTodayStepCount(walkingRecordViewModel.moveStepCount.value)
	}
    
    ...
    
    if(데이터 !=null) {
        screenContent()
    }
    
	...

이렇게 스크린에 붙여주시면 됩니다.

그리고 컴포넌트는 Screen에 그대로 넣지는 않았습니다. 저 같은 경우는 여기서는 다루지 않았지만 다른 데이터들이 들어올 때, 화면이 업데이트 되는 방식으로 만들어져 있습니다.

사실 뭐 이유가 있다기보다 구현을 하다보니 그런 방식으로 만들어진건데,
StepCounterSensorManager를 처음에 호출하여 데이터가 들어온 뒤 UI를 표시하기 위해서 그렇게 구현하였습니다.



@Composable ScreenContent().kt


...

                Column(
                    modifier = Modifier.fillMaxWidth().padding(top = defaultPadding),
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.Center
                ) {
                    Column(
                        Modifier.fillMaxWidth().padding(
                            start = defaultPadding, end = defaultPadding, bottom = 36.dp
                        ),
                        horizontalAlignment = Alignment.CenterHorizontally,
                    ) {
						WalkRecordingBox(context, moveDist, moveStepCount, todayStepCount) // 현재 산책 기록 데이터
                    }
                }
                
...






결과




이해가 안 되거나 궁금하신 게 있으신 분들은 댓글을 남겨주세요!
좋아요는 큰 힘이 됩니다!




post-custom-banner

0개의 댓글