안드로이드 api 29 이하 버전으로 녹음시도시 앱이 크래시 된다.
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
api 30 이상 버전은 정상적으로 동작함.
API 30부터는 Scoped Storage가 기본으로 적용되어 외부 저장소에 대한 권한 없이도 앱이 자신의 전용 디렉토리에 파일을 저장하고 접근할 수 있게 되어있음
getExternalFilesDir())를 사용하도록 권장됨그러나 API 29에서는 Scoped Storage가 도입되지 않았고, 기존 방식의 외부 저장소 접근이 여전히 사용되었기 때문에, 외부 저장소에 접근하기 위해서는 명시적인 권한 필요
WRITE_EXTERNAL_STORAGE권한을 요청하고, 해당 권한이 부여된 후에 파일을 생성하여 접근@Composable
fun GetRecordAccess(
onGranted: () -> Unit
) {
val recordAudioPermission = Manifest.permission.RECORD_AUDIO
val writeExternalStoragePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(recordAudioPermission)
} else {
// api 29 이하에서는 WRITE_EXTERNAL_STORAGE 권한도 필요
arrayOf(recordAudioPermission, **writeExternalStoragePermission**)
}
val permissionState = remember { mutableStateOf(false) }
val requestPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissionsMap ->
permissionState.value = permissionsMap.values.all { it }
}
if (permissionState.value) {
onGranted()
} else {
LaunchedEffect(permissionState.value) {
requestPermissionLauncher.launch(permissions)
}
}
}
android:requestLegacyExternalStorage="true”
@Composable
fun GetRecordAccess(
context: Context,
onGranted: () -> Unit,
onDisMiss: () -> Unit = {}
) {
val recordAudioPermission = Manifest.permission.RECORD_AUDIO
val writeExternalStoragePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(recordAudioPermission)
} else {
// api 29 이하에서는 WRITE_EXTERNAL_STORAGE 권한도 필요
arrayOf(recordAudioPermission, writeExternalStoragePermission)
}
val permissionState = remember { mutableStateOf(false) }
val requestPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissionsMap ->
val allPermissionsGranted = permissionsMap.values.all { it }
if (allPermissionsGranted) {
permissionState.value = true
onGranted()
} else {
permissionState.value = false
onDisMiss()
Toast.makeText(
context,
"권한을 허용해주세요.",
Toast.LENGTH_LONG
).show()
}
}
if (permissionState.value) {
onGranted()
} else {
LaunchedEffect(Unit) {
requestPermissionLauncher.launch(permissions)
}
}
}