Androidでデバイスの向きを取得するOrientation SensorはAndroid 2.2で非推奨となっており、現在は代わりに加速度センサーと磁気センサーを使って向きを計算する方法が推奨されています。
これを実装するコードはドキュメントに書かれているのですが、KotlinでやりたかったのとLiveDataを使ってより簡潔に実装したかったので、やってみました。
できあがったコードがこちらになります。
import androidx.lifecycle.LiveData
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
// 向き情報を格納するクラス
data class Orientation(
val azimuth: Float,
val pitch: Float,
val roll: Float
)
class OrientationLiveData(
context: Context,
private val sensorDelay: Int = SensorManager.SENSOR_DELAY_UI)
: LiveData<Orientation>(), SensorEventListener {
private val mSensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val accelerometer: Sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
private val magneticField: Sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
private val mAccelerometerReading = FloatArray(3)
private val mMagnetometerReading = FloatArray(3)
private val mRotationMatrix = FloatArray(9)
private val mOrientationAngles = FloatArray(3)
override fun onActive() {
super.onActive()
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, sensorDelay)
mSensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, sensorDelay)
}
override fun onInactive() {
super.onInactive()
mSensorManager.unregisterListener(this)
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor == accelerometer) {
System.arraycopy(event.values, 0, mAccelerometerReading, 0, mAccelerometerReading.size)
} else if (event.sensor == magneticField) {
System.arraycopy(event.values, 0, mMagnetometerReading, 0, mMagnetometerReading.size)
}
updateOrientationAngles()
value = Orientation(mOrientationAngles[0], mOrientationAngles[1], mOrientationAngles[2])
}
private fun updateOrientationAngles() {
SensorManager.getRotationMatrix(mRotationMatrix, null, mAccelerometerReading, mMagnetometerReading)
SensorManager.getOrientation(mRotationMatrix, mOrientationAngles)
}
}
ほぼ元のドキュメントのコードをそのまま使っていますが、ActivityのライフサイクルコールバックをLiveData用に置き換えています。
このOrientationLiveData
を利用する側のコードは以下のようになります。
import androidx.lifecycle.Observer
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
OrientationLiveData(this).observe(this, Observer { orientation ->
if (orientation == null) return@Observer
// orientation.azimuth
// orientation.pitch
// orientation.roll
// で処理を行う
})
}
}
かんたん!!!