封装的权限选择库 https://github.com/yuhongwen-coder/PermissionDemo
背景:
1: android权限的申请,可以在清单文件声明和注册,也可以通过 java 文件动态的注册,最终的是否能使用将控制权交给用户
在Android6.0之前只需在AndroidManifest.xml文件写明权限即可。但是在Android6.0之后也就是SDK>=23的时候,一些隐私权限需要动态申请,而且在用户同意授权之后App才能拥有该权限。(比如 读写sdcard权限是一个危险权限,就需要动态申请)虽然不难,但写下来代码工作量也不少,而且几乎每个应用都需要用到。所以一个简单简洁优雅的处理方式是非常重要的。
1: 动态权限的核心工作流程:
checkSelfPermission检查是否已被授予——>requestPermissions申请权限——>自动回调onRequestPermissionsResult——shouldShowRequestPermissionRationale。
无论什么框架变出花来都离不开这个基本的流程。
2: 一般ux提醒方式
如果在每个用到权限的地方去申请,有点繁琐,所以做法是直接在每个Activity界面显示前就把该界面所需的权限全部申请允许了,
再进入正常的流程。如果有权限未被允许,开发者可以在接口中自己处理,可以直接提示并退出,或者提示权限的作用后重新发起权限申请。
3:系统api
ContextCompat.checkSelfPermission(): 检查权限是否已经授权
ActivityCompat.requestPermissions():动态申请权限,并弹出对话框。
onRequestPermissionsResult():在activity里面重写该方法该方法是权限申请之后的回调方法。
shouldShowRequestPermissionRationale() :当应用首次申请权限时,如果用户点击拒绝,下次再申请权限,Android允许你提示用户,你为什么需要这个权限,
更好引导用户是否授权,其中在Android原生系统中:如果应用之前请求过此权限但用户拒绝了请求,此方法将返回true;
如果用户在过去拒绝了权限请求且在权限请求系统对话框中选择了 Don't ask again 选项将返回 false;
如果第一次申请权限也返回false;如果设备规范禁止应用具有该权限,此方法也会返回 false,返回false则不在显示提示对话框,返回true则会显示对话框。
public class DialogUtils {
/**
* 创建一个选择对话框
*
* @param context
* @param pContent 提示消息
* @param dialogClickListener 点击监听
* @return
*/
public static Dialog showSelectDialog(Context context, String title, String pContent, String pLeftBtnStr,
String pRightBtnStr,
final DialogClickListener dialogClickListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog dialog = builder.setTitle(title)
.setMessage(pContent)
.setPositiveButton(pRightBtnStr, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialogClickListener.confirm();
dialog.dismiss();
}
})
.setNegativeButton(pLeftBtnStr, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialogClickListener.cancel();
dialog.dismiss();
}
})
.create();
return dialog;
}
public interface DialogClickListener {
public abstract void confirm();
public abstract void cancel();
}
}
package com.application.permissiondemo;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class PermissionActivity extends AppCompatActivity implements View.OnClickListener,PermissionInterface{
private TextView callPhone;
private TextView openCarmera;
private int requestCode = 10000;
private PermissonHelper mPermissionHelper;
@Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.permission_layout);
callPhone = findViewById(R.id.call_telephone);
openCarmera = findViewById(R.id.open_carmera);
callPhone.setOnClickListener(this);
openCarmera.setOnClickListener(this);
//初始化
mPermissionHelper = new PermissonHelper(this, this);
//发起权限申请
mPermissionHelper.requestPermissions(new String[]{Manifest.permission.CALL_PHONE,
Manifest.permission.CAMERA});
}
//点击监听
@Override
public void onClick (View view){
switch (view.getId()) {
case R.id.call_telephone://打电话
requestCode = 0;
mPermissionHelper.requestPermissions(new String[]{Manifest.permission.CALL_PHONE});
break;
case R.id.open_carmera://打开相机
requestCode = 1;
mPermissionHelper.requestPermissions(new String[]{Manifest.permission.CAMERA});
break;
}
}
/**
* 可设置请求权限请求码
*/
@Override
public int getPermissionsRequestCode () {
//设置权限请求requestCode,只有不跟onRequestPermissionsResult方法中的其他请求码冲突即可。
return requestCode;
}
/**
* 请求权限成功回调
*/
@Override
public void requestPermissionsSuccess () {
//权限请求用户已经全部允许
if (requestCode == 10000) {
} else if (requestCode == 0) {
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:" + 10086);
intent.setData(data);
startActivity(intent);
} else if (requestCode == 1) {
Intent intent1 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent1, 0);
}
}
/**
* 请求权限失败回调
*/
@TargetApi(Build.VERSION_CODES.M)
@Override
public void requestPermissionsFail () {
//权限请求不被用户允许。可以提示并退出或者提示权限的用途并重新发起权限申请。
if (requestCode == 10000) {
} else if (requestCode == 0) {
//如果拒绝授予权限,且勾选了再也不提醒
if (!shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)) {
DialogUtils.showSelectDialog(this, "说明", "需要使用电话权限,进行电话测试", "取消", "确定", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
//用于在用户勾选“不再提示”并且拒绝时,再次提示用户
DialogUtils.showSelectDialog(PermissionActivity.this, "电话权限不可用", "请在-应用设置-权限中,允许APP使用电话权限来测试", "取消", "立即开启", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
goToAppSetting();
}
@Override
public void cancel() {
}
}).show();
}
@Override
public void cancel() {
}
}).show();
} else {
DialogUtils.showSelectDialog(PermissionActivity.this, "电话权限不可用", "请在-应用设置-权限中,允许APP使用电话权限来测试", "取消", "立即开启", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
goToAppSetting();
}
@Override
public void cancel() {
}
}).show();
}
} else if (requestCode == 1) {
//如果拒绝授予权限,且勾选了再也不提醒
if (!shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)) {
DialogUtils.showSelectDialog(this, "说明", "需要使用相机权限,进行相机测试", "取消", "确定", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
//用于在用户勾选“不再提示”并且拒绝时,再次提示用户
DialogUtils.showSelectDialog(PermissionActivity.this, "相机权限不可用", "请在-应用设置-权限中,允许APP使用相机权限来测试", "取消", "立即开启", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
goToAppSetting();
}
@Override
public void cancel() {
}
}).show();
}
@Override
public void cancel() {
}
}).show();
} else {
DialogUtils.showSelectDialog(PermissionActivity.this, "相机权限不可用", "请在-应用设置-权限中,允许APP使用相机权限来测试", "取消", "立即开启", new DialogUtils.DialogClickListener() {
@Override
public void confirm() {
goToAppSetting();
}
@Override
public void cancel() {
}
}).show();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults){
if (mPermissionHelper.requestPermissionsResult(requestCode, permissions, grantResults)) {
//权限请求结果,并已经处理了该回调
return;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* 打开Setting页面提示用户设置权限
*/
private void goToAppSetting() {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, 123);
}
}
package com.application.permissiondemo;
public interface PermissionInterface {
/**
* 可得到请求权限请求码
*/
int getPermissionsRequestCode();
/**
* 请求权限成功回调
*/
void requestPermissionsSuccess();
/**
* 请求权限失败回调
*/
void requestPermissionsFail();
}
package com.application.permissiondemo;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import java.util.ArrayList;
public class PermissionUtil {
/**
* 弹出对话框请求权限
* @param activity
* @param permissions
* @param requestCode
*/
public static void requestPermissions(Activity activity, String[] permissions, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.requestPermissions(permissions, requestCode);
}
}
/**
* 返回缺失的权限
* @param context
* @param permissions
* @return 返回缺少的权限,null 意味着没有缺少权限
*/
public static String[] getDeniedPermissions(Context context, String[] permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ArrayList<String> deniedPermissionList = new ArrayList<>();
for (String permission : permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
deniedPermissionList.add(permission);
}
} else {
return null;
}
}
int size = deniedPermissionList.size();
if (size > 0) {
return deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
}
}
return null;
}
}
package com.application.permissiondemo;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
/**
* 权限帮助类
*/
public class PermissonHelper {
private Activity mActivity;
private PermissionInterface mPermissionInterface;
public PermissonHelper(@NonNull Activity activity, @NonNull PermissionInterface permissionInterface) {
mActivity = activity;
mPermissionInterface = permissionInterface;
}
/**
* 开始请求权限。
* 方法内部已经对Android M 或以上版本进行了判断,外部使用不再需要重复判断。
* 如果设备还不是M或以上版本,则也会回调到requestPermissionsSuccess方法。
*/
public void requestPermissions(String[] permissions) {
String[] deniedPermissions = PermissionUtil.getDeniedPermissions(mActivity, permissions);
if (deniedPermissions != null && deniedPermissions.length > 0) {
PermissionUtil.requestPermissions(mActivity, deniedPermissions, mPermissionInterface.getPermissionsRequestCode());
} else {
mPermissionInterface.requestPermissionsSuccess();
}
}
/**
* 在Activity中的onRequestPermissionsResult中调用
* @param requestCode
* @param permissions
* @param grantResults
* @return true 代表对该requestCode感兴趣,并已经处理掉了。false 对该requestCode不感兴趣,不处理。
*/
public boolean requestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == mPermissionInterface.getPermissionsRequestCode()) {
boolean isAllGranted = true;//是否全部权限已授权
for(int result : grantResults){
if(result == PackageManager.PERMISSION_DENIED){
isAllGranted = false;
break;
}
}
if(isAllGranted){
//已全部授权
mPermissionInterface.requestPermissionsSuccess();
}else{
//权限有缺失
mPermissionInterface.requestPermissionsFail();
}
return true;
}
return false;
}
}
填坑之路
1: 如果在 mainfest 中配置了下面两项权限
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
那么在 app 启动的时候,由于app 会静态检测mainfest.xml 文件,所以在app启动的时候,就会弹出 :电话权限 和 相机权限
2: 如果在 Activity 中 的 onCreate() 函数,申请这两项权限,但是又不能申请成功
3 :如果在控件的 点击的时候,申请权限又是ok 的
4 :android7.0 以上:拍照崩溃问题