講到硬體控制,總有一天要面對權限管理,不過Google有個權限的第三方EasyPermissions,今天就簡單講一下怎麼安裝和使用。
https://github.com/googlesamples/easypermissions
dependencies {
    implementation 'pub.devrel:easypermissions:3.0.0'
}    
onPermissionsGranted會被執行onPermissionsDenied會被執行class CropLensActivity : AppCompatActivity(), PermissionCallbacks {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_crop_lens)
    }        
    
    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
    
    }
    
    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
    
    }
}
ctrl + O叫出Override Members頁面,尋找onRequestPermissionsResult,點選OK
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
不管是onPermissionsGranted或onPermissionsDenied,傳出來的perms都是一串英文android.permission.CAMERA,所以我們要請使用者去設定開啟相對應的權限,所以要把這些中文化。
class PermissionsTransformer {
    companion object {
        const val CALENDAR = "日曆"
        const val CAMERA = "相機"
        const val ACCOUNTS = "聯絡人"
        const val LOCATION = "位置"
        const val AUDIO = "麥克風"
        const val CALLS = "電話"
        const val SENSORS = "人體感應器"
        const val SMS = "簡訊"
        const val STORAGE = "儲存"
        const val ELSE = "未知的權限"
        const val READ_CALENDAR = "android.permission.READ_CALENDAR"
        const val WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"
        const val PERMISSION_CAMERA = "android.permission.CAMERA"
        const val READ_CONTACTS = "android.permission.READ_CONTACTS"
        const val WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"
        const val GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"
        const val ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"
        const val ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"
        const val RECORD_AUDIO = "android.permission.RECORD_AUDIO"
        const val READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"
        const val CALL_PHONE = "android.permission.CALL_PHONE"
        const val READ_CALL_LOG = "android.permission.READ_CALL_LOG"
        const val WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"
        const val VOICEMAIL_ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"
        const val ADD_VOICEMAIL = "android.permission.ADD_VOICEMAIL"
        const val USE_SIP = "android.permission.USE_SIP"
        const val PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"
        const val BODY_SENSORS = "android.permission.BODY_SENSORS"
        const val RECEIVE_SMS = "android.permission.RECEIVE_SMS"
        const val READ_SMS = "android.permission.READ_SMS"
        const val SEND_SMS = "android.permission.SEND_SMS"
        const val RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"
        const val RECEIVE_MMS = "android.permission.RECEIVE_MMS"
        const val READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
        const val WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"
    }
    fun getText(perms: String): String {
        when (perms) {
            READ_CALENDAR,
            WRITE_CALENDAR -> return CALENDAR
            PERMISSION_CAMERA -> return CAMERA
            READ_CONTACTS,
            WRITE_CONTACTS,
            GET_ACCOUNTS -> return ACCOUNTS
            ACCESS_FINE_LOCATION,
            ACCESS_COARSE_LOCATION -> return LOCATION
            RECORD_AUDIO -> return AUDIO
            READ_PHONE_STATE,
            CALL_PHONE,
            READ_CALL_LOG,
            WRITE_CALL_LOG,
            VOICEMAIL_ADD_VOICEMAIL,
            ADD_VOICEMAIL,
            USE_SIP,
            PROCESS_OUTGOING_CALLS -> return CALLS
            BODY_SENSORS -> return SENSORS
            RECEIVE_SMS,
            READ_SMS,
            RECEIVE_WAP_PUSH,
            SEND_SMS,
            RECEIVE_MMS -> return SMS
            READ_EXTERNAL_STORAGE,
            WRITE_EXTERNAL_STORAGE -> return STORAGE
            else -> return ELSE
        }
    }
    fun getPermissionsList(perms: List<String>): StringBuilder {
        val permissionsList = HashSet<String>()
        val permissionsText = StringBuilder()
        perms.forEach { it ->
            permissionsList.add(getText(it))
        }
        permissionsList.forEach { it ->
            permissionsText.append(it)
            permissionsText.append("\n")
        }
        return permissionsText
    }
}
順手寫個測試吧
class PermissionsTransformerTest {
    @Test
    fun permissions_text_return_storage() {
        val text = PermissionsTransformer().getText(PermissionsTransformer.WRITE_EXTERNAL_STORAGE)
        assertEquals(PermissionsTransformer.STORAGE, text)
    }
}
然後還需要一個AlertDialog,跟使用者說要開啟設定頁面的什麼權限
class PermissionsDeniedAlertDialog(val context: Context) {
    companion object {
        const val PACKAGE = "package"
        const val STRING_EMPTY = ""
    }
    fun show(title: String = STRING_EMPTY, message: String = STRING_EMPTY) {
        AlertDialog
            .Builder(context)
            .setTitle(title)
            .setCancelable(false)
            .setMessage(message)
            .setPositiveButton(R.string.confirm) { _, _ ->
                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                    val uri = Uri.fromParts(PACKAGE, context.packageName, null)
                    this.data = uri
                }
                context.startActivity(intent)
            }
            .show()
    }
}
基本設定完成後,可以來寫程式了
onRequestPermissionsResult的地方新增EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this),把權限控管交給EasyPermissions
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
onPermissionsDenied的地方新增PermissionsDeniedAlertDialog(this).show(),當使用者拒絕提供權限時,顯示AlertDialog,跟使用者說要開啟設定頁面的權限。
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {    
    PermissionsDeniedAlertDialog(this).show(
        "權限不足",
        "請開啟以下權限:\n${PermissionsTransformer().getPermissionsList(perms)}"
    )
}
假設我們要求讀取和寫入的權限,首先建立陣列
val permissionList = arrayOf(
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE
)
如果要做的事情沒有權限,啟動EasyPermissions.requestPermissions(),其中有個100,就是使用者提供權限時,onPermissionsGranted的requestCode就會有100
if (!EasyPermissions.hasPermissions(this, *permissionList)) {
    EasyPermissions.requestPermissions(
        this,
        "請提供讀寫檔案權限",
        100,
        *permissionList
    )
}