相关文章

Android各版本适配之7.0

Android各版本适配之8.0

权限的分组

在Android6.0(M)之后,对权限进行了分类,大致有这三种:
普通权限
危险权限
特殊权限

普通权限也就是正常权限,是对手机的一些正常操作,对用户的隐私没有太大影响的权限,比如手机的震动,网络访问,蓝牙等权限,这些权限会在应用被安装的时候默认授予,用户不能拒绝,也不能取消。

危险权限其实就是运行中需要处理的权限,也是我们最需要注意的权限,这些权限会关系到用户的隐私或影响到其他应用的运行,这些危险权限,谷歌还做了一个权限组,以分组的形式来呈现:

传送门:Android6.0动态权限列表、普通权限列表、特殊权限列表

权限分组的用途:

比如读写文件权限:

WRITE_EXTERNAL_STORAGE
READ_EXTERNAL_STORAGE
它们是属于同一个权限组的,你如果拿到了他们其中的一个权限,那么也同时会有另一个权限,同理,如果你拿到读取通讯录的权限,那么你同时也会拥有写入通讯录的权限,但是用到的权限都需要在manifest文件中声明。

效果图:

权限适配用的的api: int checkSelfPermission(String permission) 用来检测应用是否已经具有权限
void requestPermissions(String[] permissions, int requestCode) 进行请求单个或多个权限
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 请求权限结果回调
boolean shouldShowRequestPermissionRationale(String permission) (可选)是否需要解释权限作用
图一是requestPermissions时系统弹出的提示框,图二是图一选择拒绝后在回调onRequestPermissionsResult方法中弹 框提示用户去手动授权,点击手动授权跳到图三,在图三操作完成后返回原自己app页面,整个流程就结束了。

下面是我封装成的工具类,图二中跳转手动授权需要匹配不同品牌手机:

