Wear OS 심박수 센서 사용
Wear OS(갤럭시 워치4) 심박수 센서사용에 대한 내용이 별로 없어 기록용으로 작성한다.
따라서 부정확한 내용이 존재 할 수 있다.
심박수 센서와 타이머 기능을 가진 어플을 만들 것이다.
권한 설정
우선 심박수 센서를 사용하기 위해선 권한을 받아야한다.
필요한 권한은 다음과 같다.
Manifest.permission.BODY_SENSORS
아래와 같이 입력해 권한을 체크 하도록 한다.
if (checkSelfPermission(Manifest.permission.BODY_SENSORS) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.BODY_SENSORS}, 1);
} else {
Log.d("11", "ALREADY GRANTED");
}
센서를 사용하기 위해 SensorManager를 사용한다.
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
assert mSensorManager != null;
mHeartRateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
mSensorManager.registerListener(mheart, mHeartRateSensor, SensorManager.SENSOR_DELAY_FASTEST);
다음과 같이SensorManager를 사용해 센서 정보를 받아온다.
mHeartRateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
이 부분에 Sensor.TYPE_HEART_RATE 에 HEART_BEAT를 넣으면 워치의 심박수 센서가 켜지자 마자 꺼지기 때문에 꼭 TYPE_HEART_RATE를 넣어야한다.
mSensorManager.registerListener(mheart, mHeartRateSensor, SensorManager.SENSOR_DELAY_FASTEST);
마지막 부분이 센서를 등록하는 부분으로 이부분이 센서를 동작시키는 코드이다. 여기서 SensorEventListener와 SenserManager에서 받아온 Sensor.TYPE_HEART_RATE의 정보, 마지막으로 센서 속도를 지정 할 수 있다.
센서 속도에 대한 부분은 다음 블로그를 참고하자
안드로이드 SensorManager의 딜레이 옵션 : 네이버 블로그 (naver.com)
안드로이드 SensorManager의 딜레이 옵션
안드로이드에서 센서를 잡아올때 SensorManager 라는 것을 사용한다. 그리고 SensorManag...
blog.naver.com
간단하게 SENSOR_DELAY_FASTEST 가 가장 빠르다.
public class HeartListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if(sensorEvent.sensor.getType()==Sensor.TYPE_HEART_RATE && sensorEvent.values.length>0 ) {
float mHeartRateFloat = sensorEvent.values[0];
int mHeartRate = Math.round(mHeartRateFloat);
beat.setText(Integer.toString(mHeartRate));
if(mHeartRate >= 195 && flag ==1){
ll.setBackgroundResource(R.drawable.green);
}else if(mHeartRate>0 && flag ==1){
ll.setBackgroundResource(R.drawable.red);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
SensorEventListener를 구현한 클래스를 만들었는데 여기서 onSensorChanged가 센서의 값이 변경되면 작동되는 코드로 센서 동작시 실행 시킬 코드를 여기에 모두 넣으면 된다.
전체 코드는 다음과 같다.
package com.kb.myapplication;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import com.kb.myapplication.databinding.ActivityMainBinding;
import androidx.health.services.client.data.HeartRateAccuracy;
import androidx.wear.widget.BoxInsetLayout;
import org.w3c.dom.Text;
public class MainActivity extends Activity {
HeartRateAccuracy heartRateAccuracy;
private Chronometer chronometer;
private boolean running;
private long pauseOffset;
private ActivityMainBinding binding;
private Thread timeThread = null;
private Boolean isRunning = true;
private SensorManager mSensorManager;
private Sensor mHeartRateSensor;
private HeartListener mheart = new HeartListener();
TextView hh;
TextView beat;
BoxInsetLayout ll;
int flag = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Button start = (Button) findViewById(R.id.start);
Button stop = (Button) findViewById(R.id.stop);
Button reset = (Button) findViewById(R.id.reset);
beat = (TextView) findViewById(R.id.beat);
hh = (TextView) findViewById(R.id.mtext);
ll=(BoxInsetLayout) findViewById(R.id.LL);
if (checkSelfPermission(Manifest.permission.BODY_SENSORS) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.BODY_SENSORS}, 1);
} else {
Log.d("11", "ALREADY GRANTED");
}
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
assert mSensorManager != null;
mHeartRateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
mSensorManager.registerListener(mheart, mHeartRateSensor, SensorManager.SENSOR_DELAY_FASTEST);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE) != null) {
Log.d("123123", "TYPE_HEART_RATE supports");
} else {
Log.d("123123", "no TYPE_HEART_RATE supports");
}
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.setVisibility(View.GONE);
stop.setVisibility(View.VISIBLE);
timeThread = new Thread(new timeThread());
timeThread.start();
flag = 1;
if (isRunning) {
stop.setText("정지");
} else {
stop.setText("시작");
}
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
isRunning = !isRunning;
if (isRunning) {
stop.setText("정지");
} else {
stop.setText("시작");
}
}
});
reset.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
timeThread.interrupt();
isRunning = true;
hh.setText("00:00:00:00");
ll.setBackgroundResource(R.drawable.dark);
start.setVisibility(View.VISIBLE);
stop.setVisibility(View.GONE);
flag = 0;
}
});
}
@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int mSec = msg.arg1 % 100;
int sec = (msg.arg1 / 100) % 60;
int min = (msg.arg1 / 100) / 60;
int hour = (msg.arg1 / 100) / 360;
//1000이 1초 1000*60 은 1분 1000*60*10은 10분 1000*60*60은 한시간
@SuppressLint("DefaultLocale") String result = String.format("%02d:%02d:%02d:%02d", hour, min, sec, mSec);
hh.setText(result);
}
};
public class timeThread implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
while (isRunning) { //일시정지를 누르면 멈춤
Message msg = new Message();
msg.arg1 = i++;
handler.sendMessage(msg);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
hh.setText("");
hh.setText("00:00:00:00");
}
});
return; // 인터럽트 받을 경우 return
}
}
}
}
}
public class HeartListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if(sensorEvent.sensor.getType()==Sensor.TYPE_HEART_RATE && sensorEvent.values.length>0 ) {
float mHeartRateFloat = sensorEvent.values[0];
int mHeartRate = Math.round(mHeartRateFloat);
beat.setText(Integer.toString(mHeartRate));
if(mHeartRate >= 195 && flag ==1){
ll.setBackgroundResource(R.drawable.green);
}else if(mHeartRate>0 && flag ==1){
ll.setBackgroundResource(R.drawable.red);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mSensorManager.unregisterListener(mheart);
}
}