preface

Now to talk about Android 6.0 runtime permission adaptation, can be said to be very outdated, but why write? First, I tried the current top open source projects on GitHub. They were really great, but the ease of use was still not satisfactory, so I came up with the idea of my own. The second is to look at the current domestic mainstream applications, found that many have not adapted Android 6.0, so I think this article has its significance.

use

Since ease of use is mentioned above, let’s take a look at using methods that are invoked where permissions need to be requested

PermissionReq.with(this) // Activity or Fragment .permissions(Manifest.permission.CAMERA, The Manifest. Permission. WRITE_EXTERNAL_STORAGE) / / need to apply for the permissions. The result (new PermissionReq.Result() {// Request result callback @override public voidonGranted() {// Application is successful //do something
            }
            @Override
            public void onDenied() {// Application failed //do something
            }
        })
        .request();
Copy the code

Add the following code to the Activity base class and Fragment base class

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    PermissionReq.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
Copy the code

API design uses a more popular streaming call, I do not know what you see, I think it is relatively simple to use, and does not break the original code structure. At this point, I would like to add that many open source frameworks now use annotations to call back and forth application results. I feel that this approach reduces the code hierarchy, but makes it less readable, and may break the original code structure.

There’s one more thing here that I don’t know if you’ve noticed, but we didn’t use RequestCode, so where does RequestCode go, and I’ll tell you about that in the next part of the story. This is one of my favorite things, there is no need to define a RequestCode for each permission request, and no need to worry about duplicate requestcodes because PermissionReq already takes care of that for everyone.

The source code parsing

After we look at the usage, let’s look at the internal implementation, let’s look at the process

First we need to check which permissions are registered in the App. If a permission is not registered in the Manifest at all, it will fail

initManifestPermission(activity);
for (String permission : mPermissions) {
    if(! sManifestPermissionSet.contains(permission)) {if(mResult ! = null) { mResult.onDenied(); }return;
    }
}

private static Set<String> sManifestPermissionSet;
private static synchronized void initManifestPermission(Context context) {
    if(sManifestPermissionSet == null) { sManifestPermissionSet = new HashSet<>(); try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); String[] permissions = packageInfo.requestedPermissions; Collections.addAll(sManifestPermissionSet, permissions); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); }}}Copy the code

We added the synchronized modifier for thread safety.

If the required permission is registered in the Manifest, we need to differentiate the system version. If the system version is lower than 26, we will return success. Otherwise, we need to apply for permission

If the system version >= 26, then we start the real application process to check whether the permission to apply is allowed, if it is allowed, then there is no need to apply again

List<String> deniedPermissionList = getDeniedPermissions(activity, mPermissions);
if (deniedPermissionList.isEmpty()) {
    if(mResult ! = null) { mResult.onGranted(); }return;
}

private static List<String> getDeniedPermissions(Context context, String[] permissions) {
    List<String> deniedPermissionList = new ArrayList<>();
    for (String permission : permissions) {
        if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
            deniedPermissionList.add(permission);
        }
    }
    return deniedPermissionList;
}
Copy the code

If not all permissions are allowed, then we need to send a request to the system to generate a RequestCode

int requestCode = genRequestCode();

private static AtomicInteger sRequestCode = new AtomicInteger(0);
private static int genRequestCode() {
    return sRequestCode.incrementAndGet();
}
Copy the code

We use a statically incremented AtomicInteger internally as a RequestCode to ensure that the RequestCode does not duplicate, Using AtomicInteger instead of int directly is for thread-safety reasons

Send the application to the system

String[] deniedPermissions = deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
requestPermissions(mObject, deniedPermissions, requestCode);
sResultArray.put(requestCode, mResult);

@TargetApi(Build.VERSION_CODES.M)
private static void requestPermissions(Object object, String[] permissions, int requestCode) {
    if (object instanceof Activity) {
        ((Activity) object).requestPermissions(permissions, requestCode);
    } else if(object instanceof Fragment) { ((Fragment) object).requestPermissions(permissions, requestCode); }}Copy the code

