M Permissions対応についての簡単な記事

この記事は
「Android6.0で変わったアプリのアクセス権について(非開発者Android使い向け)」
http://qiita.com/LyricalMaestro0/items/8141ba76eed77cb1db65
の「Android開発者版」という立ち位置です。
Android開発者がM Permissions対応をどのようにすればよいか？というのを記載しています。

# M Permissions対応しないといけない権限
今回M Permissions対応しないといけない権限は以下の通りです。
・SMSへのアクセス
・カメラの使用
・カレンダーの読み書き
・ストレージへの読み書き
・ボディーセンサーの使用
・マイクの使用
・電話機能へのアクセス
・連絡先へのアクセス
・位置情報取得

(http://developer.android.com/intl/ja/guide/topics/security/permissions.html#perm-groups
のTable1のところを参照)

## 注意点
**上記で挙げたものは「実装レベルでのpermission」をグループ化したもの（permission-group）です。
アプリ内部で実装レベルでのpermissionの一つをON(OFF)にすると、そのpermissionが属するグループ全体のpermissionがON(OFF)になることに注意してください。**
たとえば、「READ_EXTERNAL_STORAGE」「WRITE_EXTERNAL_STORAGE」が「ストレージへの読み書き」としてグルーピングされます。READ_EXTERNAL_STORAGEをONにした場合は必然的にWRITE_EXTERNAL_STORAGEもONになります。
(AndroidManifest.xmlにuse-permissionsタグを追記しなければONにはなりませんが…)


# M Permissions対応の実装

今回は前提条件を2段階に分けて解説します。
実装に関しては基本的には
http://developer.android.com/intl/ja/training/permissions/requesting.html
を参考にしています。

## 前提1
・端末6.0でかつtargetSDKVersion=23の場合
・端末5.xでかつtargetSDKVersion=23の場合
のケースについて記載します。

(1) まずAppCompat.checkSelfPermissionで指定した権限がONになっているかチェックします。

```java
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE);
```

(2) checkSelfPermissionの結果、ONだったらそのまま処理を進めます。OFFだったらONにしてもらうリクエストを投げるための処理を行います。

```java
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
  // アクセス権がOFFならここでONにしてもらうようリクエストしてもらう
}else{
  // アクセス権がONならそのまま処理をすすめる
}
```

(3) アクセス権をONにしてもらうためのアクセス許可ダイアログを表示する処理を実装します。（この処理はアクセス権がOFFだった場合のみ実行）
アクセス権をONにしてもらう際、「アクセスする目的をきちんと説明すること」が推奨されているので、ActivityCompat.shouldShowRequestPermissionRationaleメソッドを実行し、trueなら説明し、その後実際のリクエストを行うActivityCompat.requestPermissionsを呼びましょう。

```java
boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE)
if (showRationale) {
  // ダイアログを表示する場合。
  // ここで一発アクセスする目的を説明してからリクエストするのが推奨されている。

  // 説明してOKならrequestPermissionsを呼ぶ。
  return; 
} 

/*
ダイアログを表示しない場合。
ダイアログで「今後は確認しない」にチェックをした場合に実行される。
*/
ActivityCompat.requestPermissions(thisActivity,
    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
```

アクセス権許可ダイアログで「今後は確認しない」をチェックし、閉じた場合はActivityCompat.shouldShowRequestPermissionRationaleがfalseになるのでそのときはそのままActivityCompat.requestPermissionsを呼び出します。（安心してください。ダイアログを呼び出すときと同じメソッドですが、ダイアログは出ませんよ。）

(4) アクセス許可ダイアログからの結果を受け取るためのメソッドをActivity(or Fragment)に実装します。

```java
@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    if(requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 許可するを選択した場合の処理を実装。
        } else {
            // 許可しないを選択した場合を実装
        }
        return;
    }
}
```

http://techbooster.org/android/application/17223/
や
http://sys1yagi.hatenablog.com/entry/2015/11/07/185539
を参照する限りだと許可しないを選択した場合、再度リクエスト許可申請したり、設定画面に飛ばしたり…ということもしています。
(どうするのが正しいかはアプリの性質次第によって大きく変わってくると思います)


以上の実装で、対応できると思います。
ちなみに端末5.x以下の場合、AppCompat.checkSelfPermissionは常にGRANTEDが返ってきます（つまり常に許可されていると帰ってくるということ）

## 前提2
・端末6.0でかつtargetSDKVersion=22以下の場合
・端末5.xでかつtargetSDKVersion=22以下の場合
のケースについて記載します。（普通M Permissions対応する場合は23にするとは思うが何かしらの事情のためやむなく22以下にする場合を記載します）

基本は「前提1」とほとんど同じやり方で実装できます。
ただし「前提1」そのままでは端末6.0でかつtargetSDKVersion=22以下の場合でアクセス権がOFFになっている場合の処理がうまくいきません。

今回の場合、AppCompat.checkSelfPermissionは常にGRANTEDが返ってきます。これだと上記のケースを想定でいないのでエラーが発生します。なので、
AppCompat.checkSelfPermissionの代わりにPermissionChecker.checkSelfPermissionを使えばOKです。

(http://sys1yagi.hatenablog.com/entry/2015/11/07/185539
を参考にさせていただきました。ありがとうございます。)

# まとめ
今回はAndroid開発者向けにざっくりと
・どのアクセス権について対応しないといけないか？
・M Permissions対応の具体的なやり方
について記載いたしました。

簡単なM Permissions対応ならそこまで手間をかけずに行うことができると思います。

追伸：前提2の問題を解決するためにPermissionCheckerクラスを使う、というふうに記載しましたが…どうやったらAppCompatからPermissionCheckerクラスを使う解決策にたどりつくのでしょうか…？Javadoc全部見ていってこれ使えそう、とかいう肉体労働的なことをしたのだろうか…？

# お世話になった参考サイト
http://techbooster.org/android/application/17223/
http://sys1yagi.hatenablog.com/entry/2015/11/07/185539