Nexus9でAndroid6.0の権限のチェックを検証

#Nexus9をアップデート

Nexus9をAndroid6.0にアップデートすることも出来るんです。

# Android6.0へのアップデートの仕方

設定 -> タブレット情報 -> システムアップデート -> システムアップデート

# めちゃくちゃ長い

今まで更新していなかったこともあり時間がかかりました。何度も更新を繰り返した結果、無事成功しました。

#権限

Android6.0以前のバージョンはGooglePlayのインストールボタンをタップすると、このアプリはカメラや住所録、GPSを使うけど、それでもインストールするなら同意してくれ！という権限の確認が行われてました。このおかげで、権限が同意されたという前提でアプリをガンガン開発できました。ところがこの権現がAndroid6.0から大きく変更になりました。

#Android4.4.4 
![Screenshot_2016-03-07-13-05-00_jpeg.png](https://qiita-image-store.s3.amazonaws.com/0/99731/9484e1a0-150e-6b62-a414-b4da671c9fb2.png)


#Android6.0

![Screenshot_20160307-130917_png.png](https://qiita-image-store.s3.amazonaws.com/0/99731/122fa815-97a7-41de-8882-46ffa950fd70.png)

グーグルプレイ(Android6.0)からMicrosoftのWordをインストールした時の画面です。なんとインストールするときには権限の同意は求められません。使用開始時に権限への同意が求められるというメッセージがあります。これグーグルが勝手にやってくれるわけではありません。

#試しにカレンダーを読み込むコードを実行

カレンダーのデータを読み込む権限をセット

```xml:manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.@@@@.myapplication">
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

```


```java:MainActivity.java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Uri calendars = CalendarContract.Calendars.CONTENT_URI;
        Cursor c = getContentResolver().query(calendars, new String[]{"name"}, null, null, null);
        if (c.moveToFirst()) {
            String name;
            do {
                name = c.getString(c.getColumnIndex("name"));
                Log.d("デバッグ", "名前 : "  + name);
            } while (c.moveToNext());
        }
    }
}
```

#Android4.4.4
無事カレンダーの情報はゲットできました。

#Android6
強制終了

#対象となる権限

Android6ユーザーに対しては、アプリ起動、またはその権限が必要になった時点でデベロッパーが権限の確認をしないと強制終了します。ではどんな権限が対象となるんでしょうか？

![System_Permissions___Android_Developers.png](https://qiita-image-store.s3.amazonaws.com/0/99731/0c4d1bad-5306-ac2c-725a-4bce96b032d4.png)

今後Android6ユーザーが増えてきてメインの端末になった場合には、強制終了で動作しないようなアプリが多くなってきます。早急に修正しましょう。

#対応方法

先ほどのカレンダー情報を取得するソースコードをAndroid6で動かせるように修正します。権限が複数の場合の確認方法にもふれたいので権限を増やします。


```xml:manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.@@@@@.myapplication">
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

```

```java:Man

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    String[] permissions = new String[]{
            Manifest.permission.READ_CALENDAR,
            Manifest.permission.WRITE_CALENDAR,
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.ACCESS_FINE_LOCATION
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean permissions_confirm_flag = true;
            for ( String permission : permissions) {
                if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                    permissions_confirm_flag = false;
                }
            }
            if (permissions_confirm_flag) {
                get_calendar_data();
            } else {
                requestPermissions(permissions, 123);
            }
        } else {
            get_calendar_data();
        }
    }

    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {


        for(int i = 0; i < permissions.length; i ++) {

            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                Log.d("デバッグ", permissions[i] + ":" + "OK");
            }

            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                Log.d("デバッグ", permissions[i] + ":" + "NG");
            }
        }

        if (requestCode == 123) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                get_calendar_data();

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {
                Toast.makeText(MainActivity.this, "権限をONにしないとアプリが使えません", Toast.LENGTH_SHORT).show();
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
        }
    }

    private void get_calendar_data() {

        Uri calendars = CalendarContract.Calendars.CONTENT_URI;
        Cursor c = getContentResolver().query(calendars, new String[]{"name"}, null, null, null);
        String txt = "";
        if (c.moveToFirst()) {
            do {
                txt  += c.getString(c.getColumnIndex("name"));
            } while (c.moveToNext());
        }
        Toast.makeText(MainActivity.this, txt, Toast.LENGTH_SHORT).show();
    }
}


```

##requestPermissions
文字列の配列にしてrequestPermissionsに渡します。複数の権限を順番にアラート表示してユーザーに同意してもらいます。ちなみに同意しても、後から設定画面で権限を外すことも可能なので注意しましょう。

```java

String[] permissions = new String[]{
            Manifest.permission.READ_CALENDAR,
            Manifest.permission.WRITE_CALENDAR,
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.ACCESS_FINE_LOCATION
};

requestPermissions(permissions, 123);


```

##onRequestPermissionsResult

権限のアラートを表示して権限のチェックが完了したら、onRequestPermissionsResultが呼び出されます。requestCodeはrequestPermissionsで設定した123が代入されてます。permissionsは確認した権限、grantResultsは権限の確認の結果が代入されてます。


```java

 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)

```

こんな感じでデバッグしてもらうと理解しやすいです。

```java

  for(int i = 0; i < permissions.length; i ++) {

            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                Log.d("デバッグ", permissions[i] + ":" + "OK");
            }

            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                Log.d("デバッグ", permissions[i] + ":" + "NG");
            }
        }


```