Kotlin&LiveDataで端末の向きを取得する処理を書いてみた

Androidでデバイスの向きを取得するOrientation SensorはAndroid 2.2で非推奨となっており、現在は代わりに加速度センサーと磁気センサーを使って向きを計算する方法が推奨されています。
これを実装するコードは[ドキュメントに書かれている](https://developer.android.com/guide/topics/sensors/sensors_position)のですが、Kotlinでやりたかったのと[LiveData](https://developer.android.com/topic/libraries/architecture/livedata)を使ってより簡潔に実装したかったので、やってみました。

できあがったコードがこちらになります。

```kotlin
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`を利用する側のコードは以下のようになります。

```kotlin
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
            // で処理を行う
        })
    }

}
```

かんたん！！！