Foreword

AndroidUtilCode 1.11.0 is the latest version of AndroidUtilCode, which has been used for a long time by a friend who asked if there was a privilege utility class in AndroidUtilCode. AndroidUtilCode will no longer have to rely on a third-party library for dynamic authorization. Here’s how it works.

Functions

  • Compatible with all Android versions, including Android 8.0
  • You can apply for permission anywhere, not only for activities and fragments
  • Multiple permission applications are supported
  • Use chain call, a sentence to solve permission application

Achieve

Let’s start with the implementation. For more information on runtime permissions, see portal. For the list of dangerous permissions, I encapsulate the dangerous permissions constant class PermissionConstants.java as follows:

import android.Manifest;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.support.annotation.StringDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


/** * 
 * author: Blankj * blog: http://blankj.com * time: 2017/12/29 * desc: Permissions related constants * 

*/
@SuppressLint("InlinedApi")
public final class PermissionConstants {

public static final String CALENDAR = Manifest.permission_group.CALENDAR;
public static final String CAMERA = Manifest.permission_group.CAMERA;
public static final String CONTACTS = Manifest.permission_group.CONTACTS;
public static final String LOCATION = Manifest.permission_group.LOCATION;
public static final String MICROPHONE = Manifest.permission_group.MICROPHONE;
public static final String PHONE = Manifest.permission_group.PHONE;
public static final String SENSORS = Manifest.permission_group.SENSORS;
public static final String SMS = Manifest.permission_group.SMS;
public static final String STORAGE = Manifest.permission_group.STORAGE;

private static final String[] GROUP_CALENDAR = {
permission.READ_CALENDAR, permission.WRITE_CALENDAR
};
private static final String[] GROUP_CAMERA = {
permission.CAMERA
};
private static final String[] GROUP_CONTACTS = {
permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS
};
private static final String[] GROUP_LOCATION = {
permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION
};
private static final String[] GROUP_MICROPHONE = {
permission.RECORD_AUDIO
};
private static final String[] GROUP_PHONE = {
permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE,
permission.ANSWER_PHONE_CALLS, permission.READ_CALL_LOG, permission.WRITE_CALL_LOG,
permission.ADD_VOICEMAIL, permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS
};
private static final String[] GROUP_SENSORS = {
permission.BODY_SENSORS
};
private static final String[] GROUP_SMS = {
permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS,
permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS,
};
private static final String[] GROUP_STORAGE = {
permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE
};

@StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,})
@Retention(RetentionPolicy.SOURCE)
public @interface Permission {
}

public static String[] getPermissions(@Permission final String permission) {
switch (permission) {
case CALENDAR:
return GROUP_CALENDAR;
case CAMERA:
return GROUP_CAMERA;
case CONTACTS:
return GROUP_CONTACTS;
case LOCATION:
return GROUP_LOCATION;
case MICROPHONE:
return GROUP_MICROPHONE;
case PHONE:
return GROUP_PHONE;
case SENSORS:
return GROUP_SENSORS;
case SMS:
return GROUP_SMS;
case STORAGE:
return GROUP_STORAGE;
}
return newString[]{permission}; }}Copy the code

In order to adapt to Android 8.0, WHEN APPLYING for permissions, I will apply for all permissions of the same group used in the listing file at one time, and the relevant codes are as follows:

private static final List<String> PERMISSIONS = getPermissions();

/** * Obtain application permission **@returnList of permissions in the manifest file */
public static List<String> getPermissions(a) {
    return getPermissions(Utils.getApp().getPackageName());
}

/** * Obtain application permission **@paramPackageName package name *@returnList of permissions in the manifest file */
public static List<String> getPermissions(final String packageName) {
    PackageManager pm = Utils.getApp().getPackageManager();
    try {
        return Arrays.asList(
                pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
                        .requestedPermissions
        );
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
        returnCollections.emptyList(); }}/** * Set request permission **@paramPermissions Permissions to be requested *@return {@link PermissionUtils}
 */
public static PermissionUtils permission(@Permission final String... permissions) {
    return new PermissionUtils(permissions);
}

private PermissionUtils(final String... permissions) {
    mPermissions = new LinkedHashSet<>();
    for (String permission : permissions) {
        for (String aPermission : PermissionConstants.getPermissions(permission)) {
            if (PERMISSIONS.contains(aPermission)) {
                mPermissions.add(aPermission);
            }
        }
    }
    sInstance = this;
}
Copy the code

To allow permission to be applied anywhere, I wrapped PermissionActivity in permissionUtils.java. The source code is as follows:

@RequiresApi(api = Build.VERSION_CODES.M)
public static class PermissionActivity extends Activity {
    public static void start(final Context context) {
        Intent starter = new Intent(context, PermissionActivity.class);
        starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(starter);
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        if(sInstance.mThemeCallback ! =null) {
            sInstance.mThemeCallback.onActivityCreate(this);
        } else {
            Window window = getWindow();
            window.setBackgroundDrawable(null);
            int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            window.getDecorView().setSystemUiVisibility(option);
            window.setStatusBarColor(Color.TRANSPARENT);
        }
        super.onCreate(savedInstanceState);
        if (sInstance.rationale(this)) {
            finish();
            return;
        }
        if(sInstance.mPermissionsRequest ! =null) {
            int size = sInstance.mPermissionsRequest.size();
            requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1); }}@Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        sInstance.onRequestPermissionsResult(this); finish(); }}Copy the code

