Replace Accompanist Permissions with first-party activity-compose API
Accompanist Permissions (0.36.0) is deprecated and experimental. Migrate to the stable ActivityResultContracts.RequestPermission / RequestMultiplePermissions APIs already available via activity-compose. Adds explicit state tracking with a cameraResultReceived flag to correctly distinguish "never asked" from "permanently denied" — an improvement over the previous Accompanist-based detection. Bump version to 1.1.2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
24eabfe26f
commit
878c23bf89
5 changed files with 76 additions and 35 deletions
|
|
@ -112,9 +112,6 @@ dependencies {
|
|||
// Location
|
||||
implementation(libs.play.services.location)
|
||||
|
||||
// Permissions
|
||||
implementation(libs.accompanist.permissions)
|
||||
|
||||
// Debug
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ package no.naiv.tiltshift
|
|||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -26,23 +29,24 @@ import androidx.compose.material3.Icon
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
import no.naiv.tiltshift.ui.CameraScreen
|
||||
import no.naiv.tiltshift.ui.theme.AppColors
|
||||
|
||||
|
|
@ -66,21 +70,47 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
private fun TiltShiftApp() {
|
||||
val cameraPermission = rememberPermissionState(Manifest.permission.CAMERA)
|
||||
val locationPermissions = rememberMultiplePermissionsState(
|
||||
listOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
val context = LocalContext.current
|
||||
val activity = context as? ComponentActivity
|
||||
|
||||
var cameraGranted by remember {
|
||||
mutableStateOf(
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
||||
== PackageManager.PERMISSION_GRANTED
|
||||
)
|
||||
)
|
||||
}
|
||||
var locationGranted by remember {
|
||||
mutableStateOf(
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED
|
||||
)
|
||||
}
|
||||
|
||||
// Track whether the camera permission dialog has returned a result,
|
||||
// so we can distinguish "never asked" from "permanently denied"
|
||||
var cameraResultReceived by remember { mutableStateOf(false) }
|
||||
|
||||
val cameraPermissionLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
cameraGranted = granted
|
||||
cameraResultReceived = true
|
||||
}
|
||||
|
||||
val locationPermissionLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
locationGranted = permissions.values.any { it }
|
||||
}
|
||||
|
||||
// Request camera permission on launch
|
||||
LaunchedEffect(Unit) {
|
||||
if (!cameraPermission.status.isGranted) {
|
||||
cameraPermission.launchPermissionRequest()
|
||||
if (!cameraGranted) {
|
||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,28 +120,47 @@ private fun TiltShiftApp() {
|
|||
.background(Color.Black)
|
||||
) {
|
||||
when {
|
||||
cameraPermission.status.isGranted -> {
|
||||
cameraGranted -> {
|
||||
// Camera permission granted - show camera
|
||||
CameraScreen()
|
||||
|
||||
// Request location in background (for EXIF GPS)
|
||||
LaunchedEffect(Unit) {
|
||||
if (!locationPermissions.allPermissionsGranted) {
|
||||
locationPermissions.launchMultiplePermissionRequest()
|
||||
if (!locationGranted) {
|
||||
locationPermissionLauncher.launch(
|
||||
arrayOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Permanently denied: not granted AND rationale not shown
|
||||
val cameraPermanentlyDenied = !cameraPermission.status.isGranted &&
|
||||
!cameraPermission.status.shouldShowRationale
|
||||
// Permanently denied: user has responded to the dialog, but permission
|
||||
// is still denied and the system won't show the dialog again
|
||||
val cameraPermanentlyDenied = cameraResultReceived &&
|
||||
activity?.let {
|
||||
!ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
it, Manifest.permission.CAMERA
|
||||
)
|
||||
} ?: false
|
||||
|
||||
// Show permission request UI
|
||||
PermissionRequestScreen(
|
||||
onRequestCamera = { cameraPermission.launchPermissionRequest() },
|
||||
onRequestLocation = { locationPermissions.launchMultiplePermissionRequest() },
|
||||
cameraGranted = cameraPermission.status.isGranted,
|
||||
locationGranted = locationPermissions.allPermissionsGranted,
|
||||
onRequestCamera = {
|
||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
},
|
||||
onRequestLocation = {
|
||||
locationPermissionLauncher.launch(
|
||||
arrayOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
)
|
||||
)
|
||||
},
|
||||
cameraGranted = false,
|
||||
locationGranted = locationGranted,
|
||||
cameraPermanentlyDenied = cameraPermanentlyDenied
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue