움직임 감지 센서
환경 센서
위치 센서
SensorManager
val sensorManager =
getSystemService(LocalContext.current, SensorManager::class.java) as SensorManager
Sensor
val sensor : Sensor? = sensorManager.getDefaultSensor(Sensor.SOME_SENSOR)
SensorEvent
SensorEvent 인스턴스를 생성SensorEventListener
SensorEvent 를 수신하는 callback 메서드를 포함하는 인터페이스class SensorActivity: Activity(), SensorEventListener() {
// ...
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
// Do something here if sensor accuracy changes.
}
override fun onSensorChanged(event: SensorEvent) {
// The light sensor returns a single value.
// Many sensors return 3 values, one for each axis.
val lux = event.values[0]
// Do something with this sensor value.
}
// ...
}
기기마다, Android 버전마다 존재하는 센서의 종류가 다를 수 있습니다.
해당 기기에 어떤 Sensor 가 존재하는 지 확인하기 위해서는 시스템 서비스에 접근해서 SensorManager 인스턴스를 만들어야 합니다.
private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
그 다음으로 모든 센서의 종류를 가져오려면 다음과 같이 작성하면 됩니다.
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
만약 특정 유형의 센서를 가져오려면 [센서 개요 링크](https://developer.android.com/develop/sensors-and-location/sensors/sensors_overview?hl=ko) 를 참고해 어떤 Sensor Type 이 있는 지 확인하고, getDefaultSensor(Sensor.SOME_SENSOR) 를 호출하면 됩니다.
private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor : Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
if (sensor != null) {
// Success! There's a magnetometer.
} else {
// Failure! No magnetometer.
}
Sensor 데이터를 모니터링하려면 SensorEventListener 인터페이스를 통해 오버라이딩되는 두 개의 callback 메서드를 구현하면 됩니다.
onAccuracyChanged()Sensor 객체의 정확도가 변경되었을 때 호출되는 메소드.
// SensorManager.class
public static final int SENSOR_STATUS_ACCURACY_HIGH = 3;
public static final int SENSOR_STATUS_ACCURACY_LOW = 1;
public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2;
public static final int SENSOR_STATUS_NO_CONTACT = -1;
public static final int SENSOR_STATUS_UNRELIABLE = 0;
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
// Do something here if sensor accuracy changes.
}
onSensorChanged()SensorEvent 가 발생했을 때 호출되는 메소드.
// SensorEvent.class
public int accuracy;
public boolean firstEventAfterDiscontinuity;
public Sensor sensor;
public long timestamp;
public final float[] values = null;
override fun onSensorChanged(event: SensorEvent) {
// The light sensor returns a single value.
// Many sensors return 3 values, one for each axis.
val lux = event.values[0]
// Do something with this sensor value.
}
센서, 방위각 선언
val context = LocalContext.current
val sensorManager =
remember { context.getSystemService(Context.SENSOR_SERVICE) as SensorManager }
val accelerometer = // 가속도계 센서
remember { sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) }
val magnetometer = // 자기장 센서
remember { sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) }
var azimuthDeg by remember { mutableFloatStateOf(0f) } // 방위각 상태 값 (0~360)
센서 측정값을 담을 배열 선언
val accelerometerReading = FloatArray(3)
val magnetometerReading = FloatArray(3)
val rotationMatrix = FloatArray(9)
val orientationAngles = FloatArray(3)
accelerometerReading : 가속도계 센서의 측정값을 저장할 배열magentometerReading : 자기장 센서의 측정값을 저장할 배열rotationMatrix : 가속도 배열, 자기장 배열 값을 통해orientationAngles : x,y,-z 축을 중심으로 한 회전 각도가 저장되는 배열센서 리스너 등록, 정의
when (event.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
// 가속도계 측정값 복사
accelerometerReading[0] = event.values[0]
accelerometerReading[1] = event.values[1]
accelerometerReading[2] = event.values[2]
}
Sensor.TYPE_MAGNETIC_FIELD -> {
// 자기장 센서 측정값 복사
magnetometerReading[0] = event.values[0]
magnetometerReading[1] = event.values[1]
magnetometerReading[2] = event.values[2]
}
}// 두 센서 값으로부터 회전 행렬과 방위각 계산
val success = SensorManager.getRotationMatrix(
rotationMatrix,
null,
accelerometerReading,
magnetometerReading
)if (success) {
SensorManager.getOrientation(rotationMatrix, orientationAngles)
// orientationAngles[0] 이 방위각(rad)이며, [1] Pitch, [2] Roll입니다.
val azimuthRad = orientationAngles[0]
var azimuthDegrees = Math.toDegrees(azimuthRad.toDouble()).toFloat()
if (azimuthDegrees < 0) {
azimuthDegrees += 360f // 음수 값을 0~360도로 변환
}
azimuthDeg = azimuthDegrees // 상태 업데이트
}// 센서 리스너 등록 (실행 속도: NORMAL)
sensorManager.registerListener(
sensorListener,
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL
)
sensorManager.registerListener(
sensorListener,
magnetometer,
SensorManager.SENSOR_DELAY_NORMAL
)
// 컴포저블이 사라질 때 센서 해제
onDispose {
sensorManager.unregisterListener(sensorListener)
}방위각을 통해 방향을 화살표로 표시
// 현재 기기 방위각을 센서로부터 얻음 (항상 0~360도 값 유지)
val azimuth = rememberAzimuth()
// 목표 방향 대비 기기 방향의 차이각 계산
val rotationAngle = (bearingToTarget - azimuth + 360f) % 360f
Image(
imageVector = Icons.Filled.KeyboardArrowUp, // 화살표 아이콘 (기본이 위쪽을 가리키는 그림)
contentDescription = "목표 방향 화살표",
modifier = modifier
.size(100.dp)
.rotate(rotationAngle)
)
@Composable
fun rememberAzimuth(): Float {
val context = LocalContext.current
// SensorManager 및 센서 객체 가져오기
val sensorManager =
remember { context.getSystemService(Context.SENSOR_SERVICE) as SensorManager }
val accelerometer =
remember { sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) } // 가속도계 센서
val magnetometer =
remember { sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) } // 자기장 센서
var azimuthDeg by remember { mutableFloatStateOf(0f) } // 방위각 상태 값 (0~360)
DisposableEffect(Unit) {
// 센서 측정값을 담을 배열
val accelerometerReading = FloatArray(3)
val magnetometerReading = FloatArray(3)
val rotationMatrix = FloatArray(9)
val orientationAngles = FloatArray(3)
// 센서 이벤트 리스너 정의
val sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
when (event.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
// 가속도계 측정값 복사
accelerometerReading[0] = event.values[0]
accelerometerReading[1] = event.values[1]
accelerometerReading[2] = event.values[2]
}
Sensor.TYPE_MAGNETIC_FIELD -> {
// 자기장 센서 측정값 복사
magnetometerReading[0] = event.values[0]
magnetometerReading[1] = event.values[1]
magnetometerReading[2] = event.values[2]
}
}
// 두 센서 값으로부터 회전 행렬과 방위각 계산
val success = SensorManager.getRotationMatrix(
rotationMatrix,
null,
accelerometerReading,
magnetometerReading
)
if (success) {
SensorManager.getOrientation(rotationMatrix, orientationAngles)
// orientationAngles[0] 이 방위각(rad)이며, [1] Pitch, [2] Roll입니다.
val azimuthRad = orientationAngles[0]
var azimuthDegrees = Math.toDegrees(azimuthRad.toDouble()).toFloat()
if (azimuthDegrees < 0) {
azimuthDegrees += 360f // 음수 값을 0~360도로 변환
}
azimuthDeg = azimuthDegrees // 상태 업데이트
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { /* 생략 */
}
}
// 센서 리스너 등록 (실행 속도: NORMAL)
sensorManager.registerListener(
sensorListener,
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL
)
sensorManager.registerListener(
sensorListener,
magnetometer,
SensorManager.SENSOR_DELAY_NORMAL
)
// 컴포저블이 사라질 때 센서 해제
onDispose {
sensorManager.unregisterListener(sensorListener)
}
}
return azimuthDeg // 현재 방위각 값을 반환 (수시로 업데이트됨)
}
@Composable
fun ArrowDirectionIndicator(
modifier: Modifier = Modifier,
bearingToTarget: Float
) {
// 현재 기기 방위각을 센서로부터 얻음 (항상 0~360도 값 유지)
val azimuth = rememberAzimuth()
// 목표 방향 대비 기기 방향의 차이각 계산
val rotationAngle = (bearingToTarget - azimuth + 360f) % 360f
Image(
imageVector = Icons.Filled.KeyboardArrowUp, // 화살표 아이콘 (기본이 위쪽을 가리키는 그림)
contentDescription = "목표 방향 화살표",
modifier = modifier
.size(100.dp)
.rotate(rotationAngle)
)
}