🫀
WearOSで心拍数を取る
はじめに
WearOSで取ることができる心拍数を取得していく
コード本体
長いので折りたたみ
MainActivity.kt
MainActivity.kt
package com.nenfuat.getheartrateapplication.presentation
import android.Manifest
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.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.core.content.ContextCompat
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.TimeText
import com.nenfuat.getheartrateapplication.presentation.theme.GetHeartRateApplicationTheme
class MainActivity : ComponentActivity(), SensorEventListener {
// センサマネージャ
private lateinit var sensorManager: SensorManager
private var HeartRateSensor: Sensor? = null
// 心拍数表示用
var heartRateData: MutableState<String> = mutableStateOf("データがありません")
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// パーミッションが許可された場合、センサーの登録を行う
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
} else {
// パーミッションが拒否された場合
heartRateData.value="権限がありません"
Log.e("MainActivity", "パーミッションが拒否されました。")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setTheme(android.R.style.Theme_DeviceDefault)
setContent {
WearApp(heartRateData)
}
//画面が勝手に切れないように
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// センサの初期設定
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
HeartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE)
// パーミッションの確認とリクエスト
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.BODY_SENSORS
) == PackageManager.PERMISSION_GRANTED
) {
// パーミッションが既に許可されている場合、センサーの登録
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
} else {
// パーミッションをリクエスト
requestPermissionLauncher.launch(Manifest.permission.BODY_SENSORS)
}
}
// センサの値が変更されたときに呼ばれる
override fun onSensorChanged(event: SensorEvent?) {
if (event != null) {
if (event.sensor.type == Sensor.TYPE_HEART_RATE) {
heartRateData.value = "${event.values[0]}"
}
}
}
// センサの精度が変更されたときに呼ばれる(今回は何もしない)
override fun onAccuracyChanged(event: Sensor?, p1: Int) {}
override fun onResume() {
super.onResume()
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
}
@Composable
fun WearApp(heartRateData: MutableState<String>) {
GetHeartRateApplicationTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
TimeText()
Greeting(heartRateData = heartRateData)
}
}
}
@Composable
fun Greeting(heartRateData: MutableState<String>) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
color = MaterialTheme.colors.primary,
text = heartRateData.value
)
}
解説
簡単に説明していく
権限
心拍数などの身体的情報を取得するセンサにアクセスするときはBODY_SENSORS
の権限が必要
AndroidManifest.xml
に書き加える
AndroidManifest.xml
<!--心拍数を取る権限-->
<uses-permission android:name="android.permission.BODY_SENSORS" />
設定から許可してもいいがアプリ内から権限求めるとかっこいい
MainActivity.kt
class MainActivity : ComponentActivity(), SensorEventListener {
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// パーミッションが許可された場合、センサーの登録を行う
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
} else {
// パーミッションが拒否された場合
Log.e("MainActivity", "パーミッションが拒否されました。")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
// パーミッションの確認とリクエスト
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.BODY_SENSORS
) == PackageManager.PERMISSION_GRANTED
) {
// パーミッションが既に許可されている場合、センサーの登録
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
} else {
// パーミッションをリクエスト
requestPermissionLauncher.launch(Manifest.permission.BODY_SENSORS)
}
}
}
起動すると権限がない場合こんな感じで権限を求められる
センサの取得
Androidの標準APIで提供されているSensorManager
を使って取得する
MainActivity.kt
class MainActivity : ComponentActivity(), SensorEventListener {
// センサマネージャ
private lateinit var sensorManager: SensorManager
private var HeartRateSensor: Sensor? = null
// 心拍数表示用
var heartRateData: MutableState<String> = mutableStateOf("データがありません")
override fun onCreate(savedInstanceState: Bundle?) {
// センサの初期設定
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
HeartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE)
}
// センサの値が変更されたときに呼ばれる
override fun onSensorChanged(event: SensorEvent?) {
if (event != null) {
if (event.sensor.type == Sensor.TYPE_HEART_RATE) {
heartRateData.value = "${event.values[0]}"
}
}
}
// センサの精度が変更されたときに呼ばれる(今回は何もしない)
override fun onAccuracyChanged(event: Sensor?, p1: Int) {}
override fun onResume() {
super.onResume()
HeartRateSensor?.also { heartRateSensor ->
sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
今回は心拍数だがSensor.TYPE_HEART_RATE
をSensor.TYPE_LINEAR_ACCELERATION
にすると線形加速度など他のセンサの値も取得することができる
表示
MainActivity.kt
@Composable
fun WearApp(heartRateData: MutableState<String>) {
GetHeartRateApplicationTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
TimeText()
Greeting(heartRateData = heartRateData)
}
}
}
@Composable
fun Greeting(heartRateData: MutableState<String>) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
color = MaterialTheme.colors.primary,
text = heartRateData.value
)
}
プロジェクト作成時のHello~をセンサデータに置き換えただけMutableState<String>
にセンサデータを格納しているためセンサデータが変わるごとにUIが更新される
終わりに
WearOSを用いて心拍数を取得した
Androidでセンサの値を取る時と大きな違いはないがBODY_SENSORS
の権限が必要なことだけ注意が必要
おまけ
AndroidStudioのエミュレータでも一部のセンサを試すことができる
次の画面に飛ぶ
Virtual sensors
タブへ移動
ここでセンサの値をいじれる
アプリ側で反映されている心拍数500て死ぬやろ
Discussion