新しいパーミッションモデルの対応が少し楽になるスニペット


Android MからiOSのように実行時にパーミッションを確認するようになりました。
既存のアプリをM対応するには、処理の前にパーミッションの要求処理を書かないといけなかったり、拒否された場合でも適切に動作させないといけなかったり、地味ですがやることは結構ありそうですね。

Google Play Servicesを使用している場合はGoogleが対応してくれないと、というのはありますが、Android　APIを使用している部分はM Preview2 SDKを使用して動作確認することができます。

対応にあたり日本語のドキュメントも公開されています。

* [パーミッション](https://developer.android.com/intl/ja/preview/features/runtime-permissions.html)

GitHubにはサンプルも公開されております。

* [Android RuntimePermissions Sample](https://github.com/googlesamples/android-RuntimePermissions)

これなどなどの情報をもとに正式公開前に少しずつ対応していくとよさそうですね。
（対応していないと、権限を拒否された時に最悪アプリが落ちる可能性があります…）

## 対応ポイント

実行時のパーミッションに対応するうえでのポイントは次の2点です。

1. パーミッショングループに属するパーミッションが必要な処理の前に、コード上でパーミッションの要求を追加する
2. 要求の結果の許可/拒否に応じて適切に処理する

パーミッショングループには何があるのか、グループの中にはどのパーミッションがあるのか、どう要求するのか、などなどの内容は上記のドキュメントにまとまっています。

簡単ですが、ソースコード上での流れは次のようになります。

```java
public class SampleActivity extends Activity {

    private static final int REQUEST_CAMERA = 0;
    
    // ①パーミッションの要求
    public void requestCameraPermission() {
        // カメラを使用する権限があるかどうか
        if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            // カメラの表示
            return;
        }
        // カメラの使用が許可されていない場合
        if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
            // 追加説明が必要な場合の対応（サンプルではトーストを表示している）
        }
        // カメラパーミッションを要求（一度に複数のパーミッションを要求することも可能）
        requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
    }

    // ②要求結果の受け取り
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
	    if (requestCode != REQUEST_CAMERA) {
		    return;
	    }
	
	    // カメラパーミッションの使用が許可された
	    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
		    // カメラの表示
		    return;
	    }
	    // 使用が拒否された時の対応
    }
}
```

### ユーティリティの導入

これから対応するにあたり、Googleから公開されていたサンプルを元に今後も使用しそうな部分を共通化してみました。ソースコードはGistにあります。

* [Gist](https://gist.github.com/droibit/3dfa39dc8be3017dc37c)

### 使い方

ユーティリティを使用しても大きな変化はありませんが、可読性の向上、下位互換（正式版リリースで修正が必要になるかも）に対応しています。

```java

import static com.example.android.Permission.hasSelfPermission;
import static com.example.android.Permission.requestAllPermissions;
import static com.example.android.Permission.asArray;

public class SampleActivity extends Activity {

    private static final int REQUEST_CAMERA = 0;
    
    // ①パーミッションの要求
    public void requestCameraPermission() {
        // カメラを使用する権限があるかどうか
        if (Permission.hasGranted(Permission.CAMERA)) {
            // カメラの表示
            return;
        }
        // カメラの使用が許可されていない場合
        if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
            // 追加説明が必要な場合の対応（サンプルではトーストを表示している）
        }
        // カメラパーミッションを要求（一度に複数のパーミッションを要求することも可能）
        requestAllPermissions(this, asArray(Permission.CAMERA), REQUEST_CAMERA);
        // 複数の場合
        // requestAllPermissions(this, asArray(Permission.CAMERA, Permission.READ_CONTACTS), -1);
    }

    // ②要求結果の受け取り
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
	    if (requestCode != REQUEST_CAMERA) {
		    return;
	    }
	
	    // カメラパーミッションの使用が許可された
	    if (!Permission.hasGranted(grantResults[0])) {
		    // カメラの表示
		    return;
	    }
	    // 複数を同時に確認する場合
	    // if (Permission.hasGranted(grantResults) { 
	    // }
		
	    // 使用が拒否された時の対応
    }
}
```

実行時パーミッションの対応が必要なパーミッションがどれかと迷わないように、必要なものだけ抜き出してまとめています。

全て小さなところではありますが、ユーティリティクラスにしておいた方が便利そうだったのでまとめてみました。
パーミッション対応は拒否された時にどうするか、のほうがメインになるかと思いますので、それ以外の部分が少しでも楽になればと思います。

## おまけ

下位互換のために第一引数に *Context* を渡さないといけないのがイけてないと感じた方、Kotlinなら拡張関数でいい感じに書けます。

``` kotlin
public fun Context.hasSelfPermission(permission: String): Boolean 
	= Permission.hasSelfPermission(this, permission)
public fun Context.hasSelfPermissions(permissions: Array<String>): Boolean
 	= Permission.hasSelfPermissions(this, permissions)
```

使い方は次のようになります。

```kotlin
public class TestActivity: Activity() {

    companion object {
        val REQUEST_CODE = 0
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>?, grantResults: IntArray?) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        if (requestCode != REQUEST_CODE) {
            return;
        }
        
        if (hasSelfPermissions(arrayOf(Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION))) {
        }
//        if (hasSelfPermission(Permission.ACCESS_COARSE_LOCATION)) {
//        }
    }

    private fun requestPermission() {
      // Permission#asArrayは組み込みのarrayOf関数があるので使わない
      requestPermissions(arrayOf(Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION), REQUEST_CODE)
    }
```

以上です。