目录
- 背景
- 危险权限
- 申请权限思路
- 申请权限流程
- 干货
- 后续
一.背景
android23 API新增危险权限校验,需要手动获取:
二.危险权限:
日历数据
android.permission-group.CALENDAR
android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
相机
android.permission-group.CAMERA
android.permission.CAMERA
联系人
android.permission-group.CONTACTS
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
位置
android.permission-group.LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
麦克风
android.permission-group.MICROPHONE
android.permission.RECORD_AUDIO
电话
android.permission-group.PHONE
android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
com.android.voicemail.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
传感器
android.permission-group.SENSORS
android.permission.BODY_SENSORS
短信
android.permission-group.SMS
android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission.READ_CELL_BROADCASTS
存储
android.permission-group.STORAGE
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
三.申请权限思路
Android6.0 运行时权限,相关的方法主要有四个:
1、检查是否授予权限
ContextCompat.checkSelfPermission(mContext, permissions[i])
返回的结果为PackageManager.PERMISSION_GRANTED(0)表示授予权限
2、请求权限
ActivityCompat.requestPermissions(WelcomeActivity.this, permissions, 1);
注:permissions为数组,单个的权限,传new String[]{permission}参数, 1为requestCode,在权限回调中需要使用。
3、请求权限后的回调
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
4、判断用户拒绝权限是是否勾选don't ask again选项,若勾选需要客户手动打开权限
ActivityCompat.shouldShowRequestPermissionRationale(WelcomeActivity.this, permissions[i])
注:ActivityCompat.shouldShowRequestPermissionRationale(activity,permission) 这个方法是在用户拒绝权限后返回true。也就是说:用户第一次点击一个需要权限的地方,该方法返回false(因为用户没拒绝~),当用户拒绝掉该权限,下次点击此权限处,该方法会返回true。为true的时候,显示对话框对该权限说明,并让用户选择是否再次申请权限。当用户拒绝权限并勾选don't ask again选项后,会一直返回false,并且 ActivityCompat.requestPermissions 不会弹出对话框,系统直接deny,并执行 onRequestPermissionsResult 方法
5、总结
①、检查需要申请的权限状态,将未授权的单独保存到集合中;
②、集合为空,进入首页(已全都授予权限),不为空,请求权限;
③、当用户拒绝时,判断是否勾选don't ask again,未勾选,重新申请权限,已勾选,跳过欢迎界面,进入主界面。
四.申请权限流程
A、在实际项目中,为了开发的方便,在欢迎界面,将常用的权限申请一次性申请,一般申请写存储卡、定位等权限。
申请权限分为两类: ① 直接弹出权限框,允许或禁止操作 ② 到手机具体某个页面设置。
B、在某个具体操作的时候(如:悬浮窗权限)再单独进行权限判断。
C、具体操作权限申请流程:
①、请求权限;
②、在请求回调中,当用户拒绝时,需判断是否勾选don't ask again,
未勾选,重新申请权限,
已勾选,跳转到设置界面。
五、干货
用户是否禁止权限
private boolean mShowRequestPermission = true;
申请权限 -- 进入登录页面时申请权限
private void init_permission() {
if (getSdkVersionSix()) {
String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE, Manifest.permission.RECORD_AUDIO};
List<String> mPermissionList = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(LogInAct.this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
mPermissionList.add(permissions[i]);
}
}
if (mPermissionList.isEmpty()) {// 全部允许
mShowRequestPermission = true;
} else {//存在未允许的权限
String[] permissionsArr = mPermissionList.toArray(new String[mPermissionList.size()]);
ActivityCompat.requestPermissions(LogInAct.this, permissionsArr, 101);
}
}
}
权限提示框 ,如果点击禁止且不勾选禁止访问,则无线循环弹出申请框,哈哈,就是这么任性。
如果全部允许则正常操作。如果全部禁止且禁止访问,点击登录按钮时再校验一遍,
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 101:
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
//判断是否勾选禁止后不再询问
boolean showRequestPermission = ActivityCompat.shouldShowRequestPermissionRationale(LogInAct.this, permissions[i]);
if (showRequestPermission) {
init_permission();
return;
} else { // false 被禁止了,不在访问
mShowRequestPermission = false;//已经禁止了
}
}
}
break;
}
}
点击登录按钮时 ,获取申请权限的结果 并且这里动态申请了“悬浮窗”的权限
1. 如何小于6.0 直接登陆
2. 申请悬浮窗权限,如果没有,直接跳转到具体页面进行设置,完成后在onActivityResult 中判断是否允许该权限
如果允许再次动态申请一次权限,上面如果点击了全部禁止且勾选了禁止访问请调用 getRequestPermission()方法,
自动跳转到应用程序列表中让用户手动设置权限
if (getSdkVersionSix()) {
if (Settings.canDrawOverlays(LogInAct.this)) {
if (getRequestPermission().equals("1")) { // 如果等于1则登录
init_IntentLogIn(getAccount(), getPwd());
}
} else {
Toast.makeText(this, R.string.login_canDrawOverlays, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivityForResult(intent, 2);
}
} else {
init_IntentLogIn(getAccount(), getPwd()); // 6.0以下直接登录
}
判断SDK是否在6.0以上
public boolean getSdkVersionSix() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
处理申请的悬浮窗和其他权限
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2) {
if (Settings.canDrawOverlays(LogInAct.this)) {
init_permission();
} else {
Toast.makeText(this, R.string.login_canDrawOverlays, Toast.LENGTH_SHORT).show();
}
} else if (requestCode == 3) {
init_permission();
}
}
申请权限后的操作
public String getRequestPermission() {
if (mShowRequestPermission) {
return "1";
} else {// 被禁止后提示用户必须到设置中授权,跳转到应用程序列表页面
Toast.makeText(this, R.string.login_permission, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_APPLICATION_SETTINGS);
startActivityForResult(intent, 3);
return "-1";
}
}
六、后续
工具类已出 ,代码传送门
七、远程依赖
1.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.stf-android:Permissions:-SNAPSHOT'
}
2.maven
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.stf-android</groupId>
<artifactId>Permissions</artifactId>
<version>-SNAPSHOT</version>
</dependency>