Android 6.0動態許可權及小米(MIUI)許可權的特殊處理
前言
從android6.0之後,android對使用者的許可權進行相對教嚴格的管控,針對重要的許可權,使用者在第一安裝並使用的時候,系統以彈框的形式向用戶獲取,使用者的許可權授取直接關乎著應用是否能正常使用對應的功能;因此這裡對android 6.0之後的動態許可權獲取進行整理並記錄備忘;由於國內的android系統各大手機廠商都有所定製;對於6.0之後許可權,小米系統是存在2個地方的許可權授取,必須2個地方都啟動,才能正常使用;因此帶來了一些不必要的坑;這裡一起進行整理記錄
不多說,先上程式碼及效果圖
原始碼
原始碼下載效果圖
- 允許授權
- 二次授權
- 允許授權
常規操作許可權的頁面及流程
為了不影響使用者的正使用,我們一般都會在應用的啟動頁對該應用所用到的所有許可權進行授權;避免使用者在使用過程中還存在許可權授取的動作,具體流程如下:
使用者第一次使用並允許授權的流程
如果使用者第一次使用的時候,取消了部分關鍵授權,直接影響到app的使用的二次授權流程
如果使用者第一次使用的時候,取消了部分關鍵授權,針對小米MIUI5以上的系統的授權流程
許可權說明
普通許可權
直接在AndroidManifest.xml中獲取即可- ACCESS_LOCATION_EXTRA_COMMANDS
- ACCESS_NETWORK_STATE
- ACCESS_NOTIFICATION_POLICY
- ACCESS_WIFI_STATE
- BLUETOOTH
- BLUETOOTH_ADMIN
- BROADCAST_STICKY
- CHANGE_NETWORK_STATE
- CHANGE_WIFI_MULTICAST_STATE
- CHANGE_WIFI_STATE
- DISABLE_KEYGUARD
- EXPAND_STATUS_BAR
- GET_PACKAGE_SIZE
- INSTALL_SHORTCUT
- INTERNET
- KILL_BACKGROUND_PROCESSES
- MODIFY_AUDIO_SETTINGS
- NFC
- READ_SYNC_SETTINGS
- READ_SYNC_STATS
- RECEIVE_BOOT_COMPLETED
- REORDER_TASKS
- REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- REQUEST_INSTALL_PACKAGES
- SET_ALARM
- SET_TIME_ZONE
- SET_WALLPAPER
- SET_WALLPAPER_HINTS
- TRANSMIT_IR
- UNINSTALL_SHORTCUT
- USE_FINGERPRINT
- VIBRATE
- WAKE_LOCK
- WRITE_SYNC_SETTINGS
危險許可權,需要動態申請的許可權
許可權授取流程
- 檢測是否是6.0以上的系統
Build.VERSION.SDK_INT >= 23 檢測許可權是否開啟
/** * 檢測許可權 * * @return true 所需許可權全部授取 false 存在未授權的許可權 */ public boolean isAllGranted() { /** * 第 1 步: 檢查是否有相應的許可權 */ boolean isAllGranted = checkPermissionAllGranted( new String[]{ Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE } ); return isAllGranted; } /** * 檢查是否擁有指定的所有許可權 */ private boolean checkPermissionAllGranted(String[] permissions) { for (String permission : permissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { // 只要有一個許可權沒有被授予, 則直接返回 false return false; } } return true; }
授權多個許可權
// 一次請求多個許可權, 如果其他有許可權是已經授予的將會自動忽略掉 ActivityCompat.requestPermissions( this, new String[]{ Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, MY_PERMISSION_REQUEST_CODE );
所有許可權授取之後的回撥
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == MY_PERMISSION_REQUEST_CODE) { boolean isAllGranted = true; // 判斷是否所有的許可權都已經授予了 for (int grant : grantResults) { if (grant != PackageManager.PERMISSION_GRANTED) { isAllGranted = false; break; } } if (isAllGranted) { // 如果所有的許可權都授予了, 跳轉到主頁 gotoHomeActivity(); } else { // 彈出對話方塊告訴使用者需要許可權的原因, 並引導使用者去應用許可權管理中手動開啟許可權按鈕 openAppDetails(); } } }
小米許可權特殊處理
判斷是否是小米系統
/** * 檢查手機是否是miui系統 * * @return */ public boolean isMIUI() { String device = Build.MANUFACTURER; System.out.println("Build.MANUFACTURER = " + device); if (device.equals("Xiaomi")) { System.out.println("this is a xiaomi device"); Properties prop = new Properties(); try { prop.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"))); } catch (IOException e) { e.printStackTrace(); return false; } return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null || prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null || prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null; } else { return false; } }
判斷授權管理介面中的許可權授取
這裡需要注意的是需要對許可權進行一個個的判斷,無法像上面一樣用個集合就進行判斷了;具體應用需要那些這裡就去判斷那些就好了。/** * 判斷小米MIUI系統中授權管理中對應的許可權授取 * * @return false 存在核心的未收取的許可權 true 核心許可權已經全部授權 */ public boolean initMiuiPermission() { AppOpsManager appOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); int locationOp = appOpsManager.checkOp(AppOpsManager.OPSTR_FINE_LOCATION, Binder.getCallingUid(), getPackageName()); if (locationOp == AppOpsManager.MODE_IGNORED) { return false; } int cameraOp = appOpsManager.checkOp(AppOpsManager.OPSTR_CAMERA, Binder.getCallingUid(), getPackageName()); if (cameraOp == AppOpsManager.MODE_IGNORED) { return false; } int phoneStateOp = appOpsManager.checkOp(AppOpsManager.OPSTR_READ_PHONE_STATE, Binder.getCallingUid(), getPackageName()); if (phoneStateOp == AppOpsManager.MODE_IGNORED) { return false; } int readSDOp = appOpsManager.checkOp(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, Binder.getCallingUid(), getPackageName()); if (readSDOp == AppOpsManager.MODE_IGNORED) { return false; } int writeSDOp = appOpsManager.checkOp(AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE, Binder.getCallingUid(), getPackageName()); if (writeSDOp == AppOpsManager.MODE_IGNORED) { return false; } return true; }
MIUI不聽版本,跳幀授權管理介面的方式不一樣
各個不同系統的設定介面跳轉請參考部落格末尾的幫助類,以下程式碼摘自其中。public static void Xiaomi(Activity activity) { // 只相容miui v5/v6 的應用許可權設定頁面,否則的話跳轉應用設定頁面(許可權設定上一級頁面) String miuiVersion = getMiuiVersion(); Intent intent = null; if ("V5".equals(miuiVersion)) { Uri packageURI = Uri.parse("package:" + activity.getApplicationInfo().packageName); intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); } else if ("V6".equals(miuiVersion) || "V7".equals(miuiVersion)) { intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); intent.putExtra("extra_pkgname", activity.getPackageName()); } else if ("V8".equals(miuiVersion)) { intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); intent.putExtra("extra_pkgname", activity.getPackageName()); } else { } if (null != intent) activity.startActivity(intent); }
- 小米系統測試效果
- 初次允許授權
- 二次授權
- 初次允許授權
各個android作業系統跳轉到設定頁面的公共方法
以下程式碼為結合網友提供的再根據情況調整了一下小米相關的跳轉
package com.lpf.permission;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.v7.appcompat.BuildConfig;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
*/
public class JumpPermissionManagement {
/**
* Build.MANUFACTURER
*/
private static final String MANUFACTURER_HUAWEI = "Huawei";//華為
private static final String MANUFACTURER_MEIZU = "Meizu";//魅族
private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米
private static final String MANUFACTURER_SONY = "Sony";//索尼
private static final String MANUFACTURER_OPPO = "OPPO";
private static final String MANUFACTURER_LG = "LG";
private static final String MANUFACTURER_VIVO = "vivo";
private static final String MANUFACTURER_SAMSUNG = "samsung";//三星
private static final String MANUFACTURER_LETV = "Letv";//樂視
private static final String MANUFACTURER_ZTE = "ZTE";//中興
private static final String MANUFACTURER_YULONG = "YuLong";//酷派
private static final String MANUFACTURER_LENOVO = "LENOVO";//聯想
/**
* 此函式可以自己定義
*
* @param activity
*/
public static void GoToSetting(Activity activity) {
switch (Build.MANUFACTURER) {
case MANUFACTURER_HUAWEI:
Huawei(activity);
break;
case MANUFACTURER_MEIZU:
Meizu(activity);
break;
case MANUFACTURER_XIAOMI:
Xiaomi(activity);
break;
case MANUFACTURER_SONY:
Sony(activity);
break;
case MANUFACTURER_OPPO:
OPPO(activity);
break;
case MANUFACTURER_LG:
LG(activity);
break;
case MANUFACTURER_LETV:
Letv(activity);
break;
default:
ApplicationInfo(activity);
Log.e("goToSetting", "目前暫不支援此係統");
break;
}
}
public static void Huawei(Activity activity) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");
intent.setComponent(comp);
activity.startActivity(intent);
}
public static void Meizu(Activity activity) {
Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
activity.startActivity(intent);
}
public static void Xiaomi(Activity activity) {
// 只相容miui v5/v6 的應用許可權設定頁面,否則的話跳轉應用設定頁面(許可權設定上一級頁面)
String miuiVersion = getMiuiVersion();
Intent intent = null;
if ("V5".equals(miuiVersion)) {
Uri packageURI = Uri.parse("package:" + activity.getApplicationInfo().packageName);
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
} else if ("V6".equals(miuiVersion) || "V7".equals(miuiVersion)) {
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
intent.putExtra("extra_pkgname", activity.getPackageName());
} else if ("V8".equals(miuiVersion)) {
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
intent.putExtra("extra_pkgname", activity.getPackageName());
} else {
}
if (null != intent)
activity.startActivity(intent);
}
public static void Sony(Activity activity) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity");
intent.setComponent(comp);
activity.startActivity(intent);
}
public static void OPPO(Activity activity) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity");
intent.setComponent(comp);
activity.startActivity(intent);
}
public static void LG(Activity activity) {
Intent intent = new Intent("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity");
intent.setComponent(comp);
activity.startActivity(intent);
}
public static void Letv(Activity activity) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps");
intent.setComponent(comp);
activity.startActivity(intent);
}
/**
* 只能開啟到自帶安全軟體
*
* @param activity
*/
public static void _360(Activity activity) {
Intent intent = new Intent("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");
intent.setComponent(comp);
activity.startActivity(intent);
}
/**
* 應用資訊介面
*
* @param activity
*/
public static void ApplicationInfo(Activity activity) {
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", activity.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", activity.getPackageName());
}
activity.startActivity(localIntent);
}
/**
* 系統設定介面
*
* @param activity
*/
public static void SystemConfig(Activity activity) {
Intent intent = new Intent(Settings.ACTION_SETTINGS);
activity.startActivity(intent);
}
public static String getMiuiVersion() {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop ro.miui.ui.version.name");
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return line;
}
}
總結
這裡只找了部分的手機進行了測試;由於android市場的複雜性,可能存在不相容的情況,主要的問題也應該是在授權設定哪裡;可以根據特定的手機進行調整即可;
原始碼下載