授權處理是什麼?

在某些情況下app對裝置的操作會影響隱私以及安全性等等,因此在app進行這些操作之前必須詢問使用者是否可以提供進行這些操作的權限。
這些權限必須宣告在AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
…
  <uses-permission android:name="android.permission.CAMERA"/>
…
</manifest>

要求權限的運作方式會根據app的targetSdkVersion和裝置的API level有不同的動作。
a.若app的targetSdkVersion大於等於23,且裝置的API level也是大於等於23。要求權限的動作會在app運作時,因此必須在執行這些權限前檢查是否已獲得授權,若已獲得授權就可執行相關動作,若未獲得授權可以要求使用者給予授權。
以上的機制也稱執行期權限檢查,也就是在運行app時才要求權限,安裝時不會要求授權。
b.若app的targetSdkVersion小於等於22,或裝置的API level也是小於等於22,要求權限的動作會在安裝app時請求授權,若使用者拒絕授權,app就無法安裝。若使用者給予授權,該授權就無法取消,除非重新安裝app。
以上的機制也稱安裝期權限檢查,不需進行處理,因為不給予授權即無法安裝app。
 

檢查是否已獲得授權

使用ContextCompat.checkSelfPermission方法

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
}

第1個參數為context,第2個參數為要檢查的授權,若方法回傳值等於PackageManager.PERMISSION_GRANTED 代表已獲得授權,方法回傳值等於PackageManager.PERMISSION_DENIED 代表未獲得授權。

要求申請授權

使用ActivityCompat.requestPermissions方法

ActivityCompat.requestPermissions(this, new String[]{permission.CAMERA}, REQUEST_PERMISSION_CODE);

第1個參數為Activity,第2個參數為要申請的授權,第3個參數為要求授權碼,該要求授權碼在”處理申請授權結果”需要用到。
呼叫requestPermission之後會顯示對話框讓使用者選擇是否給予授權,使用者選擇完畢後會觸發onRequestPermissionsResult方法,因此在Activity也需要複寫該方法。
 

處理申請授權結果

在Activity複寫onRequestPermissionsResult 方法,並在該方法中處理使用者回復申請授權結果。

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  if (requestCode == REQUEST_PERMISSION_CODE) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        //使用者給予授權後進行動作
    } else {
        //使用者拒絕給予授權進行動作
    }
  }
}

if (requestCode == REQUEST_PERMISSION_CODE)
首先檢查要求授權碼是否相符,因為可能在多個位置呼叫請授權的動作。
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
代表使用者給予授權
 

處理使用者點擊Deny & don’t ask again

若使用者在app第1次申請權限時拒絕給予授權,則第2次申請授權時會另外出現Deny & don’t ask again選項,若使用者點擊該選項,即使之後再要求授權都不會出現申請授權的對話框。
也就是說呼叫requestPermissions方法不會顯示申請授權的對話框,但還是會觸發onRequestPermissionsResult方法且該方法的第3個參數grantResults會回傳-1,-1代表使用者拒絕給予授權。
此時已無法再申請授權,除了重新安裝app以外就是使用者手動打開權限設定的頁面才能申請授權。
因此可在onRequestPermissionsResult方法內呼叫shouldShowRequestPermissionRationale 方法。
若使用者點擊Deny & don’t ask again 則 shouldShowRequestPermissionRationale方法回傳false,此時可以建立對話框告知使用者是否開啟授權頁面,是的話導引使用者開啟授權頁面,否的話告知使用者無法進行後續動作。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
    @NonNull int[] grantResults) {
  if (requestCode == REQUEST_PERMISSION_CODE) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
      startCameraActivity();
    } else {
      if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permission.CAMERA)) {
        AlertDialog.Builder builder = new Builder(this);
        builder.setTitle("Warning").setMessage("需要開啟相機權限,是否開啟權限頁面?").setPositiveButton("開啟權限頁面",
            new OnClickListener() {
              @Override
              public void onClick(DialogInterface dialogInterface, int i) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivity(intent);
              }
            }).setNegativeButton("取消", null).show();
      } else {
        Toast.makeText(this, "需要授權以開啟相機", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "onRequestPermissionsResult: user not click deny && don't ask");
      }
    }
  }
}

 
以上完成後可在該頁面加入按鈕讓使用者點擊後詢問是否開啟權限設定頁面

@Override
public void onClick(View view) {
  int uiID = view.getId();
  switch(uiID){
    case R.id.apply_permission:
      ActivityCompat.requestPermissions(this, new String[]{permission.CAMERA}, REQUEST_PERMISSION_CODE);
      break;
  }
}

 

重點總結

1.在AndroidManifest.xml宣告需要的授權
2. 檢查是否已獲得授權
已獲得授權->執行授權相關動作
未獲得授權->進入3.要求申請授權
3.要求申請授權
直接進入4.處理申請授權結果
4.處理申請授權結果
使用者給予授權->執行授權相關動作
使用者未給予授權->提示使用者給予授權,並提供再度進行申請授權的動作
完整程式碼如下

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  private static final String TAG = MainActivity.class.getSimpleName();
  private static final int REQUEST_PERMISSION_CODE = 999;
  private Button mApplyPermissio;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mApplyPermissio = findViewById(R.id.apply_permission);
    mApplyPermissio.setOnClickListener(this);
    checkPermission();
  }
  private void checkPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
      startCameraActivity();
    } else {
      ActivityCompat.requestPermissions(this, new String[]{permission.CAMERA}, REQUEST_PERMISSION_CODE);
    }
  }
  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
      @NonNull int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION_CODE) {
      if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        startCameraActivity();
      } else {
        if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permission.CAMERA)) {
          showOpenAppPermissionDialog();
        } else {
          Toast.makeText(this, "需要授權以開啟相機", Toast.LENGTH_SHORT).show();
        }
      }
    }
  }
  private void showOpenAppPermissionDialog() {
    Builder builder = new Builder(this);
    builder.setTitle("Warning").setMessage("需要開啟相機權限,是否開啟權限頁面?").setPositiveButton("開啟權限頁面",
        new OnClickListener() {
          @Override
          public void onClick(DialogInterface dialogInterface, int i) {
            Intent intent = new Intent();
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", getPackageName(), null);
            intent.setData(uri);
            startActivity(intent);
          }
        }).setNegativeButton("取消", new OnClickListener() {
      @Override
      public void onClick(DialogInterface dialogInterface, int i) {
        Toast.makeText(MainActivity.this, "請開啟權限設定並給予授權", Toast.LENGTH_SHORT).show();
      }
    }).show();
  }
  private void startCameraActivity() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, 0);
  }
  @Override
  public void onClick(View view) {
    int uiID = view.getId();
    switch(uiID){
      case R.id.apply_permission:
        ActivityCompat.requestPermissions(this, new String[]{permission.CAMERA}, REQUEST_PERMISSION_CODE);
        break;
    }
  }
}