However, the theme of the PermissionActivity doesn’t necessarily match the theme of your app’s Activity, so I left a callback interface for setting the theme. For example, we can set the Activity to full screen. This can be used to start an Activity without awareness, with the following theme properties:

<style name="ActivityTranslucent">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:activityOpenEnterAnimation">@null</item>
    <item name="android:activityOpenExitAnimation">@null</item>
    <item name="android:activityCloseEnterAnimation">@null</item>
    <item name="android:activityCloseExitAnimation">@null</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>
Copy the code

This should work for a lot of applications.

Of course, if you have a rationale setting, that is, a callback interface for requesting a rationale again after denying permissions, you will enter sinstance.rationale (this), as shown below:

@RequiresApi(api = Build.VERSION_CODES.M)
private boolean rationale(final Activity activity) {
    boolean isRationale = false;
    if(mOnRationaleListener ! =null) {
        for (String permission : mPermissionsRequest) {
            if (activity.shouldShowRequestPermissionRationale(permission)) {
                getPermissionsStatus(activity);
                mOnRationaleListener.rationale(new ShouldRequest() {
                    @Override
                    public void again(boolean again) {
                        if (again) {
                            startPermissionActivity();
                        } else{ requestCallback(); }}}); isRationale =true;
                break;
            }
        }
        mOnRationaleListener = null;
    }
    return isRationale;
}
Copy the code

The logic is that if the rationale callback interface shouldrequest.again (true); , then it will continue to apply for, otherwise it will not apply for, more in the pop-up a prompt dialog box to let the user choose whether to continue to request permission.

Is the initiating and eventually accept the request, and keep the final state to mPermissionsGranted, mPermissionsDenied and mPermissionsDeniedForever, eventually callback callback interface, the code is as follows:

private void getPermissionsStatus(final Activity activity) {
    for (String permission : mPermissionsRequest) {
        if (isGranted(permission)) {
            mPermissionsGranted.add(permission);
        } else {
            mPermissionsDenied.add(permission);
            if(! activity.shouldShowRequestPermissionRationale(permission)) { mPermissionsDeniedForever.add(permission); }}}}private void requestCallback(a) {
    if(mSimpleCallback ! =null) {
        if (mPermissionsRequest.size() == 0
                || mPermissions.size() == mPermissionsGranted.size()) {
            mSimpleCallback.onGranted();
        } else {
            if(! mPermissionsDenied.isEmpty()) { mSimpleCallback.onDenied(); } } mSimpleCallback =null;
    }
    if(mFullCallback ! =null) {
        if (mPermissionsRequest.size() == 0
                || mPermissions.size() == mPermissionsGranted.size()) {
            mFullCallback.onGranted(mPermissionsGranted);
        } else {
            if(! mPermissionsDenied.isEmpty()) { mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied); } } mFullCallback =null;
    }
    mOnRationaleListener = null;
    mThemeCallback = null;
}

private void onRequestPermissionsResult(final Activity activity) {
    getPermissionsStatus(activity);
    requestCallback();
}
Copy the code

Use

For example, if we want to apply for android.permission.READ_CALENDAR permission, we can go to permissionConstants.java to find its group. CALENDAR, and the application is a full-screen application, so we can initiate the request as follows.

PermissionUtils.permission(PermissionConstants.CALENDAR)
        .rationale(new PermissionUtils.OnRationaleListener() {
            @Override
            public void rationale(final ShouldRequest shouldRequest) {
                PermissionHelper.showRationaleDialog(shouldRequest);
            }
        })
        .callback(new PermissionUtils.FullCallback() {
            @Override
            public void onGranted(List<String> permissionsGranted) {
                updateAboutPermission();
            }
            @Override
            public void onDenied(List
       
         permissionsDeniedForever, List
        
          permissionsDenied)
        
        {
                if(! permissionsDeniedForever.isEmpty()) { PermissionHelper.showOpenAppSettingDialog(); } LogUtils.d(permissionsDeniedForever, permissionsDenied); } }) .theme(new PermissionUtils.ThemeCallback() {
            @Override
            public void onActivityCreate(Activity activity) {
                ScreenUtils.setFullScreen(activity);
            }
        })
        .request();
Copy the code

If not, see Demo -> PermissionActivity.java in AndroidUtilCode

Put all permission requests in a helper class, as I do in my AndroidUtilCode demo, and create a Permissionhelper.java. Many permission requests are repeated.

Conclusion

Well, this is the end of the permission tool class introduction, behind such a simple tool class is the hard work of Ben Koch, crazy debug, crazy test to eliminate the memory leak problem, although the journey is very difficult, but finally successfully completed the tool class, finally wait for you.