Before Android applies for permission, simply encapsulate the pop-up box to explain the application reason, tool class, and deal with app compliance inspection

Posted by ultraviolet_998 on Tue, 21 Dec 2021 07:16:22 +0100

Recently, when browsing the news, we often see that the Ministry of industry and information technology reported that so and so app failed the compliance inspection, refused to make rectification, and ordered all of this information, especially financial apps, off the shelves. The protection of personal information is indeed very important to users. I believe that the vast majority of industry workers have also felt the trend of the state's regulation of the Internet and apps and its determination to protect users' information in recent years.

Among the special rectification of app, one is the specification of APP permission application: before permission application, it is necessary to clarify the use of applying for permission to the user. Next, it simply encapsulates the pop-up box tool class of permission application prompt, and uniformly pop-up the box to clarify the use of APP applying for permission when applying for permission for the first time.

This approach is more advocated because the probability of users agreeing to authorization will be higher.

The three-party permission application framework used in the project is PermissionUtils in the uitlcode library, which is introduced in this way

    implementation'com.blankj:utilcode:1.30.6'

If you want to introduce the Android x version

    implementation'com.blankj:utilcodex:1.30.6'

In fact, there are many mature permission application frameworks on the market, such as Guo Lin's great God's PermissionX , the highly extended permission application framework well realizes the function that we need to pop up the box to explain the reason and use of applying for permission for the first time. In view of the introduction of utilcode Library in our project, which has ready-made PermissionUtils, PermissionX is not introduced.

Now let's talk about the usage of PermissionUtils, which can apply for a single permission or an entire permission group. First, you need to open the Android manifest The requested permission is stated in the XML file, such as applying for camera and reading / writing mobile phone storage permission:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

PermissionUtils is a simple way to apply for permission, such as:

        PermissionUtils.permission(PermissionConstants.STORAGE).callback(new PermissionUtils.SimpleCallback() {
            @Override
            public void onGranted() {
                UIToast.showShort("onGranted");
            }

            @Override
            public void onDenied() {
                UIToast.showShort("onDenied");

            }
        }).request();

The callback here is the simplest callback SimpleCallback in the framework. It cannot handle rejection and check the logic of not prompting again. If you want to know the permissions of these rejection and not prompting again, you need to use SingleCallback and FullCallback, such as:

        PermissionUtils.permission(PermissionConstants.STORAGE).callback(new PermissionUtils.SingleCallback() {
            @Override
            public void callback(boolean isAllGranted,
                                 @NonNull List<String> granted,
                                 @NonNull List<String> deniedForever,
                                 @NonNull List<String> denied) {

            }
        }).request();

Therefore, it is recommended to use the latter two callbacks, because we can get which permissions are allowed, which are rejected and which are rejected without prompt. In these callbacks, we can handle our own logic.

PermissionUtils can apply for permissions contained in multiple permission groups, or directly apply for a single permission, such as:

     PermissionUtils.permission(Manifest.permission.CAMERA).callback(new PermissionUtils.SingleCallback() {
            @Override
            public void callback(boolean isAllGranted,
                                 @NonNull List<String> granted,
                                 @NonNull List<String> deniedForever,
                                 @NonNull List<String> denied) {

            }
        }).request();

Because the permission method parameter is an indefinite array type, you can apply for multiple permission groups or multiple permissions (non permission groups) at one time. The permissions to be applied will be summarized in PermissionUtils.

Of course, PermissionUtils also follows the original permission application method of Android and defines several listeners, onrational elistener and OnExplainListener. The functions of these two listeners can be known from their names. I won't elaborate here.

The following is the simple usage of PermissionUtils. We completely rely on these features of the framework to encapsulate our PermissionManager tool class: before applying for permission, tell the user the reason why permission is needed through a pop-up box. Such an app will make the user feel reliable and the success rate of permission passing is also high.