Distinguish the source during application, and put the Result into Array after application and save it until the application Result arrives

Applicants will be notified when the application results arrive

public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    Result result = sResultArray.get(requestCode);
    if (result == null) {
        return;
    }
    sResultArray.remove(requestCode);
    for (int grantResult : grantResults) {
        if(grantResult ! = PackageManager.PERMISSION_GRANTED) { result.onDenied();return;
        }
    }
    result.onGranted();
}
Copy the code

Here our application permission process has been finished, the source code is finished.

The complete code

I posted the complete code for your convenience

public class PermissionReq {
    private static AtomicInteger sRequestCode = new AtomicInteger(0);
    private static SparseArray<Result> sResultArray = new SparseArray<>();
    private static Set<String> sManifestPermissionSet;

    public interface Result {
        void onGranted();

        void onDenied();
    }

    private Object mObject;
    private String[] mPermissions;
    private Result mResult;

    private PermissionReq(Object object) {
        mObject = object;
    }

    public static PermissionReq with(@NonNull Activity activity) {
        return new PermissionReq(activity);
    }

    public static PermissionReq with(@NonNull Fragment fragment) {
        return new PermissionReq(fragment);
    }

    public PermissionReq permissions(@NonNull String... permissions) {
        mPermissions = permissions;
        return this;
    }

    public PermissionReq result(@Nullable Result result) {
        mResult = result;
        return this;
    }

    public void request() {
        Activity activity = getActivity(mObject);
        if (activity == null) {
            throw new IllegalArgumentException(mObject.getClass().getName() + " is not supported");
        }

        initManifestPermission(activity);
        for (String permission : mPermissions) {
            if(! sManifestPermissionSet.contains(permission)) {if(mResult ! = null) { mResult.onDenied(); }return; }}if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            if(mResult ! = null) { mResult.onGranted(); }return;
        }

        List<String> deniedPermissionList = getDeniedPermissions(activity, mPermissions);
        if (deniedPermissionList.isEmpty()) {
            if(mResult ! = null) { mResult.onGranted(); }return;
        }

        int requestCode = genRequestCode();
        String[] deniedPermissions = deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
        requestPermissions(mObject, deniedPermissions, requestCode);
        sResultArray.put(requestCode, mResult);
    }

    public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Result result = sResultArray.get(requestCode);

        if (result == null) {
            return;
        }

        sResultArray.remove(requestCode);

        for (int grantResult : grantResults) {
            if(grantResult ! = PackageManager.PERMISSION_GRANTED) { result.onDenied();return;
            }
        }
        result.onGranted();
    }

    @TargetApi(Build.VERSION_CODES.M)
    private static void requestPermissions(Object object, String[] permissions, int requestCode) {
        if (object instanceof Activity) {
            ((Activity) object).requestPermissions(permissions, requestCode);
        } else if (object instanceof Fragment) {
            ((Fragment) object).requestPermissions(permissions, requestCode);
        }
    }

    private static List<String> getDeniedPermissions(Context context, String[] permissions) {
        List<String> deniedPermissionList = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                deniedPermissionList.add(permission);
            }
        }
        return deniedPermissionList;
    }

    private static synchronized void initManifestPermission(Context context) {
        if (sManifestPermissionSet == null) {
            sManifestPermissionSet = new HashSet<>();
            try {
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
                String[] permissions = packageInfo.requestedPermissions;
                Collections.addAll(sManifestPermissionSet, permissions);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    private static Activity getActivity(Object object) {
        if(object ! = null) {if (object instanceof Activity) {
                return (Activity) object;
            } else if (object instanceof Fragment) {
                return((Fragment) object).getActivity(); }}return null;
    }

    private static int genRequestCode() {
        returnsRequestCode.incrementAndGet(); }}Copy the code

conclusion

This article mainly introduces how to quickly and easily adapt Android 6.0 runtime permissions, although it is late, but still hope to help you. If you find any problems or omissions while reading this article, or if you have different opinions, please feel free to point them out!

The Brief book of Migrating Self 2017.08.31