FusedLocationProviderClientを用いた位置情報の取得

# 概要
Androidアプリで現在地の情報を取得する場合、以前はLocationManagerが一般的に使われていましたが、version 11.6.0以上のGooglePlayServicesでは、より使いやすく精度の高いFusedLocationProviderClientがサポートされています。

# 用例
## Permissionの設定
Androidアプリで位置情報を使用する場合、Manifestにパーミッションの追記が必要です。

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

## 位置情報を管理するクラスを作成
位置情報を取得する機能がアプリ内の複数箇所で必要となる場合、汎用的なクラスを作って任意の箇所から呼び出せるようにしておくと便利です。

```java:MyLocationManager
import com.google.android.gms.location.*;

public class MyLocationManager extends LocationCallback {
    private static final int LOCATION_REQUEST_CODE = 1;
    private Context context;
    private FusedLocationProviderClient fusedLocationProviderClient;
    private OnLocationResultListener mListener;

    public interface OnLocationResultListener {
        void onLocationResult(LocationResult locationResult);
    }

    public LocationManager(Context context, OnLocationResultListener mListener) {
        this.context = context;
        this.mListener = mListener;
        this.fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
    }

    @Override
    public void onLocationResult(LocationResult locationResult) {
        super.onLocationResult(locationResult);
        mListener.onLocationResult(locationResult);
    }

    public void startLocationUpdates() {
        // パーミッションの確認
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Logger.d("Permission required.");
            ActivityCompat.requestPermissions((Activity) context, new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
            }, LOCATION_REQUEST_CODE);

            return;
        }
        
        // 端末の位置情報サービスが無効になっている場合、設定画面を表示して有効化を促す
        if (!isGPSEnabled()) {
            showLocationSettingDialog();
            return;
        }

        LocationRequest request = new LocationRequest();
        request.setInterval(5000);
        request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        fusedLocationProviderClient.requestLocationUpdates(request, this,null);
    }

    public void stopLocationUpdates() {
        fusedLocationProviderClient.removeLocationUpdates(this);
    }

    private Boolean isGPSEnabled() {
        android.location.LocationManager locationManager = (android.location.LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
    }

    private void showLocationSettingDialog() {
        new android.app.AlertDialog.Builder(context)
                .setMessage("設定画面で位置情報サービスを有効にしてください")
                .setPositiveButton("設定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        context.startActivity(intent);
                    }
                })
                .setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //NOP
                    }
                })
                .create()
                .show();
    }
}
```

## 親クラスを定義

複数のFragmentで位置情報を取得できるよう、MyLocationManager.OnLocationResultListenerを実装する抽象クラスを定義しておきます。その際、Fragmentがactiveな状態の時のみ位置情報を取得するよう、onResume/onPauseで位置情報取得の開始/停止をハンドリングします。

```java:BaseFragment
public abstract class BaseFragment extends Fragment 
         implements MyLocationManager.OnLocationResultListener {
    private MyLocationManager locationManager;

    @Override
    public void onResume() {
        super.onResume();

        locationManager = new MyLocationManager(getContext(), this);
        locationManager.startLocationUpdates();
    }

    @Override
    public void onPause() {
        super.onPause();

        if (locationManager != null) {
            locationManager.stopLocationUpdates();
        }
    }
}
```

## 使用例
位置情報を利用したいFragmentで上記のBaseFragmentを継承することで、onLocationResultで位置情報取得成功時のコールバックを受け取ることができます。

```java:
public class SomeFragment extends Fragment {
    ...

    @Override
    public void onLocationResult(LocationResult locationResult) {
        if (locationResult == null) {
            Logger.e("# No location data.");
            return;
        }

        // 緯度・経度を取得
        double latitude = locationResult.getLastLocation().getLatitude();
        double longitude = locationResult.getLastLocation().getLongitude();
    }

    ...
}
```

# 参考
- [公式: FusedLocationProviderClient ](https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient)
- [Google Play ServiceのFusedLocationで位置情報を取得](https://blog.tkt989.info/2017/12/09/google-play-service%E3%81%AEfusedlocation%E3%81%A7%E4%BD%8D%E7%BD%AE%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97/)
- [FusedLocationProviderClient を使って位置情報を取得 ](https://akira-watson.com/android/fusedlocationproviderapi.html)