For high extension, we also define our own permission group name and its corresponding permissions to be applied for (the implementation here is also based on PermissionUtils), because some permissions are unnecessary to apply for. If you apply for these unnecessary permissions, you also need to explain the reason and use of the application (for example, if you apply for CAMERA, you have to explain what you do when you apply for CAMERA). For example:

 /**
     * Custom permission group name
     */
    public static final String SMS = "MDroidS.permission-group.SMS";
    public static final String CONTACTS = "MDroidS.permission-group.CONTACTS";
    public static final String LOCATION = "MDroidS.permission-group.LOCATION";
    public static final String MICROPHONE = "MDroidS.permission-group.MICROPHONE";
    public static final String PHONE = "MDroidS.permission-group.PHONE";
    public static final String CAMERA = "MDroidS.permission-group.CAMERA";
    public static final String STORAGE = "MDroidS.permission-group.STORAGE";
    public static final String ALBUM = "MDroidS.permission-group.ALBUM";

    /**
     * Customize the classification of permission groups. Users can freely extend the permissions to be applied
     */
    private static final String[] GROUP_CAMERA = new String[]{"android.permission.CAMERA"};
    private static final String[] GROUP_SMS = new String[]{"android.permission.SEND_SMS"};
    private static final String[] GROUP_CONTACTS = new String[]{"android.permission.READ_CONTACTS"};
    private static final String[] GROUP_LOCATION = new String[]{"android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
    private static final String[] GROUP_MICROPHONE = new String[]{"android.permission.RECORD_AUDIO"};
    private static final String[] GROUP_PHONE = new String[]{"android.permission.CALL_PHONE"};
    private static final String[] GROUP_STORAGE = new String[]{"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};

    private static final String[] GROUP_PERMISSION = new String[]{SMS, CONTACTS, LOCATION, MICROPHONE, PHONE, ALBUM, CAMERA};

The above is the alias of the permission group defined by us and its corresponding required permissions. If other permissions of the permission group are required, you can directly add the required permissions in the permission array. For example, phone, its corresponding group_ There is only Android in phone permission. CALL_ Phone, we can continue to add Android permission. READ_ PHONE_ State et al.

Because the permission group alias corresponds to the permission application reason one by one, we use the Map type to save this correspondence: the key value corresponds to our customized permission group alias, the value value corresponds to our application reason and use, and the application reason and use information corresponding to the permission group alias are as follows:

    <!--Reason and use description of permission application-->
    <string name="permission_bluetooth_des">Please allow Bluetooth to.</string>
    <string name="permission_camera_des">Please allow the camera to be used so that.</string>
    <string name="permission_photo_des">Please allow photo albums to.</string>
    <string name="permission_contacts_des">Please allow the address book to.</string>
    <string name="permission_location_des">Please allow positioning so that.</string>
    <string name="permission_microphone_des">Please allow the microphone to.</string>
    <string name="permission_storage_des">Please allow storage so that.</string>
    <string name="permission_phone_des">Please allow the use of the telephone in order to make a call.</string>
    <string name="permission_sms_des">Please allow text messages so that.</string>

    <string name="dialog_permission_allow">allow</string>
    <string name="dialog_permission_refuse">refuse</string>
    <string name="dialog_permission_title">Apply for permission</string>

    <string name="dialog_permission_remind_prefix">This feature requires access to your</string>
    <string name="dialog_permission_remind_suffix">,Please open the permission by applying the settings.</string>

    <string name="dialog_title">reminder</string>
    <string name="dialog_btn_cancel">cancel</string>
    <string name="dialog_btn_confirm">confirm</string>

Here, we use FullCallback to implement the processing logic of rejecting and not prompting. The pop-up box guides the user to set and open the permission. It is not difficult to implement as a whole. The calling method is simple and can deal with most situations. For example:

        PermissionManager.permission(getContext(), new PermissionManager.OnPermissionGrantCallback() {
            @Override
            public void onGranted() {
                UIToast.showShort("onGranted");
            }

            @Override
            public void onDenied() {
                UIToast.showShort("onDenied");

            }
        }, PermissionManager.CAMERA, PermissionManager.MICROPHONE, PermissionManager.ALBUM);

We just call the permission manager to automatically process the above logic. To apply for CAMERA, MICROPHONE and ALBUM, we need to display the reason description information corresponding to the three permission groups to the user through a pop-up box, such as:

Figure I Figure II

Figure 1 shows the reason why we need to clarify the permission application. Figure 2 shows that when we refuse the camera and microphone permission and don't prompt again, the pop-up box reminds the user to apply and set the opening permission. Finally, the code of this tool class is posted:

package com.littlejerk.sample.permission;

import android.annotation.SuppressLint;
import android.app.Activity;

import com.blankj.utilcode.util.ObjectUtils;
import com.blankj.utilcode.util.PermissionUtils;
import com.blankj.utilcode.util.StringUtils;
import com.littlejerk.library.manager.log.UILog;
import com.littlejerk.sample.R;
import com.littlejerk.sample.widget.dialog.AppDialogManager;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.annotation.StringDef;

/**
 * @author : HHotHeart
 * @date : 2021/8/21 00:59
 * @desc : Permission application pop-up management class
 */
public class PermissionManager {

    private static final String TAG = "PermissionManager";
    /**
     * Gets the list of permissions declared by the manifest file
     */
    private static final List<String> PERMISSIONS = PermissionUtils.getPermissions();

    /**
     * Custom permission group name
     */
    public static final String SMS = "MDroidS.permission-group.SMS";
    public static final String CONTACTS = "MDroidS.permission-group.CONTACTS";
    public static final String LOCATION = "MDroidS.permission-group.LOCATION";
    public static final String MICROPHONE = "MDroidS.permission-group.MICROPHONE";
    public static final String PHONE = "MDroidS.permission-group.PHONE";
    public static final String CAMERA = "MDroidS.permission-group.CAMERA";
    public static final String STORAGE = "MDroidS.permission-group.STORAGE";
    public static final String ALBUM = "MDroidS.permission-group.ALBUM";

    /**
     * Customize the classification of permission groups. Users can freely extend the permissions to be applied
     */
    private static final String[] GROUP_CAMERA = new String[]{"android.permission.CAMERA"};
    private static final String[] GROUP_SMS = new String[]{"android.permission.SEND_SMS"};
    private static final String[] GROUP_CONTACTS = new String[]{"android.permission.READ_CONTACTS"};
    private static final String[] GROUP_LOCATION = new String[]{"android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
    private static final String[] GROUP_MICROPHONE = new String[]{"android.permission.RECORD_AUDIO"};
    private static final String[] GROUP_PHONE = new String[]{"android.permission.CALL_PHONE"};
    private static final String[] GROUP_STORAGE = new String[]{"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};

    private static final String[] GROUP_PERMISSION = new String[]{SMS, CONTACTS, LOCATION, MICROPHONE, PHONE, ALBUM, CAMERA};

    /**
     * Pop up prompt for permission application
     *
     * @param activity
     * @param callback
     * @param permissions {@link android.Manifest.permission#CALL_PHONE} And {@ link PermissionManager#SMS}
     * @return
     */
    public static void permission(Activity activity,
                                  OnPermissionGrantCallback callback,
                                  @PermissionType String... permissions) {

        Map<String, String> map = PermissionManager.getPermissionDesMap(permissions);

        permission(activity, map, callback, permissions);
    }

    /**
     * @param activity    context
     * @param map         null:Indicates that the permission application information box is not displayed or the permission is invalid
     *                    key:Permission name, which can be a single permission or permission group name. value: useful information of the requested permission or permission group
     * @param callback    Permission callback
     * @param permissions Custom permission group name
     */
    public static void permission(Activity activity,
                                  Map<String, String> map,
                                  final OnPermissionGrantCallback callback,
                                  @PermissionType String... permissions) {
        //Indicates that the permission application information box is not displayed or the permission is invalid
        if (null == map) {
            permissionNoExplain(activity, callback, permissions);
            return;
        }

        if (activity == null) {
            UILog.e(TAG + " activity == null");
            callback.onDenied();
            return;
        }
        //Permission to request (single permission or permission group)
        Set<String> deniedPermission = map.keySet();
        if (ObjectUtils.isEmpty(deniedPermission)) {
            callback.onGranted();
            return;
        }
        UILog.e(TAG + " deniedPermission: " + deniedPermission);

        StringBuilder sb = new StringBuilder();
        int i = 0;
        Set<String> manifestPermission = new LinkedHashSet<String>();

        for (String groupType : deniedPermission) {
            i++;
            String msg = map.get(groupType);
            sb.append(i).append(",").append(msg).append("\n");
            //All permissions contained in the permission group
            String[] arrayPermission = PermissionManager.getPermissions(groupType);
            Collections.addAll(manifestPermission, arrayPermission);
        }
        String remindMsg = null;
        String sbStr = sb.toString();

        int endIndex = sbStr.lastIndexOf("\n");
        if (deniedPermission.size() == 1) {
            remindMsg = sbStr.substring(2, endIndex);
        } else {
            remindMsg = sbStr.substring(0, endIndex);
        }
        //Permissions contained in permission groups
        String[] permissionArray = manifestPermission.toArray(new String[manifestPermission.size()]);
        permissionExplain(activity, remindMsg, callback, permissionArray, permissions);

    }


    /**
     * The permission application pop-up box explains the reason for the application or the purpose of the permission
     *
     * @param activity        context
     * @param remindMsg       Permission application reason or use information
     * @param callback        Permission callback
     * @param permissionArray Permissions contained in permission groups, such as {@ link #GROUP_SMS}
     * @param permissions     Custom permission group name, such as {@ link #SMS}
     */
    private static void permissionExplain(final Activity activity, String remindMsg,
                                          final OnPermissionGrantCallback callback,
                                          final String[] permissionArray,
                                          @PermissionType String... permissions) {
        //If the permission application fails, a pop-up prompt will appear
        AppDialogManager.getInstance().showPermissionRemindDialog(activity, remindMsg,
                (dialogInterface, i) -> callback.onDenied(),
                (dialogInterface, i) -> PermissionUtils.permission(permissionArray).callback(new PermissionUtils.FullCallback() {
                    @Override
                    public void onGranted(@NonNull List<String> permissionsGranted) {
                        callback.onGranted();
                    }

                    @Override
                    public void onDenied(@NonNull List<String> permissionsDeniedForever, @NonNull List<String> permissionsDenied) {
                        if (!ObjectUtils.isEmpty(permissionsDeniedForever)) {
                            //Handling of permission no longer prompt
                            permissionsDeniedForever(activity, callback, permissionsDeniedForever, permissions);
                        } else {
                            callback.onDenied();
                        }
                    }
                }).request());
    }

    /**
     * Processing the logic that permission is no longer prompted
     *
     * @param activity                 context
     * @param callback                 Permission callback
     * @param permissionsDeniedForever Permission groups no longer prompt for certain permissions
     * @param permissions              Custom permission group name, such as {@ link #SMS}
     */
    private static void permissionsDeniedForever(Activity activity,
                                                 OnPermissionGrantCallback callback,
                                                 List<String> permissionsDeniedForever,
                                                 @PermissionType String... permissions) {
        //Obtain the corresponding user-defined permission group according to the returned no longer prompt permission
        List<String> requestPermissionList = getMatchPermissionGroup(permissionsDeniedForever, permissions);
        if (ObjectUtils.isEmpty(requestPermissionList)) {
            callback.onDenied();
            return;
        }

        //Splicing setting permission prompt
        StringBuilder permissionRequestMsg = new StringBuilder();
        permissionRequestMsg.append(StringUtils.getString(R.string.dialog_permission_remind_prefix));
        for (String permission : requestPermissionList) {
            String permissionName = getPermissionName(permission);
            if (!StringUtils.isEmpty(permissionName))
                permissionRequestMsg.append(permissionName).append(",");
        }
        if (permissionRequestMsg.lastIndexOf(",") > 0) {
            permissionRequestMsg.deleteCharAt(permissionRequestMsg.lastIndexOf(","));
        } else {
            callback.onDenied();
            return;
        }
        permissionRequestMsg.append(StringUtils.getString(R.string.dialog_permission_remind_suffix));
        AppDialogManager.getInstance().showPermissionSettingRemind(activity, permissionRequestMsg.toString(),
                (dialogInterface, i) -> callback.onDenied(),
                (dialogInterface, i) -> openAppSystemSettings());
    }

    /**
     * Get the corresponding permission list
     *
     * @param rawPermissionList Permission name collection
     * @param permissions       Custom permission group name
     * @return
     */
    private static List<String> getMatchPermissionGroup(List<String> rawPermissionList, @PermissionType String... permissions) {
        if (ObjectUtils.isEmpty(rawPermissionList)) return null;
        List<String> permissionList = new LinkedList<>();
        if (!ObjectUtils.isEmpty(permissions)) {
            for (String permission : permissions) {
                for (String permissionRaw : getPermissions(permission)) {
                    if (rawPermissionList.contains(permissionRaw)) {
                        permissionList.add(permission);
                        break;
                    }
                }
            }
        } else {
            for (String permission : GROUP_PERMISSION) {
                for (String permissionRaw : getPermissions(permission)) {
                    if (rawPermissionList.contains(permissionRaw)) {
                        permissionList.add(permission);
                        break;
                    }
                }
            }
        }
        return permissionList;
    }

    /**
     * The reason explanation box is not displayed in the permission application
     *
     * @param activity    context
     * @param callback    Permission application callback
     * @param permissions Custom permission group name
     */
    @SuppressLint("WrongConstant")
    private static void permissionNoExplain(final Activity activity,
                                            final OnPermissionGrantCallback callback,
                                            @PermissionType final String... permissions) {

        Set<String> manifestPermission = new LinkedHashSet<String>();
        for (String groupType : permissions) {
            //All permissions contained in the permission group
            String[] arrayPermission = PermissionManager.getPermissions(groupType);
            Collections.addAll(manifestPermission, arrayPermission);
        }
        String[] permissionArray = manifestPermission.toArray(new String[manifestPermission.size()]);
        PermissionUtils.permission(permissionArray).callback(new PermissionUtils.FullCallback() {

            @Override
            public void onGranted(@NonNull List<String> permissionsGranted) {
                callback.onGranted();
            }

            @Override
            public void onDenied(@NonNull List<String> permissionsDeniedForever, @NonNull List<String> permissionsDenied) {
                if (!ObjectUtils.isEmpty(permissionsDeniedForever)) {
                    if (activity == null) {
                        callback.onDenied();
                        return;
                    }
                    permissionsDeniedForever(activity, callback, permissionsDeniedForever, permissions);

                } else {
                    callback.onDenied();
                }


            }
        }).request();
    }

    /**
     * Get permission group
     *
     * @param permission Custom permission group name
     * @return
     */
    public static String[] getPermissions(String permission) {

        switch (permission) {
            case CONTACTS:
                return GROUP_CONTACTS;
            case SMS:
                return GROUP_SMS;
            case STORAGE:
            case ALBUM:
                return GROUP_STORAGE;
            case LOCATION:
                return GROUP_LOCATION;
            case PHONE:
                return GROUP_PHONE;
            case MICROPHONE:
                return GROUP_MICROPHONE;
            case CAMERA:
                return GROUP_CAMERA;
            default:
                return new String[]{permission};
        }

    }


    /**
     * Obtain the permission to be applied in the request permission and its corresponding use information
     *
     * @param permissions
     * @return key:Permission name, which can be a single permission or permission group name. value: useful information of the requested permission or permission group
     */
    private static Map<String, String> getPermissionDesMap(@NonNull String... permissions) {

        UILog.e(TAG + " permissions.length: " + permissions.length);
        //Invalid permission group
        if (ObjectUtils.isEmpty(permissions)) {
            UILog.e("Invalid permissions,Please confirm whether the permission is correct or has been declared in the manifest file");
            return null;
        }

        Set<String> deniedPermissions = PermissionManager.getDeniedPermissionGroup(permissions);
        //Invalid permission group, i.e. not in manifest file
        if (null == deniedPermissions) {
            UILog.e("Invalid permissions,Please confirm whether the permission is correct or has been declared in the manifest file");
            return null;
        }

        Map<String, String> map = new HashMap<String, String>();
        for (String permission : deniedPermissions) {
            String remindMsg = PermissionManager.getPermissionDes(permission);
            map.put(permission, remindMsg);
        }

        return map;

    }

    /**
     * Get failed permission list
     *
     * @param permissions
     * @return
     */
    private static Set<String> getDeniedPermissionGroup(String... permissions) {
        //Invalid permission (not in manifest file)
        boolean isInvalid = true;
        LinkedHashSet<String> deniedPermissions = new LinkedHashSet<String>();

        for (int length = permissions.length, i = 0; i < length; ++i) {
            String permission = permissions[i];
            //Permission groups or individual permissions
            String[] groupPermission = PermissionManager.getPermissions(permission);
            for (String aPermission : groupPermission) {
                //Checklist file declaration permissions
                if (!PERMISSIONS.contains(aPermission)) {
                    UILog.e(TAG + "Invalid permissions,Please confirm whether the permission is correct or has been declared in the manifest file:" + aPermission);
                    continue;
                }
                isInvalid = false;
                //Permission failed
                if (!PermissionUtils.isGranted(aPermission)) {
                    UILog.e(TAG + " denied: " + permission);
                    deniedPermissions.add(permission);
                    break;
                }
                UILog.e(TAG + " granted: " + permission);
            }
        }
        //Invalid permission (not in manifest file)
        if (isInvalid) {
            return null;
        }

        return deniedPermissions;
    }

    /**
     * Get the prompt information corresponding to the request permission
     *
     * @param permission
     * @return
     */
    private static String getPermissionDes(String permission) {

        String remindMsg = null;
        switch (permission) {
            case STORAGE:
                remindMsg = StringUtils.getString(R.string.permission_storage_des);
                break;
            case ALBUM:
                remindMsg = StringUtils.getString(R.string.permission_photo_des);
                break;
            case CAMERA:
                remindMsg = StringUtils.getString(R.string.permission_camera_des);
                break;
            case LOCATION:
                remindMsg = StringUtils.getString(R.string.permission_location_des);
                break;
            case MICROPHONE:
                remindMsg = StringUtils.getString(R.string.permission_microphone_des);
                break;
            case CONTACTS:
                remindMsg = StringUtils.getString(R.string.permission_contacts_des);
                break;
            case PHONE:
                remindMsg = StringUtils.getString(R.string.permission_phone_des);
                break;
            case SMS:
                remindMsg = StringUtils.getString(R.string.permission_sms_des);
                break;
            default:
                //Single permission application extension
                break;
        }

        return remindMsg;
    }

    /**
     * Get request permission name
     *
     * @param permission Custom permission group name
     * @return
     */
    private static String getPermissionName(String permission) {

        String name = null;
        switch (permission) {
            case STORAGE:
                name = "storage";
                break;
            case ALBUM:
                name = "album";
                break;
            case CAMERA:
                name = "camera";
                break;
            case LOCATION:
                name = "location";
                break;
            case MICROPHONE:
                name = "Microphone";
                break;
            case CONTACTS:
                name = "mail list";
                break;
            case PHONE:
                name = "Telephone";
                break;
            case SMS:
                name = "short message";
                break;
            default:
                //Single permission application extension
                break;
        }

        return name;
    }

    /**
     * Judge whether permission has been applied
     * Special note:
     * When using PermissionUtils tripartite framework permission application, if you want to call PermissionUtils isGranted(permissionGroup)
     * Ensure that the requested permission or permission group has been declared in the manifest file, otherwise it will always be false
     *
     * @param permissions
     * @return
     */
    public static boolean isGranted(String... permissions) {
        if (ObjectUtils.isEmpty(permissions)) {
            return false;
        }
        Set<String> deniedPermissions = PermissionManager.getDeniedPermissionGroup(permissions);
        if (null == deniedPermissions) return false;

        return ObjectUtils.isEmpty(deniedPermissions);
    }

    /**
     * Open the application details page
     */
    public static void openAppSystemSettings() {
        PermissionUtils.launchAppDetailsSettings();
    }


    /**
     * Custom permission group type
     */
    @StringDef({SMS, MICROPHONE, STORAGE, ALBUM, CONTACTS, PHONE, LOCATION, CAMERA})
    @Retention(RetentionPolicy.SOURCE)
    public @interface PermissionType {
    }

    /**
     * Permission application callback
     */
    public interface OnPermissionGrantCallback {
        void onGranted();

        void onDenied();
    }

}

The UILog and AppDialogManager should be replaced by their own log and dialog. They will not be posted here. Just deal with the permission group, the permissions in the permission group and the corresponding description information of the permission group. Just increase or decrease the permissions.

Topics: Java Android