Visitar URL original
[Permissions] status.shouldShowRationale is always false by default · Issue #1066 · google/accompanist · GitHub
Skip to content

[Permissions] status.shouldShowRationale is always false by default #1066

Closed

Description

@madhurgupta10

Description

implementation "com.google.accompanist:accompanist-permissions:0.24.3-alpha"

status.shouldShowRationale is always false by default even when the application is installed for the first time or when app data is cleared.

When the user has not decided if permission should be granted or denied and don't ask ideally we should be able to ask the user for permission and show the prompt.

Steps to reproduce

  1. Install the Application for the first time
  2. Permission is permanently denied by default and shouldShowRationale is false
  3. Go to settings to enable the permission and go back to the app. Permission is granted.
  4. Go back to settings and disable permission and now shouldShowRationale is true and we can prompt the user for permission in the app.

Expected behavior

  1. Install the Application for the first time
  2. Permission should not be permanently denied by default and shouldShowRationale should be true

Additional context

fun PermissionStatus.isPermanentlyDenied(): Boolean {
    return !shouldShowRationale && !isGranted
}

@Composable
fun GetPermission() {
      val locationPermissionState = rememberPermissionState(Manifest.permission.ACCESS_COARSE_LOCATION)
      when {
              locationPermissionState.status.isGranted -> {
                  // location permission is granted
              }
              locationPermissionState.status.shouldShowRationale -> {
                 // location permission is not permanently denied
              }
              locationPermissionState.status.isPermanentlyDenied() -> {
                  //  location permission is permanently denied
          }
}

Tested on Android Version 10 (API level 29)

Activity

  1. jari-kuipers commented on Mar 9, 2022

    @jari-kuipers

    Also encountering this.

  2. manuelvicnt commented on Mar 10, 2022

    @manuelvicnt
    Contributor

    Nothing we can do here in Accompanist, unfortunately. That's how permissions work. The status.shouldShowRationale value comes directly from ActivityCompat.shouldShowRequestPermissionRationale, it's the system what's giving us that value

  3. ijero commented on Mar 11, 2022

    @ijero

    Try this one:

    class RequestPermissionState(initRequest: Boolean, val permissions: List<String>) {
        var request by mutableStateOf(initRequest)
    }
    
    @Composable
    fun rememberRequestPermissionsState(
        initRequest: Boolean = false,
        permissions: List<String>
    ): RequestPermissionState {
        return remember {
            RequestPermissionState(initRequest, permissions)
        }
    }
    
    @OptIn(ExperimentalPermissionsApi::class)
    @Composable
    fun RequestPermissions(requestState: RequestPermissionState, onSuccess: () -> Unit) {
        val context = LocalContext.current
        val state = rememberMultiplePermissionsState(permissions = requestState.permissions) { it ->
            val needToSetting = it.filter {
                !ActivityCompat.shouldShowRequestPermissionRationale(
                    context.findActivity(),
                    it.key
                ) && !it.value
            }.isNotEmpty()
            if (needToSetting) {
                val intent = Intent(
                    Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                    Uri.parse("package:" + context.packageName)
                )
                context.startActivity(intent)
            }
        }
    
        if (requestState.request) {
            requestState.request = false
    
            if (state.allPermissionsGranted) {
                onSuccess()
            } else {
                LaunchedEffect(key1 = Unit) {
                    state.launchMultiplePermissionRequest()
                }
            }
    
        }
    }
    
    internal fun Context.findActivity(): Activity {
        var context = this
        while (context is ContextWrapper) {
            if (context is Activity) return context
            context = context.baseContext
        }
        throw IllegalStateException("Permissions should be called in the context of an Activity")
    }
    

    Example:

        // 判断权限
        val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            listOf(
                Manifest.permission.MANAGE_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO,
            )
        } else {
            listOf(
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO,
            )
        }
        val requestState = rememberRequestPermissionsState(permissions = permissions)
        RequestPermissions(requestState = requestState) {
            // Your business logic
        }
  4. leoull commented on Jul 15, 2022

    @leoull

    I modified the above and added more callbacks:

    /**
     * request permission or not
     */
    class RequestPermissionState(initRequest: Boolean, val permission: String) {
        var requestPermission by mutableStateOf(initRequest)
    }
    
    /**
     * Remember whether permission should be requested or not, true initially by default
     */
    @Composable
    fun rememberRequestPermissionsState(
        initRequest: Boolean = true,
        permissions: String
    ): RequestPermissionState {
        return remember {
            RequestPermissionState(initRequest, permissions)
        }
    }
    
    /**
     * Permission requester
     */
    @OptIn(ExperimentalPermissionsApi::class)
    @Composable
    fun RequestPermission(
        context: Context,
        requestState: RequestPermissionState,
        granted: () -> Unit,
        showRational: () -> Unit,
        permanentlyDenied: () -> Unit
    ) {
        val permissionState =
            rememberPermissionState(permission = requestState.permission) { isGranted ->
                // This block will be triggered after the user chooses to grant or deny the permission
                // and we can tell if the user permanently declines or if we need to show rational
                val permissionPermanentlyDenied = !ActivityCompat.shouldShowRequestPermissionRationale(
                    context.findActivity(), requestState.permission
                ) && !isGranted
    
                if (permissionPermanentlyDenied) {
                    permanentlyDenied()
                } else if (!isGranted) {
                    showRational()
                }
            }
    
        // If requestPermission, then launchPermissionRequest and the user will be able to choose
        // to grant or deny the permission.
        // After that, the RequestPermission will recompose and permissionState above will be triggered
        // and we can differentiate whether the permission is permanently declined or whether rational
        // should be shown
        if (requestState.requestPermission) {
            requestState.requestPermission = false
            if (permissionState.status.isGranted) {
                granted()
            } else {
                LaunchedEffect(key1 = Unit) {
                    permissionState.launchPermissionRequest()
                }
            }
        }
    }

    Example:

        val context = LocalContext.current
        
        val permissionRequestState = rememberRequestPermissionsState(
            permissions = Manifest.permission.ACCESS_FINE_LOCATION
        )
        RequestPermission(
            context = context,
            requestState = permissionRequestState,
            granted = {
                // do stuff here
            },
            showRational = {
                // show Rational here
            },
            permanentlyDenied = {
               context. navigateToAppSettings()
            }
        )
    
    fun Context.navigateToAppSettings() {
        this.startActivity(
            Intent(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                Uri.fromParts("package", this.packageName, null)
            )
        )
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions