今天大概會聊到的範圍
- rememberLauncherForActivityResult
上一篇我學到可以透過 AndroidView 中將 CameraX 的 PreviewView 加入 Compose 中,要顯示相機的畫面,還需要取得 User 的 Permission。所今天想來研究一下如果我的 Compose 元素想要與 Permission 互動應該怎麼處理。
首先,取得 permission 並不會太複雜:
// checkPermission
private fun checkPermissionsGranted(context: Context) = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
取得 permission 時會需要 Context,而在 Compose 中要取得 Context 可以透過 LocalContext.current 取得
@Composable
fun CameraScreen() {
// 取得 context
val context = LocalContext.current
// 檢查完 permission 後將答案存在 remember 中
var hasPermission by remember {
mutableStateOf(checkPermissionsGranted(context))
}
if (hasPermission) {
CameraView()
} else {
LoadingView()
}
}
startActivityForResult in Compose
接下來,我們需要透過 startActivityForResult 向 User 請求 permission。在 Compose 中,可以透過 rememberLauncherForActivityResult 建立一個 launcher。這個 launcher 中的 callback 我們可以對 result 做處理(這邊我們將 result 存放回 state )
var hasPermission by remember { // ...
val permissionRequester = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
hasPermission = isGranted
}
permissionRequester 並不會立刻起作用,而是要在需要取得的時候觸發:
if (hasPermission) {
CameraView()
} else {
LoadingView(modifier = Modifier.clickable { permissionRequester.launch(Manifest.permission.CAMERA) })
}
使用rememberLauncherForActivityResult 時,需要提供一個 contract 和一個 callback
@Composable
public fun <I, O> rememberLauncherForActivityResult(
contract: ActivityResultContract<I, O>,
onResult: (O) -> Unit
): ManagedActivityResultLauncher<I, O> {
ActivityResultContract 各種 startActivityForResult 的流程,例如 ActivityResultContract.RequestPermission 可以取得一個 Permission ,另外還有 ActivityResultContract.RequestMultiplePermissions 可以一次請求多種權限。當今天內建的流程不符合需求時,還可以使用 ActivityResultContract.StartIntentSenderForResult 自訂
class RequestPermission extends ActivityResultContract<String, Boolean>
ActivityResultContract 會需要一個 input 和一個 output 的 type,以 RequestMultiplePermissions 為例, input 就是一個 String ( permission key),output 則是 Boolean 代表是否接受權限。output 的資料會在 callback ( onResult ) 中出現,input 則是在 launch 時提供
完成以上,就可以在 user 點擊 LoadingView 後跳出權限取得的 Dialog。但是如果今天要自動 launch 則會出現 Launcher has not been initialized 的錯誤。這個原因會需要提到 SideEffect,主題滿大的,之後再獨立一篇分享吧!