DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Sensor API Complete Guide — Accelerometer/Gyroscope/Step Counter/Compose Integration

What You'll Learn

センサーAPI(Accelerometer、Gyroscope、Step Counter、Proximity Sensor、Compose連携)を解説します。


Convert Sensor to Flow

fun sensorFlow(
    context: Context,
    sensorType: Int,
    samplingPeriod: Int = SensorManager.SENSOR_DELAY_UI
): Flow<FloatArray> = callbackFlow {
    val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    val sensor = sensorManager.getDefaultSensor(sensorType) ?: run {
        close(IllegalStateException("Sensor not available"))
        return@callbackFlow
    }

    val listener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            trySend(event.values.copyOf())
        }
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
    }

    sensorManager.registerListener(listener, sensor, samplingPeriod)
    awaitClose { sensorManager.unregisterListener(listener) }
}
Enter fullscreen mode Exit fullscreen mode

Accelerometer

@Composable
fun AccelerometerScreen() {
    val context = LocalContext.current
    var acceleration by remember { mutableStateOf(floatArrayOf(0f, 0f, 0f)) }

    LaunchedEffect(Unit) {
        sensorFlow(context, Sensor.TYPE_ACCELEROMETER)
            .collect { values -> acceleration = values }
    }

    Column(Modifier.fillMaxSize().padding(16.dp)) {
        Text("Accelerometer", style = MaterialTheme.typography.headlineMedium)
        Spacer(Modifier.height(16.dp))
        Text("X: ${"%.2f".format(acceleration[0])} m/s²")
        Text("Y: ${"%.2f".format(acceleration[1])} m/s²")
        Text("Z: ${"%.2f".format(acceleration[2])} m/s²")

        Spacer(Modifier.height(24.dp))
        Canvas(Modifier.size(200.dp)) {
            val centerX = size.width / 2
            val centerY = size.height / 2
            val ballX = centerX + acceleration[0] * -10
            val ballY = centerY + acceleration[1] * 10

            drawCircle(Color.LightGray, radius = size.minDimension / 2)
            drawCircle(Color.Red, radius = 20f, center = Offset(ballX, ballY))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step Counter

@Composable
fun StepCounterScreen() {
    val context = LocalContext.current
    var steps by remember { mutableIntStateOf(0) }

    LaunchedEffect(Unit) {
        sensorFlow(context, Sensor.TYPE_STEP_COUNTER)
            .collect { values -> steps = values[0].toInt() }
    }

    Column(
        Modifier.fillMaxSize().padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("今日のSteps", style = MaterialTheme.typography.headlineMedium)
        Text("$steps", style = MaterialTheme.typography.displayLarge)
        Text("歩", style = MaterialTheme.typography.headlineSmall)

        Spacer(Modifier.height(24.dp))
        LinearProgressIndicator(
            progress = { minOf(steps / 10000f, 1f) },
            modifier = Modifier.fillMaxWidth()
        )
        Text("目標: 10,000歩", style = MaterialTheme.typography.bodySmall)
    }
}
Enter fullscreen mode Exit fullscreen mode

Shake Detection

@Composable
fun ShakeDetector(onShake: () -> Unit) {
    val context = LocalContext.current
    var lastShakeTime by remember { mutableLongStateOf(0L) }

    LaunchedEffect(Unit) {
        sensorFlow(context, Sensor.TYPE_ACCELEROMETER)
            .collect { values ->
                val magnitude = sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2])
                if (magnitude > 15f) {
                    val now = System.currentTimeMillis()
                    if (now - lastShakeTime > 1000) {
                        lastShakeTime = now
                        onShake()
                    }
                }
            }
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

センサー TYPE
Acceleration TYPE_ACCELEROMETER
Gyroscope TYPE_GYROSCOPE
Steps TYPE_STEP_COUNTER
Proximity TYPE_PROXIMITY
Light TYPE_LIGHT
  • callbackFlowでセンサーをFlow化
  • SensorManagerでセンサー登録/解除
  • Shake DetectionはAccelerationの大きさで判定
  • awaitCloseで確実にListener Cleanup

8種類のAndroidAppTemplates(センサーIntegration Support)を公開しています。

Template ListGumroad

Related Articles:


Ready-Made Android App Templates

8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.

Browse templatesGumroad

Top comments (0)