情境
當你在Android6.0以上的版本,加強了隱私權的重視,因此在操作APP上,就必須讓使用者自行勾選是否開放這些權限。
問題
如何使用Runtime Permission讓使用者勾選隱私權選項
完整程式碼
可以直接參考本篇文章,完整範例可以透過 GitHub 下載來看。
- 注意 : 你必須在Android 6.0以上的裝置才能使用這個章節的權限控制
class MainActivity : AppCompatActivity() {
companion object {
private const val MY_PERMISSIONS_REQUEST_READ_CONTACTS = 100
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity, Manifest.permission.CAMERA)) {
AlertDialog.Builder(this@MainActivity)
.setMessage("我真的沒有要做壞事, 給我權限吧?")
.setPositiveButton("OK") { _, _ ->
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.CAMERA),
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
}
.setNegativeButton("No") { _, _ -> finish() }
.show()
} else {
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE),
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
finish()
}
return
}
}
}
}
在 Android 6.0 之後,多了一個功能叫做Request Permission at Run Time。
https://developer.android.com/training/permissions/requesting.html
也就是說在執行程式的時候,會跟user請求是否開放權限給APP,
如果使用者同意,APP才可以使用這些權限。
在新版的權限管理將權限分為兩大類:
- 一般的權限
- 危險的權限
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
由這個連結可以看到危險的權限有哪些。
http://www.tablesgenerator.com/markdown_tables
權限群組(Permission Group) | 權限(Permissions) |
行事曆(CALENDAR) | READ_CALENDAR WRITE_CALENDAR |
照相機(CAMERA) | CAMERA |
聯絡人(CONTACTS) | READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS |
位置(LOCATION) | ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION |
麥克風(MICROPHONE) | RECORD_AUDIO |
電話(PHONE) | READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
感應器(SENSORS) | BODY_SENSORS |
簡訊(SMS) | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
儲存(STORAGE) | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.example.givemepass.permissiondemo"
minSdkVersion 23
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
}
一開始可以使用 checkSelfPermission 來檢查是否擁有這個權限。
// Assume thisActivity is the current activity
val permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR)
你會得到 PackageManager.PERMISSION_GRANTED 跟 PackageManager.PERMISSION_DENIED 兩種結果。
如果是 PackageManager.PERMISSION_GRANTED,代表你已經獲得使用者同意 App 可以使用該權限,否則就可以透過requestPermissions 來跟使用者要求權限。
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
MY_PERMISSIONS_REQUEST_READ_CONTACTS 是你自定義常數,透過 requestPermissions 方法呼叫,會出現這樣的一個畫面。
當使用者按下 DENY or ALLOW,就會回傳至 onRequestPermissionsResult 方法,第二個參數是你所要求的權限,可以同時要求多個權限。ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
那麼你就會看到權限 Dialog 會出現數字,並且要按下同意數次。
而當使用者按下的結果會回傳至 onRequestPermissionsResult 方法,只需透過 requestCode 你自訂的參數,以及 permissions 跟 grantResults 的順序,就可以知道使用者同意了那些權限。
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
finish()
}
return
}
}
}
如果你把 permissions 和 grantResults 印出來就會看到以下訊息。
permissions:["android.permission.CAMERA","android.permission.READ_EXTERNAL_STORAGE"]
grantResults:[0,0]
grantResults 回來的結果如果 0,代表使用者同意權限,如果回來的是 -1,代表被拒絕了。
如果想知道 App 是否有開啟權限,可以到設定 -> 應用程式 -> 你的 App -> PERMISSION。
就可以看到APP內權限是否被打開。
在Google官方網站的範例中,使用 shouldShowRequestPermissionRationale 方法來跟使用者解釋更多需要使用這些權限的理由,當使用者第一次看到授權畫面,這個方法會先回傳 false,當使用者按下了拒絕, 而第二次再進入 App 的時候,shouldShowRequestPermissionRationale 就會回傳 true,讓你透過客製化的畫面來跟使用者強調拿到這個權限並沒有要做甚麼壞事 :P
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
} else {
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
}
可以透過這個方法進行這樣的修改
if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity, Manifest.permission.CAMERA)) {
AlertDialog.Builder(this@MainActivity)
.setMessage("我真的沒有要做壞事, 給我權限吧?")
.setPositiveButton("OK") { _, _ ->
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.CAMERA),
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
}
.setNegativeButton("No") { _, _ -> finish() }
.show()
} else {
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE),
MY_PERMISSIONS_REQUEST_READ_CONTACTS)
}
}
}
如此一來, 使用者可以決定是否給權限,則無法操作某些功能或者整個 App 無法操作(當然使用者經驗會變差)。
參考
https://developer.android.com/training/permissions/requesting.html#perm-request
https://developer.android.com/guide/topics/security/permissions.html
http://android-developers.blogspot.tw/2015/08/building-better-apps-with-runtime.html