Android8.0, also known as Android O, is coming soon, with a lot of new features. You can download the Developer Preview 4 version of Android O’s latest system image via AndroidStudio3.0 Canary version. Developer Preview 4 is the last Preview release of Android O before the official release, so it is a candidate for Android O and we can use it to complete development and testing and smooth the transition to Android O.

Later, the accountant lays out an article about Android O behavior change and compatibility scheme. This article mainly talks about one of Android O behavior change — the policy change and adaptation scheme of system runtime permissions.

Android runtime permissions have been added since Android 6.0 (Android M). If you don’t already know about Android runtime permissions, you can check out my article on Android 6.0 runtime permissions management best Practices: Juejin. Cn/post / 684490…

For runtime permission management, there are a lot of open source management library, this time last year I also open source a run permission management solution, it is compatible with the greatest extent of domestic machines, of course, also compatible with Android 8.0: github.com/yanzhenjie/…

Before the official start, first correct a problem, I saw a project on the Internet that can be customized to apply for authorization of the system Dialog, first correct for the present is absolutely not, at most before calling the application code to play a Dialog to remind the user to apply for authorization. I quickly read the source code of the project, as I expected, in a circle after finally calling the system to apply for authorization code.


Android O runtime permission policy changes

If you like to see Google’s website, you can see here: developer.android.com/preview/beh…

Prior to Android O, if an application requested permission at runtime and was granted that permission, the system mistakenly granted the application other permissions that belonged to the same permission group and were registered in the listing.

This behavior has been corrected for Android O applications. The system only grants the application explicit request permission. However, once a user grants a permission to an application, all subsequent requests for permissions in that permission group are automatically approved.

For example, suppose an application lists READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE in its manifest. The application requests READ_EXTERNAL_STORAGE and the user grants this permission. If the application is targeting API level 24 or lower, the system also grants WRITE_EXTERNAL_STORAGE because this permission also belongs to the STORAGE permission group and is registered in the manifest. If the application is targeting Android O, the system grants only READ_EXTERNAL_STORAGE. However, when the application applies for WRITE_EXTERNAL_STORAGE permission in the future, the system grants the permission immediately without prompting the user.

Let’s use the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE examples to see how this affects our existing code.

Before we begin, let’s agree on two methods:

