前段时间写过一篇文章《Android 6.0动态权限大总结》,参考了网上众多博客文章的结晶。
在公司项目中应用时,还是被同事质疑了。当然了,是好事。后来在同事修改的基础上,我又进行了优化处理,现在看了是好用多了。下面分享下最后的工具类,和用法。
我就不上传代码了,直接贴给你看。
一、工具类:
DynamicPermissionCompat.java
/**
* @Author: duke
* @DateTime: 2017-06-08 10:51
* @Description: android 6.0 权限适配
*/
public class DynamicPermissionCompat {
private Activity activity;
private String[] permissions;
private int requestCode;
private OnPermissionListener listener;
private DynamicPermissionCompat(Activity activity) {
this.activity = activity;
}
/**
* 获取实例对象(非单例)
*
* @param activity
* @param permissionManager 此参数的目的是为了避免重复创建
* @return
*/
public static DynamicPermissionCompat getInstance(Activity activity, DynamicPermissionCompat permissionManager) {
if (permissionManager != null) {
return permissionManager;
}
if (activity == null) {
throw new IllegalArgumentException("activity not allow null");
}
return new DynamicPermissionCompat(activity);
}
public DynamicPermissionCompat setRequestCode(int requestCode) {
this.requestCode = requestCode;
return this;
}
public DynamicPermissionCompat setPermissions(String... permissions) {
this.permissions = permissions;
return this;
}
public DynamicPermissionCompat setOnPermissionListener(OnPermissionListener listener) {
this.listener = listener;
return this;
}
/**
* 开始申请权限
*/
public void start() {
if (!isNeedAdapt()) {
succeed();
} else if (permissions == null) {
failed(new ArrayList<String>());
} else {
String[] deniedPermissions = getDeniedPermissions(activity, permissions);
if (deniedPermissions.length > 0) {
//has没有授权
ActivityCompat.requestPermissions(activity, deniedPermissions, requestCode);
} else {
//全部都已经授权过了
succeed();
}
}
}
/**
* 是否需要6.0权限适配
*
* @return
*/
private static boolean isNeedAdapt() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
/**
* 向用户申请打开系统设置和浮框权限
*
* @param activity
* @return true:申请成功,false:申请失败
*/
@TargetApi(Build.VERSION_CODES.M)
public static boolean adaptWriteSettingAndOverlay(Activity activity) {
if (activity == null) {
//失败,不可以继续执行
return false;
}
if (!isNeedAdapt()) {
//不需要适配,确定可以继续走
return true;
}
if (Settings.canDrawOverlays(activity) && Settings.System.canWrite(activity)) {
//已经适配,确定可以继续走
return true;
}
if (!Settings.canDrawOverlays(activity)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + activity.getPackageName()));
activity.startActivity(intent);
}
if (!Settings.System.canWrite(activity)) {
Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + activity.getPackageName()));
activity.startActivity(intentWrite);
}
activity.finish();
android.os.Process.killProcess(android.os.Process.myPid());
//不确定是否打开权限,不可以继续执行,等待下次启动app来判断
return false;
}
/**
* 是否需要申请权限
*
* @param context
* @param permissions
* @return
*/
public static boolean isNeedRequestGrantPermission(Context context, String... permissions) {
if (context == null
|| permissions == null
|| permissions.length == 0
|| !isNeedAdapt()
|| getDeniedPermissions(context, permissions).length == 0) {
return false;
}
return true;
}
/**
* 获取未授权列表
*
* @return
*/
private static String[] getDeniedPermissions(Context context, String... permissions) {
if (context == null || permissions == null || permissions.length == 0) {
return new String[]{};
}
ArrayList<String> deniedList = new ArrayList<>(1);
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
deniedList.add(permission);
}
}
return deniedList.toArray(new String[deniedList.size()]);
}
/**
* 向用户弹出解释对话框 <br/>
* *******************************************************************************
* ** 应用安装后第一次访问,直接返回false; **
* ** 第一次请求权限时用户拒绝了,下一次返回 true, **
* ** 这时候可以显示一些为什么需要这个权限的说明; **
* ** 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时,返回 false; **
* ** 设备的系统设置中禁止当前应用获取这个权限的授权,返回false; **
* ** 注意:第二次请求权限时,才会有“不再提醒”的选项, **
* ** 如果用户一直拒绝,并没有选择“不再提醒”的选项, **
* ** 下次请求权限时,会继续有“不再提醒”的选项,并且会一直返回true **
* *******************************************************************************
*
* @param permissions 需要提示解释的权限申请
* @return 需要提示:true,不需要:false
*/
private static boolean shouldShowRationalePermissions(Activity activity, ArrayList<String> permissions) {
if (!isNeedAdapt()) {
return false;
}
if (activity == null || permissions == null || permissions.size() == 0) {
return false;
}
for (String permission : permissions) {
boolean rationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
if (rationale) {
return true;
}
}
return false;
}
/**
* 权限授权成功
*/
private void succeed() {
if (listener != null) {
listener.success(requestCode);
}
}
/**
* 用户拒绝了某个或者某些权限
*
* @param deniedList
*/
private void failed(ArrayList<String> deniedList) {
/**
* shouldShowRationalePermissions(activity, deniedList):
* true:是第一次拒绝 ~ 勾选不再提醒之前。
* false:为拒绝 ~ 勾选不再提醒之后。
*/
if (!shouldShowRationalePermissions(activity, deniedList)) {
if (listener != null) {
listener.alwaysDenied(requestCode, deniedList);
}
} else {
if (listener != null) {
listener.failAndTipUser(requestCode, deniedList);
}
}
}
/**
* 授权的回调
*
* @param permissions
* @param grantResults
*/
public void onRequestPermissionsResult(String[] permissions, int[] grantResults) {
if (permissions.length != grantResults.length) {
failed(null);
return;
}
ArrayList<String> deniedList = new ArrayList<>(permissions.length);
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
deniedList.add(permissions[i]);
}
}
deniedList.trimToSize();
if (deniedList.isEmpty()) {
succeed();
} else {
failed(deniedList);
}
}
/**
* 如果用户勾选了不再提示,打开APP设置,引导用户手动开启
*/
public static void showSetting(Activity activity, int requestCode) {
if (activity == null) {
return;
}
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent, requestCode);
}
public static abstract class OnPermissionListener {
/**
* 全部授权成功
*
* @param requestCode
*/
public abstract void success(int requestCode);
/**
* 有权限被拒绝,提示用户
*
* @param requestCode
* @param deniedPermissions 被拒绝的权限集合
*/
public void failAndTipUser(int requestCode, List<String> deniedPermissions) {
}
/**
* 用户拒绝权限,并勾选了下次不再提醒
*
* @param requestCode
* @param deniedPermissions 被拒绝的权限集合
*/
public void alwaysDenied(int requestCode, List<String> deniedPermissions) {
}
}
}
核心东西没变,只是封装优化了。看下面的用法吧:
permissionManager = DynamicPermissionCompat.getInstance(this, permissionManager);
创建工具类对象,建议有一个Activity中创建一个对象就好。
permissionManager.setOnPermissionListener(permissionListener)
.setPermissions(permissions)
.setRequestCode(requestCode)
.start();
以构建者模式,调起授权的代码。你不用判断是否是6.0以上版本,里面的逻辑中判断了,向下兼容的。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (permissionManager != null) {
permissionManager.onRequestPermissionsResult(permissions, grantResults);
}
}
在当前Activity或者bese Activity中重写这个函数,接受回调处理。
授权回调:
private DynamicPermissionCompat.OnPermissionListener permissionListener = new DynamicPermissionCompat.OnPermissionListener() {
@Override
public void success(int requestCode) {
boolean is1 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "other.apk", new File(otherFilePath));
boolean is2 = ApkManager.copyAssetsFileTo(ApkInstallActivity.this, "me.apk", new File(myFilePath));
// ApkManager.installApk(ApkInstallActivity.this, new File(otherFilePath));
}
@Override
public void failAndTipUser(int requestCode, List<String> deniedPermissions) {
Toast.makeText(ApkInstallActivity.this, "用户拒绝了", Toast.LENGTH_SHORT).show();
}
@Override
public void alwaysDenied(int requestCode, List<String> deniedPermissions) {
Toast.makeText(ApkInstallActivity.this, "用户勾选了\"不再提醒\"", Toast.LENGTH_SHORT).show();
}
};
瞧瞧回调函数,里面有三个方法:授权成功、用户拒绝、用户勾选了不再提醒。
+++++++++完了,就这些。与上一版本相比,是不是简单多了?+++++++++++++++++++++++++++++++++++++++++++++++++++++
下面再说说6.0权限的另一部分吧。
其实android6.0动态权限不止普通权限、动态权限两类,还有一个系统等级的权限,这个是没办法动态申请的,但是用户不打开,程序的系统设置、系统浮窗功能就是用不了,比喻修改屏幕亮度、音量等等。如果没有权限的话,你会在控制台上看到TYPE_SYSTEM_ALERT 或 WRITE_SETTINGS等权限提示。
下面提供一个封装的工具类:
SystemSettings.java
/**
* @Author: duke
* @DateTime: 2017-06-16 14:25
* @Description: 系统设置工具类 <br/>
* 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/>
* Settings.System -- WRITE_SETTINGS <br/>
* Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
* Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
*/
public class SystemSettings {
public static final String TAG = SystemSettings.class.getSimpleName();
public static boolean isNeedCompatPermission() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
/**
* 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限
*
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static boolean hasWritePermission(Context context) {
if (context == null) {
return false;
}
if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {
return true;
}
return false;
}
/**
* 判断是否有 设置系统浮窗 权限
*
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static boolean hasOverlaysPermission(Context context) {
if (context == null) {
return false;
}
if (!isNeedCompatPermission() || Settings.canDrawOverlays(context)) {
return true;
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public static void guideUserWritePermission(Context context) {
if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {
return;
}
Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + context.getPackageName()));
context.startActivity(intentWrite);
}
@TargetApi(Build.VERSION_CODES.M)
public static void guideUserOverlaysPermission(Context context) {
if (context == null || !isNeedCompatPermission() || hasOverlaysPermission(context)) {
return;
}
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
}
public static String getAndroidId(Context context) {
if (context == null) {
return "";
}
try {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.getAndroidId() is exception of" + e.getMessage());
e.printStackTrace();
}
return "";
}
public static int getAccelerometerRotation(Context context) {
if (context == null) {
return 0;
}
try {
return Settings.System.getInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.getAccelerometerRotation() is exception of" + e.getMessage());
e.printStackTrace();
}
return 0;
}
public static void putAccelerometerRotation(Context context, int val) {
if (context == null) {
return;
}
if (!hasWritePermission(context)) {
// TODO: 2017/6/16 需确定提示文本
Toast.makeText(context, "暂无屏幕自动切换的权限", Toast.LENGTH_LONG).show();
guideUserWritePermission(context);
return;
}
try {
Settings.System.putInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, val);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.putAccelerometerRotation() is exception of" + e.getMessage());
e.printStackTrace();
}
}
//使用的地方已被注释
public static int getScreenBrightness(Context context) {
if (context == null) {
return 0;
}
try {
return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.getScreenBrightness() is exception of" + e.getMessage());
e.printStackTrace();
}
return 0;
}
//使用的地方已被注释
public static void putScreenBrightness(Context context, int val) {
if (context == null) {
return;
}
if (!hasWritePermission(context)) {
// TODO: 2017/6/16 需确定提示文本
Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();
guideUserWritePermission(context);
return;
}
try {
Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());
e.printStackTrace();
}
}
//使用的地方已被注释
public static int getScreenBrightnessMode(Context context) {
if (context == null) {
return 0;
}
try {
return Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.getScreenBrightnessMode() is exception of" + e.getMessage());
e.printStackTrace();
}
return 0;
}
//使用的地方已被注释
public static void putScreenBrightnessMode(Context context, int val) {
if (context == null) {
return;
}
if (!hasWritePermission(context)) {
// TODO: 2017/6/16 需确定提示文本
Toast.makeText(context, "暂无设置系统亮度模式的权限", Toast.LENGTH_LONG).show();
guideUserWritePermission(context);
return;
}
try {
Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, val);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.putScreenBrightnessMode() is exception of" + e.getMessage());
e.printStackTrace();
}
}
}
拿WRITE_SETTINGS权限来,下面大概讲解下核心部分。
public static boolean isNeedCompatPermission() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
这是6.0的权限,也需要版本判断。
/**
* 判断是否有 WRITE_SETTINGS or WRITE_SECURE_SETTINGS 权限
*
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static boolean hasWritePermission(Context context) {
if (context == null) {
return false;
}
if (!isNeedCompatPermission() || Settings.System.canWrite(context)) {
return true;
}
return false;
判断有没有修改系统设置权限的核心方法:Settings.System.canWrite(context)
如果没有呢?
@TargetApi(Build.VERSION_CODES.M)
public static void guideUserWritePermission(Context context) {
if (context == null || !isNeedCompatPermission() || hasWritePermission(context)) {
return;
}
Intent intentWrite = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + context.getPackageName()));
context.startActivity(intentWrite);
}
通过intent,引导用户去系统设置界面了。
/**
* 下面三张系统表,读取时都不需要权限,写入时分别需要的权限如下 <br/>
* Settings.System -- WRITE_SETTINGS <br/>
* Settings.Global -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
* Settings.Secure -- WRITE_SETTINGS or WRITE_SECURE_SETTINGS <br/>
*/
WRITE_SETTINGS权限,其实就是对Settings下的System,Global,Secure的三张表修改的权限。读取不需要权限。
故,像所有Settings.xxx.putxxx()这类方法,都需要权限。建议把所有对Settings下三张表进行读写的操作都集中到这个类里面,统一进行判断处理。例如:
public static void putScreenBrightness(Context context, int val) {
if (context == null) {
return;
}
if (!hasWritePermission(context)) {
// TODO: 2017/6/16 需确定提示文本
Toast.makeText(context, "暂无设置系统亮度的权限", Toast.LENGTH_LONG).show();
guideUserWritePermission(context);
return;
}
try {
Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, val);
} catch (Exception e) {
Log.e(TAG, "SystemSettings.putScreenBrightness() is exception of" + e.getMessage());
e.printStackTrace();
}
}
还有一个系统浮窗的权限,正在整理中,后续再发布。