概要

AndroidアプリのパーミッションにはNormalとDangerousの2種類がありGPS位置情報を取得するにはDangerousに分類されるACCESS_FINE_LOCATIONパーミッションが必要になる。Dangerousに分類されるパーミッションはパーミッションの取得にユーザの承認が必要で、このパーミッションの承認方法がtargetSdkVersionによって違う。

targetSdkVersionが23未満のとき

AndroidManifest.xmlにuses-permissionを追加する。

<manifest ...>

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

    <application ...>
        ...
    </application>
</manifest>

これでアプリのインストール時にシステムがユーザに承認を求め、アプリの実行時には特別な処理をしなくてもGPS機能にアクセスすることができる。

targetSdkVersionが23以上のとき

現時点ではほぼこちらだろう。AndroidManifest.xmlにuses-permissionとuses-featureを追加する。

<manifest ...>

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature android:name="android.hardware.location.gps" />

    <application ...>
        ...
    </application>
</manifest>

targetSdkVersionが23以上の時はインストール時にはパーミッションの承認は行われず、アプリ実行時に必要になったときにrequestPermissions()を使ってユーザに承認をリクエストする。minSdkVersionが23以上なことはほぼないので基本的にPermissionCheckerやActivityCompatにはサポートライブラリを使う。

AppcompatActivityを継承したActivityのGPS機能にアクセスするメソッドにて次のような処理を行いパーミッションのリクエストを行う。requestPermissions()を呼ぶとユーザに承認を求めるダイアログが表示され、その結果はコールバックでアプリに通知される。そのためGPSのアクセスする部分はもとからパーミッションが得られていた場合とユーザにリクエストして取得した場合の複数にまたがってしまう。

private static final int REQUEST_CODE_GPS = 1;

public void onClicked(View view) {
    if(PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
        //Access GPS
    } else {
        if(ActivityCompat.shouldShowRequestPermissionRationable(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
            //Show Reason
        }
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_GPS);
    }
}

@Override
public void onRequestPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grandResults) {
    switch(requestCode) {
    case REQUEST_CODE_GPS:
        if(PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
            //Access GPS
        }
        break;
    }
}

Show Reasonと書いてあるところでは自前でユーザにGPS機能にアクセスする理由を説明する必要がある。

GPS機能を使って位置情報を取得

これまでの方法でアプリがGPS機能を利用できる状態になった。実際にGPSを使用する部分はtargetSdkVersionによらずに行える。まずはGPS位置情報を受け取るためのLocationListenerを実装する。例えば無名クラスを使って実装すると次のようになる。

 LocationListener locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
        textView.setText(String.format(Locale.US, "%f,%f", latitude, longitude));
     }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {

    }

    @Override
    public void onProviderEnabled(String s) {

    }

    @Override
    public void onProviderDisabled(String s) {

    }
};

この例では適当なTextViewに得られた緯度経度を表示するようにしている。 次に上記したAccess GPSの部分で次のようにしてGPS位置情報のアクセスを開始する。

LocationManager locationManager;

@Oerride
public void onStart() {
    super.onStart();
    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}

@Override
public void onStop() {
    super.onStop();
    locationManager.removeUpdates(locationListener);
}

この例では実行時パーミッションは取得済みと仮定している。もし取得できていなければrequestLocationUpdatesはSecurityExceptionを投げる。よってrequestLocationUpdatesの直前にPermissionCheckerを使って再度パーミッションの確認を行うかcatchでSecurityExceptionを処理したほうが良い。

以上でGPS位置情報が取得できるようになった。でも実用するにはもっと工夫が必要。