/** * get permissions that are not authorized. * /
getDeinedPermission(String... permissions);
/** * request several permissions. * /
requestPermission(String... deinedPermissions);Copy the code

The permission constant is in the manifest.Permission class, and the READ_EXTERNAL_STORAGE permission was added after API 16, so we typically apply for permissions after Android M for later versions of the system (pseudocode) :

// The permission to apply for.
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}Copy the code

Callback = WRITE_EXTERNAL_STORAGE = READ_EXTERNAL_STORAGE = READ_EXTERNAL_STORAGE = READ_EXTERNAL_STORAGE When we read the memory card elsewhere, we only need to check the WRITE_EXTERNAL_STORAGE permission to read.

If the application is installed in Android O system, we will find that the application crashes when we try to read the contents of the memory card after determining that we have WRITE_EXTERNAL_STORAGE permission, because we did not apply for READ_EXTERNAL_STORAGE permission.

Solutions to Android O runtime permission policy changes

For Android O’s run-time permission policy, our code will be changed to the following (pseudocode) for each version of the system:

// The permission to apply for.
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}Copy the code

However, there are two problems in this way. One is that some permission groups have many permissions, and it is difficult for developers to remember all of them. The READ_EXTERNAL_STORAGE permission constant was not added to the SDK until API 16. There are several such permission constants, some of which were not added to the SDK until Android M. If we force it, the APP will still crash when it’s running on a lower version of the system. Someone said, why don’t we just judge the system version before we apply? Of course, if you don’t mind the trouble, it’s perfectly fine.

Upgrade package

Therefore, we concluded a better solution, in the final analysis, the application of the right to apply for a group of permissions, rather than a single permission. So we put the constants of a group into an array, and assign values to the array based on the system version, resulting in a class like this:

public final class Permission {

    public static final String[] CALENDAR;   // Read and write calendar.
    public static final String[] CAMERA;     / / camera.
    public static final String[] CONTACTS;   // Read and write contacts.
    public static final String[] LOCATION;   // Read the location information.
    public static final String[] MICROPHONE; // Use the microphone.
    public static final String[] PHONE;      // Read phone status, make calls, read and write phone records.
    public static final String[] SENSORS;    // Sensor.
    public static final String[] SMS;        // Read and write SMS messages.
    public static final String[] STORAGE;    // Read and write the memory card.

    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            CALENDAR = new String[]{};
            CAMERA = new String[]{};
            CONTACTS = new String[]{};
            LOCATION = new String[]{};
            MICROPHONE = new String[]{};
            PHONE = new String[]{};
            SENSORS = new String[]{};
            SMS = new String[]{};
            STORAGE = new String[]{};
        } else {
            CALENDAR = new String[]{
                    Manifest.permission.READ_CALENDAR,
                    Manifest.permission.WRITE_CALENDAR};

            CAMERA = new String[]{
                    Manifest.permission.CAMERA};

            CONTACTS = new String[]{
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_CONTACTS,
                    Manifest.permission.GET_ACCOUNTS};

            LOCATION = new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION};

            MICROPHONE = new String[]{
                    Manifest.permission.RECORD_AUDIO};

            PHONE = new String[]{
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.CALL_PHONE,
                    Manifest.permission.READ_CALL_LOG,
                    Manifest.permission.WRITE_CALL_LOG,
                    Manifest.permission.USE_SIP,
                    Manifest.permission.PROCESS_OUTGOING_CALLS};

            SENSORS = new String[]{
                    Manifest.permission.BODY_SENSORS};

            SMS = new String[]{
                    Manifest.permission.SEND_SMS,
                    Manifest.permission.RECEIVE_SMS,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.RECEIVE_WAP_PUSH,
                    Manifest.permission.RECEIVE_MMS};

            STORAGE = newString[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; }}}Copy the code

Before Android M, there is no need for user authorization to use a certain permission, just register in the Manifest. After Android M, you need to register and apply for user authorization. Therefore, we use an empty array as the permission group according to the system version before Android M. Use real array permissions after Android M.

Since we are passing in multiple permission groups, the two methods we agreed upon are not sufficient, so we add two methods:

/** * get permissions that are not authorized. * /
String[] getDeinedPermission(String... permissions);
/** * request several permissions. * /
void requestPermission(String... deinedPermissions);
/** * get permissions that are not authorized. * /
String[] getDeinedPermission(String[]... permissions);
/** * request several permissions. * /
void requestPermission(String[]... deinedPermissions);Copy the code

So our permission application code is simplified like this:

// This method checks the version and returns either an empty array or an array with no permissions.
String[] deniedPermissions = getDeinedPermission(Permission.STORAGE, Permission.SMS);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}Copy the code

Of course, this isn’t the simplest, but it’s enough to accommodate the changes in Android O’s permission policies.

How to simplify AndPermission

Android OS is compatible with Android OS permissions. If you think this project is not for you, you can package it yourself. I encourage developers to do it themselves, and here is the open source address: github.com/yanzhenjie/…

Some of its simple features:

  1. Chain call, one sentence to apply for permission, save complex logical judgment.
  2. Supports annotation callback results and Listener callback results.
  3. Note When applying for a right again after a right is denied, you can use a Rationale note to explain the purpose of applying for the right again. After the user agrees to the request, the user cannot apply for the right again.
  4. Use a SettingDialog to prompt the user to set authorization, even if the user refuses permission and checks no prompt.
  5. Rational Dialog and SettingDialog allow developers to customize.
  6. The default dialog box that comes with AndPermission is customizable and also supports internationalization.
  7. You can apply for permissions anywhere, not just on activities and fragments.
  8. Support permission group application, compatible with Android8.0.

Example for applying for multiple permission groups:

AndPermission.with(this)
    .permission(Permission.CAMERA, Permission.SMS) // Multiple permission groups.
    .callback(new PermissionListener() {
        @Override
        public void onSucceed(int i, @NonNull List<String> list) {
            // TODO do something...
        }

        @Override
        public void onFailed(int i, @NonNull List<String> list) {
            // TODO user does not agree to authorize, usually pop-up Dialog user to authorize in Setting.
        }
    })
    .start();Copy the code

Apply for a single or several permission examples, which are now discouraged due to Android O, but there is no problem until Android O is officially released:

AndPermission.with(this)
    .permission(
        // Multiple groups of permissions. This is now discouraged, but is ok until Android O is officially released.
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_SMS
    ) 
    .callback(new PermissionListener() {
        @Override
        public void onSucceed(int i, @NonNull List<String> list) {
            // TODO do something...
        }

        @Override
        public void onFailed(int i, @NonNull List<String> list) {
            // TODO user does not agree to authorize, usually pop-up Dialog user to authorize in Setting.
        }
    })
    .start();Copy the code

That’s the end of Android O’s run-time permission policy changes and solutions. Leave a comment below if you don’t understand.