【Kotlin】GPSで位置情報GNSSで衛星情報を取得

#はじめに
GPSで緯度と経度をGNSSで受信した衛星の情報を取得します。
API Level 24 (Android 7.0)以降が対象です。

#準備
build.gradle(app)に下記の1行を追加します。

```build.gradle
implementation 'com.google.android.gms:play-services-location:15.0.1'
```

マニフェストファイルには以下のパーミッションを追加します。

```Manifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
```

#ユーザーに許可をもらう
GPSやGNSSの情報を取得するにはユーザーに取得する旨を通知して許可をもらう必要があります。
ユーザーへの許可は

```permissions.kt
val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
ActivityCompat.requestPermissions(this,permissions,1000)
```

という処理を行うと許可をもらう画面になります。
ただしこのとき、処理が止まっているわけではありません。
許可された場合（されない場合も）はMainActivityの**override fun onRequestPermissionsResult**リスナーに処理が渡されるので、これを元に適切に処理します。
なお**1000**という数値はonRequestPermissionsResult内で他の処理と区別するために使います。

```MainActivity.kt
    override fun onRequestPermissionsResult(requestCode : Int,permissions : Array<String>,grantResults: IntArray){
        if (requestCode == 1000 && grantResults[0] == PackageManager.PERMISSION_GRANTED){

        }
    }
```

さっきの**1000**がここで出てきます。
PERMISSION_GRANTEDは許可が取れたことを示しています。

#ソースの説明
MainActivityのソースは極力減らしています。
LocationManagerが相当古いSDKなのか**LocationManager::class.kotlinJvmJava**という見たことも無い変換で渡すことになりました。

位置情報が得られた場合は**onLocation**リスナーが、衛星情報が得られた場合は**onSatellit**リスナーが処理されます。
onLocationはlocationResult.lastLocation.latitudeに経度、locationResult.lastLocation.longitudeに緯度が入っています。

onSatellitはstatus.satelliteCountに得られた衛星の数が入ります。
status,get_xxxのメソッドの引数は衛星のインデックス 0..status.satelliteCount-1 までの数値を渡して情報を取得します。

|メソッド名|意味|
|:--:|:--:|
|getSvid（idx）|衛星の識別番号を取得します。ただしこちらは使われていない様子です。|
|getConstellationType(idx)|こちらが使われている方の識別番号 GPSの場合は1..32の値になります|
|getAzimuthDegrees(idx)|衛星の方位（角度）が0.0..359.9の値で取得出来ます|
|getElevationDegrees(idx)|衛星の高さを取得出来ます。単位は・・・何でしょ？|
|getCn0DbHz(idx)|衛星からの電波の強さを取得します。強さはC/N0比で単位はdBHz です|
|usedInFix|位置情報の取得に使われている衛星の場合trueが返されます|
|getCarrierFrequencyHz(idx)|おそらく使われていません|

#ソース

```gps.kt
class MainActivity : AppCompatActivity() {
    private val gpsService = GpsService(this,this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        gpsService.setListener(gpsListener)
        gpsService.permissioRequestCode = 1000
        gpsService.start()

    }

    private val gpsListener = object : GpsServiceInterface{
        override fun onLocation(locationResult: LocationResult) {
            Toast.makeText(this@MainActivity,
                    "緯度:${locationResult.lastLocation.latitude}, 経度:${locationResult.lastLocation.longitude}", Toast.LENGTH_LONG).show()
        }

        override fun onSatellit(status: GnssStatus) {
            for (i in 0..status.satelliteCount-1){
                Log.d("Sample", status.getSvid(i).toString())
            }
        }
    }

    override fun onRequestPermissionsResult(requestCode : Int,permissions : Array<String>,grantResults: IntArray){
        if (requestCode == gpsService.permissioRequestCode && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            if (gpsService.isGPSPermission()){
                gpsService.start()
            }
        }
    }
}
interface GpsServiceInterface : GpsService.Listener {
    fun onLocation(locationResult: LocationResult)       // 受信イベント
    fun onSatellit(status: GnssStatus)
}

class GpsService(private var context: Context,private var activity: MainActivity) {
    private var _latitude = 0.0
    private var _longitude = 0.0
    private lateinit var fusedLocation : FusedLocationProviderClient
    private lateinit var locationManager: LocationManager

    var permissioRequestCode : Int = 1000
    private lateinit var locationCallback: LocationCallback
    private lateinit var gnssCallback : GnssStatus.Callback

    private var listener: GpsServiceInterface? = null
    interface Listener {}

    private var enable : Boolean = false

    fun setListener(listener: GpsService.Listener?) {
        if (listener is GpsServiceInterface) {
            this.listener = listener as GpsServiceInterface
        }
    }
    fun start(){
        if (enable) return
        if (isGPSPermission() == false){
            val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
            ActivityCompat.requestPermissions(activity,permissions,permissioRequestCode)
            return
        }
        locationManager = getSystemService<LocationManager>(context, LocationManager::class.kotlinJvmJava) as LocationManager
        fusedLocation = FusedLocationProviderClient(context)
        val locationRequest = LocationRequest().apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                if (locationResult == null) return
                listener?.onLocation(locationResult)
            }
        }
        gnssCallback = object: GnssStatus.Callback() {
            override fun onSatelliteStatusChanged(status: GnssStatus?) {
                super.onSatelliteStatusChanged(status)
                if (status == null) return
                listener?.onSatellit(status)
            }
        }
        if (ActivityCompat.checkSelfPermission(context,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            fusedLocation.requestLocationUpdates(locationRequest,locationCallback,Looper.myLooper())
            locationManager.registerGnssStatusCallback(gnssCallback)
        }
        enable = true
    }
    fun stop(){
        if (!enable) return
        fusedLocation.removeLocationUpdates(locationCallback)
        locationManager.unregisterGnssStatusCallback(gnssCallback)
        enable = false
    }

    fun isGps() : Boolean{
        return isGPSPermission() and isGPSEnabled()
    }
    fun isGPSPermission() : Boolean{
        return  ActivityCompat.checkSelfPermission(context,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    fun isGPSEnabled(): Boolean {
        val locationManager =
            context.getSystemService(LOCATION_SERVICE) as LocationManager
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    }
}
```