Hiltを使って位置情報を取得する

位置情報の取得で、LocationManagerが将来的に廃止されるため、FusedLocationProviderClientに切り替えたました。
その際、HiltとLiveDataを使用してどこでも位置情報の変更を受信出来るようにしたので、それを説明したいと思います。

### 条件

* permissionに位置情報を追加
* permissionの許可を取得済み
* Hiltを導入済み

### ModuleにFusedLocationProviderClientを定義

```kotlin
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideFusedLocationProviderClient(@ApplicationContext context: Context): FusedLocationProviderClient {
        return LocationServices.getFusedLocationProviderClient(context)
    }
}
```

### Repositoryを作成する

FusedLocationProviderClientをInjectする。
LocationCallbackで位置情報に変化があった場合、Listenerに通知します。

```kotlin
@Singleton
class LocationRepository @Inject constructor(
    @ApplicationContext private val context: Context,
    private val fusedLocationProviderClient: FusedLocationProviderClient
) {

    interface OnLocationListener {

        fun onLocationChanged(location: Location)
        fun onProviderChanged(isEnable: Boolean)
    }

    private var onLocationListener: OnLocationListener? = null

    private val locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            onLocationListener?.onLocationChanged(locationResult.lastLocation)
        }
    }

    fun start(onListener: OnLocationListener) {
        onLocationListener = onListener

        if (ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {

            return
        }

        val locationRequest = LocationRequest.create()
        locationRequest.apply {
            interval = TimeUnit.SECONDS.toMillis(5)
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
        fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
    }

    fun stop() {
        fusedLocationProviderClient.removeLocationUpdates(locationCallback)
        onLocationListener = null
    }
}
```

### LiveDataを作成する

LocationRepositoryをInjectします。
LiveDataを作成するときにLocationRepositoryにListenerを渡します。
Listenerに変更があった場合、LiveDataのvalueに入れて通知します。

```kotlin
class LocationLiveData @Inject constructor(
    private val locationRepository: LocationRepository
) : LiveData<Location>() {

    private val onLocationListener = object : LocationRepository.OnLocationListener {
        override fun onLocationChanged(location: Location) {
            value = location
        }

        override fun onProviderChanged(isEnable: Boolean) {
        }
    }

    override fun onInactive() {
        super.onInactive()

        stop()
    }

    fun start() {
        locationRepository.start(onLocationListener)
    }

    fun stop() {
        locationRepository.stop()
    }
}
```

### LocationLiveDataをモジュールに登録する

```kotlin
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideFusedLocationProviderClient(@ApplicationContext context: Context): FusedLocationProviderClient {
        return LocationServices.getFusedLocationProviderClient(context)
    }

    @Provides
    @Singleton
    fun provideLocationLiveData(locationRepository: LocationRepository): LocationLiveData {
        return LocationLiveData(locationRepository)
    }
}
```

* LocationLiveDataを使用する

ViewModelでLocationLiveDataをInjectすることで、位置情報が変わったらActivity/Fragmentで取得することが可能です。

```kotlin
class MainViewModel @Inject constructor(
    val locationLiveData: LocationLiveData
) {

  fun start() {
    locationLiveData.start()
  }
}
```

```kotlin
  viewModel.locationLiveData.observe(this) {
    // it = Location
  }
```

---

Hiltに関しては、自分の過去記事にありますので、よろしければそちらを参照してください。
LiveDataを使うことで、同じような位置情報を取得するコードを書かなくて済むようになりますので、よければ参考にしてください。