1. 程式人生 > >Android 6.0: 動態許可權管理的解決方案

Android 6.0: 動態許可權管理的解決方案

歡迎Follow我的GitHub, 關注我的CSDN.

許可權

Book

Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了使用者體驗, 同時也為程式設計師帶來新的負擔. 動態許可權管理就是這樣, 一方面讓使用者更加容易的控制自己的隱私, 一方面需要重新適配應用許可權. 時代總是不斷髮展, 程式總是以人為本, 讓我們為應用新增動態許可權管理吧! 這裡提供了一個非常不錯的解決方案, 提供原始碼, 專案可以直接使用.

Android系統包含預設的授權提示框, 但是我們仍需要設定自己的頁面. 原因是系統提供的授權框, 會有不再提示的選項. 如果使用者選擇, 則無法觸發授權提示. 使用自定義的提示頁面, 可以給予使用者手動修改授權的指導.

本文示例GitHub下載地址.

在Api 23中, 許可權需要動態獲取, 核心許可權必須滿足. 標準流程:

流程圖

如果使用者點選, 不再提示, 則系統授權彈窗將不會彈出. 流程變為:

流程圖

流程就這些, 讓我們看看程式碼吧.

1. 許可權

在AndroidManifest中, 新增兩個許可權, 錄音修改音量.

    <!--危險許可權-->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    <!--一般許可權-->
    <uses-permission
android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

危險許可權必須要授權, 一般許可權不需要.

檢測許可權類

/**
 * 檢查許可權的工具類
 * <p/>
 * Created by wangchenlong on 16/1/26.
 */
public class PermissionsChecker {
    private final Context mContext;

    public PermissionsChecker(Context context) {
        mContext = context.getApplicationContext();
    }

    // 判斷許可權集合
public boolean lacksPermissions(String... permissions) { for (String permission : permissions) { if (lacksPermission(permission)) { return true; } } return false; } // 判斷是否缺少許可權 private boolean lacksPermission(String permission) { return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_DENIED; } }

2. 首頁

假設首頁需要使用許可權, 在頁面顯示前, 即onResume時, 檢測許可權,
如果缺少, 則進入許可權獲取頁面; 接收返回值, 拒絕許可權時, 直接關閉.

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE = 0; // 請求碼

    // 所需的全部許可權
    static final String[] PERMISSIONS = new String[]{
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.MODIFY_AUDIO_SETTINGS
    };

    @Bind(R.id.main_t_toolbar) Toolbar mTToolbar;

    private PermissionsChecker mPermissionsChecker; // 許可權檢測器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setSupportActionBar(mTToolbar);

        mPermissionsChecker = new PermissionsChecker(this);
    }

    @Override protected void onResume() {
        super.onResume();

        // 缺少許可權時, 進入許可權配置頁面
        if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
            startPermissionsActivity();
        }
    }

    private void startPermissionsActivity() {
        PermissionsActivity.startActivityForResult(this, REQUEST_CODE, PERMISSIONS);
    }

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 拒絕時, 關閉頁面, 缺少主要許可權, 無法執行
        if (requestCode == REQUEST_CODE && resultCode == PermissionsActivity.PERMISSIONS_DENIED) {
            finish();
        }
    }
}

核心許可權必須滿足, 如攝像應用, 攝像頭許可權就是必須的, 如果使用者不予授權, 則直接關閉.

3. 授權頁

授權頁, 首先使用系統預設的授權頁, 當用戶拒絕時, 指導使用者手動設定, 當用戶再次操作失敗後, 返回繼續提示. 使用者手動退出授權頁時, 給使用頁傳送授權失敗的通知.

/**
 * 許可權獲取頁面
 * <p/>
 * Created by wangchenlong on 16/1/26.
 */
public class PermissionsActivity extends AppCompatActivity {

    public static final int PERMISSIONS_GRANTED = 0; // 許可權授權
    public static final int PERMISSIONS_DENIED = 1; // 許可權拒絕

    private static final int PERMISSION_REQUEST_CODE = 0; // 系統許可權管理頁面的引數
    private static final String EXTRA_PERMISSIONS =
            "me.chunyu.clwang.permission.extra_permission"; // 許可權引數
    private static final String PACKAGE_URL_SCHEME = "package:"; // 方案

    private PermissionsChecker mChecker; // 許可權檢測器
    private boolean isRequireCheck; // 是否需要系統許可權檢測

    // 啟動當前許可權頁面的公開介面
    public static void startActivityForResult(Activity activity, int requestCode, String... permissions) {
        Intent intent = new Intent(activity, PermissionsActivity.class);
        intent.putExtra(EXTRA_PERMISSIONS, permissions);
        ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
    }

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
            throw new RuntimeException("PermissionsActivity需要使用靜態startActivityForResult方法啟動!");
        }
        setContentView(R.layout.activity_permissions);

        mChecker = new PermissionsChecker(this);
        isRequireCheck = true;
    }

    @Override protected void onResume() {
        super.onResume();
        if (isRequireCheck) {
            String[] permissions = getPermissions();
            if (mChecker.lacksPermissions(permissions)) {
                requestPermissions(permissions); // 請求許可權
            } else {
                allPermissionsGranted(); // 全部許可權都已獲取
            }
        } else {
            isRequireCheck = true;
        }
    }

    // 返回傳遞的許可權引數
    private String[] getPermissions() {
        return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
    }

    // 請求許可權相容低版本
    private void requestPermissions(String... permissions) {
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    }

    // 全部許可權均已獲取
    private void allPermissionsGranted() {
        setResult(PERMISSIONS_GRANTED);
        finish();
    }

    /**
     * 使用者許可權處理,
     * 如果全部獲取, 則直接過.
     * 如果許可權缺失, 則提示Dialog.
     *
     * @param requestCode  請求碼
     * @param permissions  許可權
     * @param grantResults 結果
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
            isRequireCheck = true;
            allPermissionsGranted();
        } else {
            isRequireCheck = false;
            showMissingPermissionDialog();
        }
    }

    // 含有全部的許可權
    private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }

    // 顯示缺失許可權提示
    private void showMissingPermissionDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(PermissionsActivity.this);
        builder.setTitle(R.string.help);
        builder.setMessage(R.string.string_help_text);

        // 拒絕, 退出應用
        builder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
            @Override public void onClick(DialogInterface dialog, int which) {
                setResult(PERMISSIONS_DENIED);
                finish();
            }
        });

        builder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
            @Override public void onClick(DialogInterface dialog, int which) {
                startAppSettings();
            }
        });

        builder.show();
    }

    // 啟動應用的設定
    private void startAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
        startActivity(intent);
    }
}

注意isRequireCheck引數的使用, 防止和系統提示框重疊.
系統授權提示: ActivityCompat.requestPermissions, ActivityCompat相容低版本.

效果

自定義授權

關鍵部分就這些了, 動態許可權授權雖然給程式設計師帶來了一些麻煩, 但是對使用者還是很有必要的, 我們也應該歡迎, 畢竟每個程式設計師都是半個產品經理.

OK, that’s all! Enjoy it.