Bỏ qua để đến nội dung

Permissions

Permissions bảo vệ quyền riêng tư của user bằng cách yêu cầu app xin phép trước khi truy cập sensitive data hoặc hardware.

Tự động được cấp, chỉ cần khai báo trong Manifest:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Cần runtime request:

  • CAMERA
  • LOCATION
  • MICROPHONE
  • CONTACTS
  • CALENDAR
  • STORAGE
  • PHONE
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Storage permissions -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
class MainActivity : ComponentActivity() {
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openCamera()
} else {
showPermissionDeniedMessage()
}
}
private fun checkCameraPermission() {
when {
checkSelfPermission(Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED -> {
openCamera()
}
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
showRationaleDialog()
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
}
private val requestMultiplePermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val allGranted = permissions.entries.all { it.value }
if (allGranted) {
startLocationTracking()
} else {
// Check which permissions were denied
permissions.forEach { (permission, granted) ->
if (!granted) {
handleDenied(permission)
}
}
}
}
private fun requestLocationPermissions() {
requestMultiplePermissions.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
build.gradle.kts
implementation("com.google.accompanist:accompanist-permissions:0.34.0")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)
when {
cameraPermissionState.status.isGranted -> {
CameraPreview()
}
cameraPermissionState.status.shouldShowRationale -> {
Column {
Text("Camera permission is required for this feature")
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Text("Grant Permission")
}
}
}
else -> {
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Text("Request Camera Permission")
}
}
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationScreen() {
val locationPermissions = rememberMultiplePermissionsState(
listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
when {
locationPermissions.allPermissionsGranted -> {
LocationContent()
}
locationPermissions.shouldShowRationale -> {
RationaleDialog(onConfirm = { locationPermissions.launchMultiplePermissionRequest() })
}
else -> {
Button(onClick = { locationPermissions.launchMultiplePermissionRequest() }) {
Text("Request Location Permissions")
}
}
}
}
if (!shouldShowRequestPermissionRationale(permission)
&& checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
// User selected "Don't ask again"
showSettingsDialog()
}
private fun showSettingsDialog() {
AlertDialog.Builder(this)
.setTitle("Permission Required")
.setMessage("Please enable permission in Settings")
.setPositiveButton("Settings") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", packageName, null)
startActivity(intent)
}
.setNegativeButton("Cancel", null)
.show()
}
// Photo picker - không cần permission
val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
uri?.let { handleSelectedImage(it) }
}
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
// ✅ Good: Request khi cần
fun onCameraButtonClick() {
requestCameraPermission()
}
// ❌ Bad: Request khi app start
override fun onCreate() {
requestAllPermissions() // Don't do this
}
// ✅ Good: Explain why
@Composable
fun PermissionRationale() {
Column {
Text("We need camera access to take photos for your profile")
Button(onClick = { requestPermission() }) {
Text("Continue")
}
}
}
// ✅ Good: Graceful degradation
if (hasLocationPermission) {
showNearbyPlaces()
} else {
showAllPlaces() // Fallback without location
}
// Overlay permission
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
startActivity(intent)
}
// Notification permission (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

Type Request
Normal Manifest only
Dangerous Runtime request
Special Intent to Settings
Best Practice Description
Request in context Khi user cần feature
Explain why Show rationale
Handle denial Graceful degradation
Don’t ask again Link to Settings