package com.###.util;import android.Manifest;import android.app.Activity;import android.app.AlertDialog;import android.app.TabActivity;import android.content.ActivityNotFoundException;import android.content.ComponentName;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.ResolveInfo;import android.net.Uri;import android.os.Build;import android.provider.Settings;import android.support.v4.app.ActivityCompat;import android.support.v4.app.Fragment;import android.support.v4.content.ContextCompat;import android.util.Log;import android.widget.Toast;import com.sybercare.thermometer.ble.android.KzyApplication;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import static com.tencent.open.utils.Global.getPackageName;/** * PackageName: com.###.util * ClassName: PermissionUtil * Author: cuiqingchao * CreateDate: 2018/6/19 19:43 * Description: * * -----------------------------------Version Info---------------------------------------------- * Version: V0.1    UpdateUser: cuiqingchao    UpdateDate: 2018/6/19 19:43   UpdateRemark: * * * --------------------------------------------------------------------------------------------- */public class PermissionUtil {    /**     * group:android.permission-group.CONTACTS     * permission:android.permission.GET_ACCOUNTS     * permission:android.permission.READ_CONTACTS     * permission:android.permission.WRITE_CONTACTS     */    public static int PERMISSION_WRITE_CONTACTS_CODE = 0;    /**     * group:android.permission-group.PHONE     * permission:android.permission.READ_CALL_LOG     * permission:android.permission.READ_PHONE_STATE     * permission:android.permission.CALL_PHONE     * permission:android.permission.WRITE_CALL_LOG     * permission:android.permission.USE_SIP     * permission:android.permission.PROCESS_OUTGOING_CALLS     * permission:com.android.voicemail.permission.ADD_VOICEMAIL     */    public static int PERMISSION_READ_PHONE_STATE_CODE = 1;    /**     * group:android.permission-group.CALENDAR     * permission:android.permission.WRITE_CALENDAR     * permission:android.permission.WRITE_CALENDAR     */    public static int PERMISSION_READ_CALENDAR_CODE = 2;    /**     * group:android.permission-group.CAMERA     * permission:android.permission.CAMERA     */    public static int PERMISSION_CAMERA_CODE = 3;    /**     * group:android.permission-group.SENSORS     * permission:android.permission.BODY_SENSORS     */    public static int PERMISSION_BODY_SENSORS_CODE = 4;    /**     * group:android.permission-group.LOCATION     * permission:android.permission.ACCESS_FINE_LOCATION     * permission:android.permission.ACCESS_COARSE_LOCATION     */    public static int PERMISSION_ACCESS_FINE_LOCATION_CODE = 5;    /**     * group:android.permission-group.STORAGE     * permission:android.permission.READ_EXTERNAL_STORAGE     * permission:android.permission.WRITE_EXTERNAL_STORAGE     */    public static int PERMISSION_READ_EXTERNAL_STORAGE_CODE = 6;    /**     * group:android.permission-group.MICROPHONE     * permission:android.permission.RECORD_AUDIO     */    public static int PERMISSION_RECORD_AUDIO_CODE = 7;    /**     * group:android.permission-group.SMS     * permission:android.permission.READ_SMS     * permission:android.permission.RECEIVE_WAP_PUSH     * permission:android.permission.RECEIVE_MMS     * permission:android.permission.RECEIVE_SMS     * permission:android.permission.SEND_SMS     * permission:android.permission.READ_CELL_BROADCASTS     */    public static int PERMISSION_READ_SMS_CODE = 8;    /**     * android.permission.REQUEST_INSTALL_PACKAGES     */    public static int PERMISSION_INSTALL_PACKAGES_CODE = 9;    /**     * all project need permission group     */    public static int PERMISSION_INIT_CODE = 10;    public static String[] initPermission = {            Manifest.permission.READ_PHONE_STATE,            Manifest.permission.CAMERA,            Manifest.permission.ACCESS_FINE_LOCATION,            Manifest.permission.READ_EXTERNAL_STORAGE,            Manifest.permission.RECORD_AUDIO,        };    /**     * 判断是否具备所有权限     *     * @param permissions 所有权限     * @return true 具有所有权限  false没有具有所有权限,此时包含未授予的权限     */    public static boolean isHasPermissions(String... permissions) {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)            return true;        for (String permission : permissions) {            if (!isHasPermission(permission))                return false;        }        return true;    }    /**     * 判断该权限是否已经被授予     *     * @param permission     * @return true 已经授予该权限 ,false未授予该权限     */    private static boolean isHasPermission(String permission) {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)            return true;        boolean isHas = ContextCompat.checkSelfPermission(KzyApplication.getInstance().getApplicationContext(), permission) == PackageManager.PERMISSION_GRANTED;        return isHas;    }    /**     * 请求权限,经测试发现TabActivity管理Activity时,在Activity中请求权限时需要传入父Activity对象,即TabActivity对象     * 并在TabActivity管理Activity中重写onRequestPermissionsResult并分发到子Activity,否则回调不执行  。TabActivity回调中  调用getLocalActivityManager().getCurrentActivity().onRequestPermissionsResult(requestCode, permissions, grantResults);分发到子Activity     *     *     * @param object      Activity or Fragment     * @param requestCode 请求码     * @param permissions 请求权限     */    public static void requestPermissions(Object object, int requestCode, String... permissions) {        ArrayList arrayList = new ArrayList<>();        for (String permission : permissions) {            if (!isHasPermissions(permission)) {                arrayList.add(permission);            }        }        if (arrayList.size() > 0) {            if (object instanceof Activity) {                Activity activity = (Activity) object;                Activity activity1 = activity.getParent() != null && activity.getParent() instanceof TabActivity ? activity.getParent() : activity;                ActivityCompat.requestPermissions(activity1, arrayList.toArray(new String[] {}), requestCode);            } else if (object instanceof Fragment) {                Fragment fragment = (Fragment) object;                //当Fragment嵌套Fragment时使用getParentFragment(),然后在父Fragment进行分发,否则回调不执行                Fragment fragment1 = fragment.getParentFragment() != null ? fragment.getParentFragment() : fragment;                fragment1.requestPermissions(arrayList.toArray(new String[]{}), requestCode);            } else {                throw new RuntimeException("the object must be Activity or Fragment");            }        }    }    /**     * 打开 APP 的详情设置     */    public static void openAppDetails(final Context context,String message) {        AlertDialog.Builder builder = new AlertDialog.Builder(context);        //builder.setMessage("备份通讯录需要访问 “通讯录” 和 “外部存储器”,请到 “应用信息 -> 权限” 中授予!");        builder.setMessage(message);        builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                jumpPermissionPage(context);            }        });        builder.setNegativeButton("取消", null);        builder.show();    }    /**     * 国产厂商跳转“设置”适配     */    public static void jumpPermissionPage(Context context) {        String name = Build.MANUFACTURER;        //L.e(TAG, "jumpPermissionPage --- name : " + name);        switch (name) {            case "HUAWEI":                goHuaWeiMainager(context);                break;            case "vivo":                goVivoMainager(context);                break;            case "OPPO":                goOppoMainager(context);                break;            case "Coolpad":                goCoolpadMainager(context);                break;            case "Meizu":                goMeizuMainager(context);                break;            case "Xiaomi":                goXiaoMiMainager(context);                break;            case "samsung":                goSangXinMainager(context);                break;            case "Sony":                goSonyMainager(context);                break;            case "LG":                goLGMainager(context);                break;            default:                goIntentSetting(context);                break;        }    }    private static void goLGMainager(Context context){        try {            Intent intent = new Intent();            ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity");            intent.setComponent(comp);            intent.setData(Uri.parse("package:" + getPackageName()));            intent.addCategory(Intent.CATEGORY_DEFAULT);            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);            context.startActivity(intent);        } catch (Exception e) {            Toast.makeText(context, "跳转失败", Toast.LENGTH_LONG).show();            e.printStackTrace();            goIntentSetting(context);        }    }    private static void goSonyMainager(Context context){        try {            Intent intent = new Intent();            intent.addCategory(Intent.CATEGORY_DEFAULT);            intent.setData(Uri.parse("package:" + getPackageName()));            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);            ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity");            intent.setComponent(comp);            context.startActivity(intent);        } catch (Exception e) {            Toast.makeText(context, "跳转失败", Toast.LENGTH_LONG).show();            e.printStackTrace();            goIntentSetting(context);        }    }    private static void goHuaWeiMainager(Context context) {        try {            Intent intent = new Intent();            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");            intent.setComponent(comp);            intent.addCategory(Intent.CATEGORY_DEFAULT);            intent.setData(Uri.parse("package:" + getPackageName()));            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);            context.startActivity(intent);        } catch (Exception e) {            Toast.makeText(context, "跳转失败", Toast.LENGTH_LONG).show();            e.printStackTrace();            goIntentSetting(context);        }    }    private static String getMiuiVersion(Context context) {        String propName = "ro.miui.ui.version.name";        String line;        BufferedReader input = null;        try {            Process p = Runtime.getRuntime().exec("getprop " + propName);            input = new BufferedReader(                    new InputStreamReader(p.getInputStream()), 1024);            line = input.readLine();            input.close();        } catch (IOException ex) {            ex.printStackTrace();            return null;        } finally {            try {                input.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return line;    }    private static void goXiaoMiMainager(Context context) {        String rom = getMiuiVersion(context);        //L.e(TAG,"goMiaoMiMainager --- rom : "+rom);        Intent intent=new Intent();        if ("V6".equals(rom) || "V7".equals(rom)) {            intent.setAction("miui.intent.action.APP_PERM_EDITOR");            intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");            intent.putExtra("extra_pkgname", getPackageName());        } else if ("V8".equals(rom) || "V9".equals(rom)) {            intent.setAction("miui.intent.action.APP_PERM_EDITOR");            intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");            intent.putExtra("extra_pkgname", getPackageName());        } else {            goIntentSetting(context);        }        context.startActivity(intent);    }    private static void goMeizuMainager(Context context) {        try {            Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            intent.addCategory(Intent.CATEGORY_DEFAULT);            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);            intent.putExtra("packageName", getPackageName());            context.startActivity(intent);        } catch (ActivityNotFoundException localActivityNotFoundException) {            localActivityNotFoundException.printStackTrace();            goIntentSetting(context);        }    }    private static void goSangXinMainager(Context context) {        //三星4.3可以直接跳转        goIntentSetting(context);    }    private static void goIntentSetting(Context context) {        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        intent.addCategory(Intent.CATEGORY_DEFAULT);        intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);        Uri uri = Uri.fromParts("package", context.getPackageName(), null);        intent.setData(uri);        try {            context.startActivity(intent);        } catch (Exception e) {            e.printStackTrace();        }    }    private static void goOppoMainager(Context context) {        doStartApplicationWithPackageName(context,"com.coloros.safecenter");    }    /**     * doStartApplicationWithPackageName("com.yulong.android.security:remote")     * 和Intent open = getPackageManager().getLaunchIntentForPackage("com.yulong.android.security:remote");     * startActivity(open);     * 本质上没有什么区别,通过Intent open...打开比调用doStartApplicationWithPackageName方法更快,也是android本身提供的方法     */    private static void goCoolpadMainager(Context context) {        doStartApplicationWithPackageName(context,"com.yulong.android.security:remote");      /*  Intent openQQ = getPackageManager().getLaunchIntentForPackage("com.yulong.android.security:remote");        startActivity(openQQ);*/    }    private static void goVivoMainager(Context context) {        doStartApplicationWithPackageName(context,"com.bairenkeji.icaller");     /*   Intent openQQ = getPackageManager().getLaunchIntentForPackage("com.vivo.securedaemonservice");        startActivity(openQQ);*/    }    /**     * 此方法在手机各个机型设置中已经失效     *     * @return     */    private Intent getAppDetailSettingIntent(Context context) {        Intent localIntent = new Intent();        localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        localIntent.addCategory(Intent.CATEGORY_DEFAULT);        localIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);        localIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);        if (Build.VERSION.SDK_INT >= 9) {            localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");            localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));        } else if (Build.VERSION.SDK_INT <= 8) {            localIntent.setAction(Intent.ACTION_VIEW);            localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");            localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());        }        return localIntent;    }    private static void doStartApplicationWithPackageName(Context context, String packagename) {        // 通过包名获取此APP详细信息,包括Activities、services、versioncode、name等等        PackageInfo packageinfo = null;        try {            packageinfo = context.getPackageManager().getPackageInfo(packagename, 0);        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }        if (packageinfo == null) {            return;        }        // 创建一个类别为CATEGORY_LAUNCHER的该包名的Intent        Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);        resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);        resolveIntent.setPackage(packageinfo.packageName);        // 通过getPackageManager()的queryIntentActivities方法遍历        List resolveinfoList = context.getPackageManager()                .queryIntentActivities(resolveIntent, 0);        Log.e("PermissionPageManager", "resolveinfoList" + resolveinfoList.size());        for (int i = 0; i < resolveinfoList.size(); i++) {            Log.e("PermissionPageManager", resolveinfoList.get(i).activityInfo.packageName + resolveinfoList.get(i).activityInfo.name);        }        ResolveInfo resolveinfo = resolveinfoList.iterator().next();        if (resolveinfo != null) {            // packageName参数2 = 参数 packname            String packageName = resolveinfo.activityInfo.packageName;            // 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packageName参数2.mainActivityname]            String className = resolveinfo.activityInfo.name;            // LAUNCHER Intent            Intent intent = new Intent(Intent.ACTION_MAIN);            intent.addCategory(Intent.CATEGORY_LAUNCHER);            // 设置ComponentName参数1:packageName参数2:MainActivity路径            ComponentName cn = new ComponentName(packageName, className);            intent.setComponent(cn);            try {                context.startActivity(intent);            } catch (Exception e) {                goIntentSetting(context);                e.printStackTrace();            }        }    }}

