Android开发动态询问权限总流程
以问相机权限为例。
首先是检查是否有相机权限,如果没有就询问相机权限,如果用户永久禁止权限又怎么处理。
一、引入第三方库:
api 'com.github.tbruyelle:rxpermissions:0.12'
二、检查权限的工具类:
public class PermissionCheckUtil {
private static final String TAG = PermissionCheckUtil.class.getSimpleName();
public PermissionCheckUtil() {
}
public static boolean requestPermissions(Fragment fragment, String[] permissions) {
return requestPermissions((Fragment)fragment, permissions, 0);
}
public static boolean requestPermissions(final Fragment fragment, String[] permissions, int requestCode) {
if (permissions.length != 0 && fragment.getActivity() != null && !fragment.isDetached()) {
List<String> permissionsNotGranted = new ArrayList();
boolean result = false;
String[] var5 = permissions;
int var6 = permissions.length;
for(int var7 = 0; var7 < var6; ++var7) {
String permission = var5[var7];
if ((isFlyme() || Build.VERSION.SDK_INT < 23) && permission.equals("android.permission.RECORD_AUDIO")) {
showPermissionAlert(fragment.getContext(), fragment.getString(R.string.permission_grant_needed) + fragment.getString(R.string.permission_microphone), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (-1 == which) {
fragment.startActivity(new Intent("android.settings.MANAGE_APPLICATIONS_SETTINGS"));
}
}
});
return false;
}
if (!hasPermission(fragment.getActivity(), permission)) {
permissionsNotGranted.add(permission);
}
}
if (permissionsNotGranted.size() > 0) {
int size = permissionsNotGranted.size();
fragment.requestPermissions((String[])permissionsNotGranted.toArray(new String[size]), requestCode);
} else {
result = true;
}
return result;
} else {
return true;
}
}
public static boolean requestPermissions(Activity activity, @NonNull String[] permissions) {
return requestPermissions((Activity)activity, permissions, 0);
}
@TargetApi(23)
public static boolean requestPermissions(Activity activity, @NonNull String[] permissions, int requestCode) {
if (Build.VERSION.SDK_INT < 23) {
return true;
} else if (permissions.length == 0) {
return true;
} else {
List<String> permissionsNotGranted = new ArrayList();
boolean result = false;
String[] var5 = permissions;
int var6 = permissions.length;
for(int var7 = 0; var7 < var6; ++var7) {
String permission = var5[var7];
if (!hasPermission(activity, permission)) {
permissionsNotGranted.add(permission);
}
}
if (permissionsNotGranted.size() > 0) {
int size = permissionsNotGranted.size();
activity.requestPermissions((String[])permissionsNotGranted.toArray(new String[size]), requestCode);
} else {
result = true;
}
return result;
}
}
public static boolean checkPermissions(Context context, @NonNull String[] permissions) {
if (permissions.length != 0) {
String[] var2 = permissions;
int var3 = permissions.length;
for (int var4 = 0; var4 < var3; ++var4) {
String permission = var2[var4];
if ((isFlyme() || Build.VERSION.SDK_INT < 23) && permission.equals("android.permission.RECORD_AUDIO")) {
if (Build.BRAND.toLowerCase().equals("meizu") && Build.MODEL.equals("M1852")) {
return hasPermission(context, permission);
}
if (!hasRecordPermision(context)) {
return false;
}
} else if (!hasPermission(context, permission)) {
return false;
}
}
}
return true;
}
private static boolean isFlyme() {
String osString = "";
try {
Class<?> clz = Class.forName("android.os.SystemProperties");
Method get = clz.getMethod("get", String.class, String.class);
osString = (String)get.invoke(clz, "ro.build.display.id", "");
} catch (Exception var3) {
}
return osString != null && osString.toLowerCase().contains("flyme");
}
private static boolean hasRecordPermision(Context context) {
boolean hasPermission = false;
int bufferSizeInBytes = AudioRecord.getMinBufferSize(44100, 12, 2);
if (bufferSizeInBytes < 0) {
return false;
} else {
AudioRecord audioRecord = null;
try {
audioRecord = new AudioRecord(1, 44100, 12, 2, bufferSizeInBytes);
audioRecord.startRecording();
} catch (Exception var5) {
hasPermission = false;
}
if (audioRecord == null) {
return false;
} else {
if (audioRecord.getRecordingState() == 3) {
audioRecord.stop();
hasPermission = true;
}
audioRecord.release();
return hasPermission;
}
}
}
public static String getNotGrantedPermissionMsg(Context context, String[] permissions, int[] grantResults) {
if (permissions != null && permissions.length != 0) {
StringBuilder sb = new StringBuilder();
sb.append(context.getResources().getString(R.string.permission_grant_needed));
sb.append("(");
try {
for(int i = 0; i < permissions.length; ++i) {
if (grantResults[i] == -1) {
String permissionName = context.getString(context.getResources().getIdentifier("rc_" + permissions[i], "string", context.getPackageName()), new Object[]{0});
sb.append(permissionName);
if (i != permissions.length - 1) {
sb.append(" ");
}
}
}
} catch (Resources.NotFoundException var6) {
return "";
}
sb.append(")");
return sb.toString();
} else {
return "";
}
}
private static String getNotGrantedPermissionMsg(Context context, List<String> permissions) {
if (permissions != null && permissions.size() != 0) {
HashSet permissionsValue = new HashSet();
try {
Iterator var4 = permissions.iterator();
while(var4.hasNext()) {
String permission = (String)var4.next();
String permissionValue = context.getString(context.getResources().getIdentifier("rc_" + permission, "string", context.getPackageName()), new Object[]{0});
permissionsValue.add(permissionValue);
}
} catch (Resources.NotFoundException var7) {
return "";
}
StringBuilder result = new StringBuilder("(");
Iterator var9 = permissionsValue.iterator();
while(var9.hasNext()) {
String value = (String)var9.next();
result.append(value).append(" ");
}
result = new StringBuilder(result.toString().trim() + ")");
return result.toString();
} else {
return "";
}
}
@TargetApi(11)
private static void showPermissionAlert(Context context, String content, DialogInterface.OnClickListener listener) {
(new AlertDialog.Builder(context)).setMessage(content).setPositiveButton(R.string.permission_confirm, listener).setNegativeButton(R.string.cancel, listener).setCancelable(false).create().show();
}
@TargetApi(19)
public static boolean canDrawOverlays(Context context) {
return canDrawOverlays(context, true);
}
@TargetApi(19)
public static boolean canDrawOverlays(final Context context, boolean needOpenPermissionSetting) {
boolean result = true;
if (Build.VERSION.SDK_INT >= 23) {
try {
boolean booleanValue = (Boolean) Settings.class.getDeclaredMethod("canDrawOverlays", Context.class).invoke((Object)null, context);
if (!booleanValue && needOpenPermissionSetting) {
ArrayList<String> permissionList = new ArrayList();
permissionList.add("android.settings.action.MANAGE_OVERLAY_PERMISSION");
showPermissionAlert(context, context.getString(R.string.permission_grant_needed) + getNotGrantedPermissionMsg(context, permissionList), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (-1 == which) {
Intent intent = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION", Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
}
}
});
}
return booleanValue;
} catch (Exception var7) {
return true;
}
} else if (Build.VERSION.SDK_INT < 19) {
return true;
} else {
Object systemService = context.getSystemService("appops");
Method method;
try {
method = Class.forName("android.app.AppOpsManager").getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
} catch (NoSuchMethodException var8) {
method = null;
} catch (ClassNotFoundException var9) {
method = null;
}
if (method != null) {
try {
Integer tmp = (Integer)method.invoke(systemService, 24, context.getApplicationInfo().uid, context.getPackageName());
result = tmp != null && tmp == 0;
} catch (Exception var10) {
}
}
return result;
}
}
private static boolean hasPermission(Context context, String permission) {
String opStr = AppOpsManagerCompat.permissionToOp(permission);
if (opStr == null) {
return true;
} else {
return context == null ? false : context.checkCallingOrSelfPermission(permission) == 0;
}
}
public static void showRequestPermissionFailedAlter(final Context context, String content) {
if (!TextUtils.isEmpty(content)) {
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switch(which) {
case -1:
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
Uri uri = Uri.fromParts("package", context.getPackageName(), (String)null);
intent.setData(uri);
context.startActivity(intent);
case -2:
default:
}
}
};
if (Build.VERSION.SDK_INT >= 21) {
(new AlertDialog.Builder(context, 16974394)).setMessage(content).setPositiveButton(R.string.permission_confirm, listener).setNegativeButton(R.string.cancel, listener).setCancelable(false).create().show();
} else {
(new AlertDialog.Builder(context)).setMessage(content).setPositiveButton(R.string.permission_confirm, listener).setNegativeButton(R.string.cancel, listener).setCancelable(false).create().show();
}
}
}
}
我们用它的checkPermissions方法,不用requestPermissions方法。
三、请求权限工具类
/**
* author:congge
* date: 2019/2/15 17:14
* desc:基本权限集合
*/
object PermissionNewUtils {
const val TAG = "PermissionAppUtils"
const val REQUEST_STORAGE = 1001
const val REQUEST_CAMERA_STORAGE = 1002
const val READ_CALENDAR = "android.permission.READ_CALENDAR"
const val WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"
const val CAMERA = "android.permission.CAMERA"
const val READ_CONTACTS = "android.permission.READ_CONTACTS"
const val WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"
const val GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"
const val ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"
const val ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"
const val RECORD_AUDIO = "android.permission.RECORD_AUDIO"
const val READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"
const val CALL_PHONE = "android.permission.CALL_PHONE"
const val READ_CALL_LOG = "android.permission.READ_CALL_LOG"
const val WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"
const val ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"
const val USE_SIP = "android.permission.USE_SIP"
const val PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"
const val BODY_SENSORS = "android.permission.BODY_SENSORS"
const val SEND_SMS = "android.permission.SEND_SMS"
const val RECEIVE_SMS = "android.permission.RECEIVE_SMS"
const val READ_SMS = "android.permission.READ_SMS"
const val RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"
const val RECEIVE_MMS = "android.permission.RECEIVE_MMS"
const val READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
const val WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"
const val BACKGROUND_LOCATION_PERMISSION = "android.permission.ACCESS_BACKGROUND_LOCATION"
const val MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION"
const val READ_MEDIA_VISUAL_USER_SELECTED = "android.permission.READ_MEDIA_VISUAL_USER_SELECTED"
object Group {
val CAMERA = arrayOf(PermissionNewUtils.CAMERA)
val MICROPHONE = arrayOf(RECORD_AUDIO)
val SENSORS = arrayOf(BODY_SENSORS)
val SMS = arrayOf(
SEND_SMS,
RECEIVE_SMS,
READ_SMS,
RECEIVE_WAP_PUSH,
RECEIVE_MMS
)
val CAMERA_RECORD = arrayOf(
PermissionNewUtils.CAMERA,
RECORD_AUDIO
)
val MANAGE_OVERLAY = arrayOf(
MANAGE_OVERLAY_PERMISSION
)
}
fun getImageMediaPermission(context: Context):Array<String>{
return if (checkSDK34(context)){
arrayOf(
Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
Manifest.permission.READ_MEDIA_IMAGES,
)
} else if (checkSDK33(context)){
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES
)
} else {
arrayOf(
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE
)
}
}
@JvmStatic
fun getImageVideoMediaPermission(context: Context):Array<String>{
return if (checkSDK34(context)){
arrayOf(
Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO
)
} else if (checkSDK33(context)){
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO
)
} else {
arrayOf(
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE
)
}
}
@JvmStatic
fun getVideoMediaPermission(context: Context):Array<String>{
return if (checkSDK34(context)){
arrayOf(
Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
Manifest.permission.READ_MEDIA_VIDEO
)
} else if (checkSDK33(context)){
arrayOf(
Manifest.permission.READ_MEDIA_VIDEO
)
} else {
arrayOf(
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE
)
}
}
@JvmStatic
fun checkSDK33(context: Context): Boolean {
val applicationInfo: ApplicationInfo = context.applicationInfo
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && applicationInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU
}
@JvmStatic
fun checkSDK34(context: Context): Boolean {
val applicationInfo: ApplicationInfo = context.applicationInfo
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && applicationInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
}
fun requestEachPermission(context: Context, permissions:String, call: Consumer<Permission>){
if (context is FragmentActivity ){
RxPermissions(context).requestEach(permissions)
.subscribe(call)
} else if (context is Fragment){
RxPermissions(context).requestEach(permissions)
.subscribe(call)
}
}
/**
* @desc : 请求多个权限时,结果回调只会回调一次
* 注:即返回所有权限的总结果
* 包含了所有请求权限的名字,并且所有权限都成功的话,Permission的granted属性为true,否则为false
* // 没测是否一个永久拒绝,整一个就是永久拒绝
* @author : congge on 2022-01-18 15:18
* 注意:Context不能用MyApplication的
**/
fun requestCombinedPermission(context: Context, permissions:Array<String>, call: Consumer<Permission>){
if (context is FragmentActivity ){
RxPermissions(context).requestEachCombined(*permissions).subscribe(call)
} else if (context is Fragment){
RxPermissions(context).requestEachCombined(*permissions).subscribe(call)
}
}
/**
* desc:询问通知栏是否开启,version>19才访问
* create by cong on 2018/5/5 16:32
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun isNotificationEnabled(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//8.0手机以上
if ((context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).importance == NotificationManager.IMPORTANCE_NONE) {
return false
}
}
try {
val appOpsClass = Class.forName(AppOpsManager::class.java.name)
return appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String::class.java).invoke(
context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager,
appOpsClass.getDeclaredField("OP_POST_NOTIFICATION")[Int::class.java] as Int,
context.applicationInfo.uid,
context.applicationContext.packageName
) as Int == AppOpsManager.MODE_ALLOWED
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
/**
* desc: 跳转到应用程序信息界面,一般用于用户开启通知权限
* Created by congge on 2018/7/18 14:58
*/
fun startAppNotifySetting(mContext: Context) {
try {
val packageName = mContext.packageName
val uid = mContext.applicationInfo.uid
val intent = Intent()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, uid)
}
else -> {
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("app_package", packageName)
intent.putExtra("app_uid", uid)
}
}
mContext.startActivity(intent)
} catch (e: java.lang.Exception) {
e.printStackTrace()
val intent = Intent()
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.action = "android.settings.APPLICATION_DETAILS_SETTINGS"
intent.data = Uri.fromParts("package", mContext.packageName, null)
mContext.startActivity(intent)
}
}
/**
* time : 2022/4/22 17:42
* author : hpy
* desc : 打开应用信息
*/
fun startAppSetting(mContext: Context){
mContext.startActivity(
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", mContext.packageName, null)
}
)
}
/**
* @desc : 判断全部集合是否有权限
* @author : congge on 2022-05-09 9:50
**/
fun hasPermissions(context: Context, perms: Array<String>): Boolean {
if (Build.VERSION.SDK_INT < 23) {
return true
}
for (perm in perms) {
if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) {
return false
}
}
return true
}
/**
* @desc : 判断单个是否有权限
* @author : congge on 2022-05-09 9:51
**/
fun hasPermission(context: Context, perm: String): Boolean {
return Build.VERSION.SDK_INT < 23 || ContextCompat.checkSelfPermission(context, perm) == PackageManager.PERMISSION_GRANTED
}
}
我们用它的requestCombinedPermission请求权限
四、完整请求相机的案例
if(PermissionCheckUtil.checkPermissions(mContext, PermissionNewUtils.Group.CAMERA)){
granted()
}else{
DialogUtils.okAndCancel(mContext,tipStrTitle, tipStr, R.string.go_open, R.string.cancel){
PermissionNewUtils.requestCombinedPermission(mContext, PermissionNewUtils.Group.CAMERA) {
if (it.granted) {
granted()
} else {
if (it.shouldShowRequestPermissionRationale) {
//禁止,不做操作
} else {
//永久禁止
DialogUtilsKT.okAndCancel(
mContext,
mContext.getString(alwaysContent),
mContext.getString(R.string.ok_go_setting),
title = mContext.getString(alwaysTitle)
) {
PermissionNewUtils.startAppSetting(mContext)
}
}
}
}
}
}