diff --git a/permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt b/permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt index 6a2902bbd..045b8965b 100644 --- a/permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt +++ b/permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt @@ -43,7 +43,10 @@ import java.util.EnumSet public class PermissionsLaunchDetector : Detector(), SourceCodeScanner { override fun getApplicableMethodNames(): List = listOf( - LaunchPermissionRequest.shortName, LaunchMultiplePermissionsRequest.shortName + LaunchPermissionRequest.shortName, LaunchMultiplePermissionsRequest.shortName, + LaunchPermissionRequestOrAppSettings.shortName, + LaunchMultiplePermissionsRequestOrAppSettings.shortName, + OpenAppSettings.shortName ) override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { @@ -87,5 +90,11 @@ private fun PsiMethod.isInPackageName(packageName: PackageName): Boolean = private val PermissionsPackageName = Package("com.google.accompanist.permissions") private val LaunchPermissionRequest = Name(PermissionsPackageName, "launchPermissionRequest") +private val LaunchPermissionRequestOrAppSettings = + Name(PermissionsPackageName, "launchPermissionRequestOrAppSettings") private val LaunchMultiplePermissionsRequest = Name(PermissionsPackageName, "launchMultiplePermissionRequest") +private val LaunchMultiplePermissionsRequestOrAppSettings = + Name(PermissionsPackageName, "launchMultiplePermissionRequestOrAppSettings") +private val OpenAppSettings = + Name(PermissionsPackageName, "openAppSettings") diff --git a/permissions/api/current.api b/permissions/api/current.api index 9348beb05..4a121b9a6 100644 --- a/permissions/api/current.api +++ b/permissions/api/current.api @@ -5,18 +5,31 @@ package com.google.accompanist.permissions { } @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public interface MultiplePermissionsState { + method public boolean getAllNotGrantedPermissionsArePermanentlyDenied(); method public boolean getAllPermissionsGranted(); method public java.util.List getPermissions(); - method public java.util.List getRevokedPermissions(); method public boolean getShouldShowRationale(); + method public boolean isNotRequested(); method public void launchMultiplePermissionRequest(); + method public void openAppSettings(); + property public abstract boolean allNotGrantedPermissionsArePermanentlyDenied; property public abstract boolean allPermissionsGranted; + property public abstract boolean isNotRequested; property public abstract java.util.List permissions; - property public abstract java.util.List revokedPermissions; property public abstract boolean shouldShowRationale; } public final class MultiplePermissionsStateKt { + method public static inline java.util.List getDeniedPermissions(com.google.accompanist.permissions.MultiplePermissionsState); + method public static inline java.util.List getGrantedPermissions(com.google.accompanist.permissions.MultiplePermissionsState); + method public static inline java.util.List getNotGrantedPermissions(com.google.accompanist.permissions.MultiplePermissionsState); + method public static inline java.util.List getPermanentlyDeniedPermissions(com.google.accompanist.permissions.MultiplePermissionsState); + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static boolean isDenied(com.google.accompanist.permissions.MultiplePermissionsState, String permission); + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static boolean isGranted(com.google.accompanist.permissions.MultiplePermissionsState, String permission); + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static boolean isNotGranted(com.google.accompanist.permissions.MultiplePermissionsState, String permission); + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static boolean isPermanentlyDenied(com.google.accompanist.permissions.MultiplePermissionsState, String permission); + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static void launchMultiplePermissionRequestOrAppSettings(com.google.accompanist.permissions.MultiplePermissionsState); + method @androidx.compose.runtime.Composable public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(String permission, String![] otherPermissions, optional kotlin.jvm.functions.Function1,kotlin.Unit> onPermissionsResult); method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(java.util.List permissions, optional kotlin.jvm.functions.Function1,kotlin.Unit> onPermissionsResult); } @@ -30,32 +43,45 @@ package com.google.accompanist.permissions { method public String getPermission(); method public com.google.accompanist.permissions.PermissionStatus getStatus(); method public void launchPermissionRequest(); + method public void openAppSettings(); property public abstract String permission; property public abstract com.google.accompanist.permissions.PermissionStatus status; } public final class PermissionStateKt { + method @com.google.accompanist.permissions.ExperimentalPermissionsApi public static void launchPermissionRequestOrAppSettings(com.google.accompanist.permissions.PermissionState); method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.PermissionState rememberPermissionState(String permission, optional kotlin.jvm.functions.Function1 onPermissionResult); } @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public sealed interface PermissionStatus { } - public static final class PermissionStatus.Denied implements com.google.accompanist.permissions.PermissionStatus { - ctor public PermissionStatus.Denied(boolean shouldShowRationale); - method public boolean component1(); - method public com.google.accompanist.permissions.PermissionStatus.Denied copy(boolean shouldShowRationale); - method public boolean getShouldShowRationale(); - property public final boolean shouldShowRationale; - } - public static final class PermissionStatus.Granted implements com.google.accompanist.permissions.PermissionStatus { field public static final com.google.accompanist.permissions.PermissionStatus.Granted INSTANCE; } + public static sealed interface PermissionStatus.NotGranted extends com.google.accompanist.permissions.PermissionStatus { + } + + public static final class PermissionStatus.NotGranted.Denied implements com.google.accompanist.permissions.PermissionStatus.NotGranted { + field public static final com.google.accompanist.permissions.PermissionStatus.NotGranted.Denied INSTANCE; + } + + public static final class PermissionStatus.NotGranted.NotRequested implements com.google.accompanist.permissions.PermissionStatus.NotGranted { + field public static final com.google.accompanist.permissions.PermissionStatus.NotGranted.NotRequested INSTANCE; + } + + public static final class PermissionStatus.NotGranted.PermanentlyDenied implements com.google.accompanist.permissions.PermissionStatus.NotGranted { + field public static final com.google.accompanist.permissions.PermissionStatus.NotGranted.PermanentlyDenied INSTANCE; + } + public final class PermissionsUtilKt { method public static boolean getShouldShowRationale(com.google.accompanist.permissions.PermissionStatus); + method public static boolean isDenied(com.google.accompanist.permissions.PermissionStatus); method public static boolean isGranted(com.google.accompanist.permissions.PermissionStatus); + method public static boolean isNotGranted(com.google.accompanist.permissions.PermissionStatus); + method public static boolean isNotRequested(com.google.accompanist.permissions.PermissionStatus); + method public static boolean isPermanentlyDenied(com.google.accompanist.permissions.PermissionStatus); } } diff --git a/permissions/src/main/java/com/google/accompanist/permissions/MultiplePermissionsState.kt b/permissions/src/main/java/com/google/accompanist/permissions/MultiplePermissionsState.kt index 14bcbc6d6..64a2960f4 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/MultiplePermissionsState.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/MultiplePermissionsState.kt @@ -17,7 +17,29 @@ package com.google.accompanist.permissions import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.util.fastMap + +/** + * Creates a [MultiplePermissionsState] that is remembered across compositions. + * + * @param permission a permission to control and observe. + * @param otherPermissions additional permissions to control and observe. + * @param onPermissionsResult will be called with whether or not the user granted the permissions + * after [MultiplePermissionsState.launchMultiplePermissionRequest] is called. + */ +@OptIn(ExperimentalPermissionsApi::class) +@Composable +public fun rememberMultiplePermissionsState( + permission: String, + vararg otherPermissions: String, + onPermissionsResult: (Map) -> Unit = {} +): MultiplePermissionsState = rememberMultiplePermissionsState( + permissions = listOf(permission) + otherPermissions.toList(), + onPermissionsResult = onPermissionsResult, +) /** * Creates a [MultiplePermissionsState] that is remembered across compositions. @@ -35,7 +57,10 @@ public fun rememberMultiplePermissionsState( permissions: List, onPermissionsResult: (Map) -> Unit = {} ): MultiplePermissionsState { - return rememberMutableMultiplePermissionsState(permissions, onPermissionsResult) + return when { + LocalInspectionMode.current -> PreviewMultiplePermissionsState(permissions) + else -> rememberMutableMultiplePermissionsState(permissions, onPermissionsResult) + } } /** @@ -56,15 +81,20 @@ public interface MultiplePermissionsState { public val permissions: List /** - * List of permissions revoked by the user. + * When `true`, the user hasn't requested [permissions] yet. */ - public val revokedPermissions: List + public val isNotRequested: Boolean /** * When `true`, the user has granted all [permissions]. */ public val allPermissionsGranted: Boolean + /** + * When `true`, the user has permanently denied all [permissions] that haven't been granted. + */ + public val allNotGrantedPermissionsArePermanentlyDenied: Boolean + /** * When `true`, the user should be presented with a rationale. */ @@ -82,4 +112,118 @@ public interface MultiplePermissionsState { * This behavior varies depending on the Android level API. */ public fun launchMultiplePermissionRequest(): Unit + + /** + * Open the app settings page. + * + * If the first request permission in [permissions] is [android.Manifest.permission.POST_NOTIFICATIONS] then + * the notification settings will be opened. Otherwise the app's settings will be opened. + * + * This should always be triggered from non-composable scope, for example, from a side-effect + * or a non-composable callback. Otherwise, this will result in an IllegalStateException. + */ + public fun openAppSettings(): Unit +} + +/** + * Calls [MultiplePermissionsState.openAppSettings] when + * [MultiplePermissionsState.allNotGrantedPermissionsArePermanentlyDenied] is `true`; otherwise calls + * [MultiplePermissionsState.launchMultiplePermissionRequest]. + * + * This should always be triggered from non-composable scope, for example, from a side-effect + * or a non-composable callback. Otherwise, this will result in an IllegalStateException. + */ +@ExperimentalPermissionsApi +public fun MultiplePermissionsState.launchMultiplePermissionRequestOrAppSettings() { + when { + allNotGrantedPermissionsArePermanentlyDenied -> openAppSettings() + else -> launchMultiplePermissionRequest() + } +} + +/** + * List of permissions granted by the user. + */ +@ExperimentalPermissionsApi +public inline val MultiplePermissionsState.grantedPermissions: List + get() = permissions.filter { it.status.isGranted } + +/** + * List of permissions not granted by the user. + */ +@ExperimentalPermissionsApi +public inline val MultiplePermissionsState.notGrantedPermissions: List + get() = permissions.filter { it.status.isNotGranted } + +/** + * List of permissions denied by the user. + */ +@ExperimentalPermissionsApi +public inline val MultiplePermissionsState.deniedPermissions: List + get() = permissions.filter { it.status.isDenied } + +/** + * List of permissions permanently denied by the user. + */ +@ExperimentalPermissionsApi +public inline val MultiplePermissionsState.permanentlyDeniedPermissions: List + get() = permissions.filter { it.status.isPermanentlyDenied } + +/** + * Returns `true` if [permission] was granted, otherwise `false`. + * + * If [permission] wasn't requested a [IllegalArgumentException] will be thrown. + */ +@ExperimentalPermissionsApi +public fun MultiplePermissionsState.isGranted(permission: String): Boolean = + requireNotNull(permissions.find { it.permission == permission }) { + "$permission is not present in the list of requested permissions" + }.status.isGranted + +/** + * Returns `true` if [permission] was not granted, otherwise `false`. + * + * If [permission] wasn't requested a [IllegalArgumentException] will be thrown. + */ +@ExperimentalPermissionsApi +public fun MultiplePermissionsState.isNotGranted(permission: String): Boolean = + requireNotNull(permissions.find { it.permission == permission }) { + "$permission is not present in the list of requested permissions" + }.status.isNotGranted + +/** + * Returns `true` if [permission] was denied, otherwise `false`. + * + * If [permission] wasn't requested a [IllegalArgumentException] will be thrown. + */ +@ExperimentalPermissionsApi +public fun MultiplePermissionsState.isDenied(permission: String): Boolean = + requireNotNull(permissions.find { it.permission == permission }) { + "$permission is not present in the list of requested permissions" + }.status.isDenied + +/** + * Returns `true` if [permission] was permanently denied, otherwise `false`. + * + * If [permission] wasn't requested a [IllegalArgumentException] will be thrown. + */ +@ExperimentalPermissionsApi +public fun MultiplePermissionsState.isPermanentlyDenied(permission: String): Boolean = + requireNotNull(permissions.find { it.permission == permission }) { + "$permission is not present in the list of requested permissions" + }.status.isPermanentlyDenied + +@OptIn(ExperimentalPermissionsApi::class) +@Immutable +private class PreviewMultiplePermissionsState( + permissions: List +) : MultiplePermissionsState { + override val permissions: List = permissions.fastMap(::PreviewPermissionState) + override val isNotRequested: Boolean = true + override val allPermissionsGranted: Boolean = false + override val allNotGrantedPermissionsArePermanentlyDenied: Boolean = false + override val shouldShowRationale: Boolean = false + + override fun launchMultiplePermissionRequest() {} + override fun openAppSettings() {} } diff --git a/permissions/src/main/java/com/google/accompanist/permissions/MutableMultiplePermissionsState.kt b/permissions/src/main/java/com/google/accompanist/permissions/MutableMultiplePermissionsState.kt index 4bf119ddc..dcb9d87a5 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/MutableMultiplePermissionsState.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/MutableMultiplePermissionsState.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.util.fastAll /** * Creates a [MultiplePermissionsState] that is remembered across compositions. @@ -57,6 +58,7 @@ internal fun rememberMutableMultiplePermissionsState( val launcher = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissionsResult -> + multiplePermissionsState.isNotRequested = false multiplePermissionsState.updatePermissionsStatus(permissionsResult) onPermissionsResult(permissionsResult) } @@ -117,15 +119,16 @@ internal class MutableMultiplePermissionsState( override val permissions: List = mutablePermissions - override val revokedPermissions: List by derivedStateOf { - permissions.filter { it.status != PermissionStatus.Granted } - } + override var isNotRequested: Boolean = true override val allPermissionsGranted: Boolean by derivedStateOf { permissions.all { it.status.isGranted } || // Up to date when the lifecycle is resumed - revokedPermissions.isEmpty() // Up to date when the user launches the action + notGrantedPermissions.isEmpty() // Up to date when the user launches the action } + override val allNotGrantedPermissionsArePermanentlyDenied: Boolean + get() = permissions.fastAll { it.status.isGranted || it.status.isPermanentlyDenied } + override val shouldShowRationale: Boolean by derivedStateOf { permissions.any { it.status.shouldShowRationale } } @@ -136,6 +139,10 @@ internal class MutableMultiplePermissionsState( ) ?: throw IllegalStateException("ActivityResultLauncher cannot be null") } + override fun openAppSettings() { + permanentlyDeniedPermissions.firstOrNull()?.openAppSettings() + } + internal var launcher: ActivityResultLauncher>? = null internal fun updatePermissionsStatus(permissionsStatus: Map) { diff --git a/permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt b/permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt index 482b68701..210abb633 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt @@ -56,6 +56,7 @@ internal fun rememberMutablePermissionState( // Remember RequestPermission launcher and assign it to permissionState val launcher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { + permissionState.isPermissionPostRequest = true permissionState.refreshPermissionStatus() onPermissionResult(it) } @@ -86,6 +87,9 @@ internal class MutablePermissionState( private val activity: Activity ) : PermissionState { + internal var isPreviousStatusNotRequested = true + internal var isPermissionPostRequest = false + override var status: PermissionStatus by mutableStateOf(getPermissionStatus()) override fun launchPermissionRequest() { @@ -94,6 +98,10 @@ internal class MutablePermissionState( ) ?: throw IllegalStateException("ActivityResultLauncher cannot be null") } + override fun openAppSettings() { + activity.openAppSettings(permission) + } + internal var launcher: ActivityResultLauncher? = null internal fun refreshPermissionStatus() { @@ -105,7 +113,32 @@ internal class MutablePermissionState( return if (hasPermission) { PermissionStatus.Granted } else { - PermissionStatus.Denied(activity.shouldShowRationale(permission)) + val shouldShowRationale = activity.shouldShowRationale(permission) + when { + isPermissionPostRequest -> { + when { + shouldShowRationale -> PermissionStatus.NotGranted.Denied + else -> when { + // we can't go from NotRequested->PermanentlyDenied since there's + // no way to determine if the permission was already PermanentlyDenied + // or if the permission request was canceled. + // isPreviousStatusNotRequested forces an extra step by inserting a + // Denied status even though it should be PermanentlyDenied + // since this will allow us to handle the case where the permission + // request was canceled + isPreviousStatusNotRequested -> PermissionStatus.NotGranted.Denied + else -> PermissionStatus.NotGranted.PermanentlyDenied + } + }.also { + isPreviousStatusNotRequested = false + } + } + + else -> when { + shouldShowRationale -> PermissionStatus.NotGranted.Denied + else -> PermissionStatus.NotGranted.NotRequested + } + } } } } diff --git a/permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt b/permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt index a97a47f66..a199a6692 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt @@ -17,7 +17,9 @@ package com.google.accompanist.permissions import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import androidx.compose.ui.platform.LocalInspectionMode /** * Creates a [PermissionState] that is remembered across compositions. @@ -35,7 +37,10 @@ public fun rememberPermissionState( permission: String, onPermissionResult: (Boolean) -> Unit = {} ): PermissionState { - return rememberMutablePermissionState(permission, onPermissionResult) + return when { + LocalInspectionMode.current -> PreviewPermissionState(permission) + else -> rememberMutablePermissionState(permission, onPermissionResult) + } } /** @@ -72,4 +77,40 @@ public interface PermissionState { * This behavior varies depending on the Android level API. */ public fun launchPermissionRequest(): Unit + + /** + * Open the app settings page. + * + * If the [permission] is [android.Manifest.permission.POST_NOTIFICATIONS] then + * the notification settings will be opened. Otherwise the app's settings will be opened. + * + * This should always be triggered from non-composable scope, for example, from a side-effect + * or a non-composable callback. Otherwise, this will result in an IllegalStateException. + */ + public fun openAppSettings(): Unit +} + +/** + * Calls [PermissionState.launchPermissionRequest] or [PermissionState.openAppSettings] + * depending on the state of [PermissionState.status]. + * + * This should always be triggered from non-composable scope, for example, from a side-effect + * or a non-composable callback. Otherwise, this will result in an IllegalStateException. + */ +@ExperimentalPermissionsApi +public fun PermissionState.launchPermissionRequestOrAppSettings() { + when (status) { + PermissionStatus.NotGranted.PermanentlyDenied -> openAppSettings() + else -> launchPermissionRequest() + } +} + +@OptIn(ExperimentalPermissionsApi::class) +@Immutable +internal class PreviewPermissionState( + override val permission: String +) : PermissionState { + override val status: PermissionStatus = PermissionStatus.NotGranted.NotRequested + override fun launchPermissionRequest() {} + override fun openAppSettings() {} } diff --git a/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt b/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt index 9ba75827b..376f16198 100644 --- a/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt +++ b/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt @@ -19,7 +19,11 @@ package com.google.accompanist.permissions import android.app.Activity import android.content.Context import android.content.ContextWrapper +import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.provider.Settings import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.Stable @@ -40,10 +44,12 @@ public annotation class ExperimentalPermissionsApi @ExperimentalPermissionsApi @Stable public sealed interface PermissionStatus { - public object Granted : PermissionStatus - public data class Denied( - val shouldShowRationale: Boolean - ) : PermissionStatus + public data object Granted : PermissionStatus + public sealed interface NotGranted : PermissionStatus { + public data object NotRequested : NotGranted + public data object Denied : NotGranted + public data object PermanentlyDenied : NotGranted + } } /** @@ -53,15 +59,40 @@ public sealed interface PermissionStatus { public val PermissionStatus.isGranted: Boolean get() = this == PermissionStatus.Granted +/** + * `true` if the permission is not granted. + */ +@ExperimentalPermissionsApi +public val PermissionStatus.isNotGranted: Boolean + get() = this is PermissionStatus.NotGranted + +/** + * `true` if the permission has not been requested. + */ +@ExperimentalPermissionsApi +public val PermissionStatus.isNotRequested: Boolean + get() = this == PermissionStatus.NotGranted.NotRequested + +/** + * `true` if the permission is denied. + */ +@ExperimentalPermissionsApi +public val PermissionStatus.isDenied: Boolean + get() = this == PermissionStatus.NotGranted.Denied + /** * `true` if a rationale should be presented to the user. */ @ExperimentalPermissionsApi public val PermissionStatus.shouldShowRationale: Boolean - get() = when (this) { - PermissionStatus.Granted -> false - is PermissionStatus.Denied -> shouldShowRationale - } + get() = isDenied + +/** + * `true` if the permission is permanently denied. + */ +@ExperimentalPermissionsApi +public val PermissionStatus.isPermanentlyDenied: Boolean + get() = this == PermissionStatus.NotGranted.PermanentlyDenied /** * Effect that updates the `hasPermission` state of a revoked [MutablePermissionState] permission @@ -145,3 +176,23 @@ internal fun Context.checkPermission(permission: String): Boolean { internal fun Activity.shouldShowRationale(permission: String): Boolean { return ActivityCompat.shouldShowRequestPermissionRationale(this, permission) } + +internal fun Activity.openAppSettings(permission: String?) { + if (permission == android.Manifest.permission.POST_NOTIFICATIONS) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + startActivity( + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra(Settings.EXTRA_APP_PACKAGE, packageName) + } + ) + } + } else { + startActivity( + Intent().apply { + action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + data = Uri.fromParts("package", packageName, null) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + ) + } +} diff --git a/sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt b/sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt index eec1e8224..82e5188fb 100644 --- a/sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt +++ b/sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.deniedPermissions import com.google.accompanist.permissions.rememberMultiplePermissionsState import com.google.accompanist.sample.AccompanistSampleTheme @@ -59,7 +60,7 @@ private fun Sample() { Column { val allPermissionsRevoked = locationPermissionsState.permissions.size == - locationPermissionsState.revokedPermissions.size + locationPermissionsState.deniedPermissions.size val textToShow = if (!allPermissionsRevoked) { // If not all the permissions are revoked, it's because the user accepted the COARSE diff --git a/sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt b/sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt index c4369df93..5f4aca88e 100644 --- a/sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt +++ b/sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.unit.dp import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.MultiplePermissionsState import com.google.accompanist.permissions.PermissionState +import com.google.accompanist.permissions.deniedPermissions import com.google.accompanist.permissions.rememberMultiplePermissionsState import com.google.accompanist.sample.AccompanistSampleTheme @@ -62,7 +63,7 @@ private fun Sample(multiplePermissionsState: MultiplePermissionsState) { Column { Text( getTextToShowGivenPermissions( - multiplePermissionsState.revokedPermissions, + multiplePermissionsState.deniedPermissions, multiplePermissionsState.shouldShowRationale ) )