在Activity中的使用:
if(!PermissionUtil.isHasPermissions(PermissionUtil.initPermission)){   PermissionUtil.requestPermissions(this,PermissionUtil.PERMISSION_INIT_CODE,PermissionUtil.initPermission);}

/** * 处理权限请求结果 * * @param requestCode *          请求权限时传入的请求码,用于区别是哪一次请求的 * * @param permissions *          所请求的所有权限的数组 * * @param grantResults *          权限授予结果,和 permissions 数组参数中的权限一一对应,元素值为两种情况,如下: *          授予: PackageManager.PERMISSION_GRANTED *          拒绝: PackageManager.PERMISSION_DENIED */@Overridepublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {   if(requestCode == PermissionUtil.PERMISSION_INIT_CODE){      boolean isAllGranted = true;      // 判断是否所有的权限都已经授予了      for (int grant : grantResults) {         if (grant != PackageManager.PERMISSION_GRANTED) {            isAllGranted = false;            break;         }      }      if (isAllGranted) {         // 如果所有的权限都授予了      } else {         // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮         PermissionUtil.openAppDetails(this,"需要使用“定位” 、“外部存储器”、“录音”、“手机状态”、“相机”权限,请到 “应用信息 -> 权限” 中授予!");      }   }}
此外特殊权限的无法申请只能手动到设置中授权权限检查也有所不同,比如请求SYSTEM_ALERT_WINDOW权限:
if (!Settings.canDrawOverlays(this)) {   Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,         Uri.parse("package:" + getPackageName()));   startActivityForResult(intent, requestCodeAlertWindow);} else {   Toast.makeText(PermissionTestActivity.this, "SYSTEM_ALERT_WINDOW 已经被授权", Toast.LENGTH_SHORT).show();}
Android8.0中“android.permission.REQUEST_INSTALL_PACKAGES”权限需要在manifest中声明后(8.0之前不需要)在代码中检测:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()){   PermissionUtil.requestPermissions(this,PermissionUtil.PERMISSION_INSTALL_PACKAGES_CODE, Manifest.permission.REQUEST_INSTALL_PACKAGES);}
总结:以上就是我做的所有授权的操作及代码,对于跳转到设置页面后 如果用户依然没有进行授权,返回后我并没有做任何提醒,为了增加用户授权的可能性在从设置返回后可以在onActivityForResult()中对授权结果进行判断,如果没有授权则弹框提示用户会对app造成哪些影响。

系统api解释:

/** * 确定权限是否已经被授予 * @param permission 被检测权限的名字. * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} 如果权限被授予, * {@link android.content.pm.PackageManager#PERMISSION_DENIED} 如果权限被拒绝返回值. */public static int checkSelfPermission(@NonNull Context context, @NonNull String permission)/** * 是否显示自定义UI提示用户 * 华为手机测试 第一次使用时返回false * 如果拒绝返回true * 如果拒绝并点击不在提醒返回false * 已经同意过权限,但在设置拒绝此时返回true * 没有同意过权限,在设置中开启并拒绝权限返回false * @param activity   请求权限Activity. * @param permission 需要请求的权限. * @return 是否显示自定义对话框提示用户. */public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,        @NonNull String permission)/** * 给应用申请权限,申请的权限必须在manifest文件注册,正常权限在安装时自动被授权,不需要使用此方法请求权限 * 请求之后会弹出系统提示框,供我们选择是拒绝还是允许,点击后 * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult( * int, String[], int[])} 方法将会被回调, * @param activity 请求权限的Activity. * @param permissions 需要请求的权限. * @param requestCode 指定一个请求码,用于区别返回结果 * */public static void requestPermissions(final @NonNull Activity activity,        final @NonNull String[] permissions, final int requestCode)/** * 调用requestPermissions方法请求权限的回调 * 需要注意的是可能请求的权限与用户互动中断;正在这种情况下回调将接收一个空的permissions和grantResults数组 * @param permissions 请求的权限. 不为null,长度可能为0. * @param grantResults 请求权限的结果PERMISSION_GRANTED表示权限被允许,PERMISSION_DENIED表示权限被拒绝 */void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,        @NonNull int[] grantResults)


更多相关文章

  1. Android(安卓)运行时权限管理最佳实践
  2. android 实现一个按钮按下时总触发一个事件
  3. Android系统篇之----Android中的run-as命令引出升降权限的安全问
  4. android利用httpclient实现post、get请求restful接口进行json和f
  5. android中Retrofit2.0的封装:设计到请求前后的操作,比如添加请求头
  6. Android(安卓)中的危险权限
  7. Android(安卓)SELinux 编写 SELinux 政策(转自官网)
  8. Android(安卓)6.0以上权限拒绝打开权限设置界面的解决方法
  9. Android通过OkHttp框架上传照片到服务器

随机推荐

  1. ionic框架对Android返回键的处理
  2. 终于打开ndk的第一扇门了:Hello World
  3. android多媒体篇-音频-1 音频相关基础知
  4. android 软键盘Enter键图标的设置 androi
  5. STM32循迹小车/Android蓝牙控制小车(四)完
  6. Android(安卓)TTS 中文 文字转语音 使用T
  7. Android(安卓)必知必会-Android(安卓)Spl
  8. Android项目开发时版本选择
  9. 使用vs2010查看android原生代码
  10. Android信息推送技术简要分析