Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Example that requests permissions from a composable #53

Open
dri94 opened this issue Jun 17, 2020 · 7 comments · May be fixed by #106
Open

Add Example that requests permissions from a composable #53

dri94 opened this issue Jun 17, 2020 · 7 comments · May be fixed by #106
Assignees
Labels
example request Request for a new example in Compose

Comments

@dri94
Copy link

dri94 commented Jun 17, 2020

It is apparent how we could request permissions from the MainActivity. However what if we are several layers deep in our app, how would we request runtime permission (such as location)?

@vinaygaba vinaygaba added the example request Request for a new example in Compose label Jun 17, 2020
@vinaygaba vinaygaba added this to To do in Learn Jetpack Compose By Example via automation Jun 17, 2020
@vinaygaba
Copy link
Owner

Thanks for the request! This would be a very useful example so I'll add this soon. In the meantime, if you need something sooner, here's a related discussion - https://kotlinlang.slack.com/archives/CJLTWPH7S/p1590597865122200

@qrezet
Copy link

qrezet commented Oct 15, 2020

Hi @vinaygaba ,

I did something like this:

class PermissionHandler(
   private val activity: AppCompatActivity
) {

   fun requestPermission(
       permission: String,
       onResultReceived: (PermissionResult) -> Unit,
   ) {
       val requestPermissionLauncher = activity.registerForActivityResult(
           ActivityResultContracts.RequestPermission(),
       ) { granted ->
           onResultReceived(
               if (granted)
                   PermissionResult.Granted
               else
                   PermissionResult.Denied
           )
       }

       when {
           ContextCompat.checkSelfPermission(
               activity,
               permission
           ) == PackageManager.PERMISSION_GRANTED -> {
               onResultReceived(PermissionResult.Granted)
           }
           ActivityCompat.shouldShowRequestPermissionRationale(
               activity,
               permission
           ) -> {
               onResultReceived(PermissionResult.ShowRationale)
           }
           else -> requestPermissionLauncher.launch(permission)
       }
   }

}

@Composable
fun requestPermission(
   permission: String,
): State<PermissionResult> {
   val permissionHandler = AmbientPermissionHandler.current

   val permissionResult = remember(permission) {
       mutableStateOf<PermissionResult>(PermissionResult.Requesting)
   }

   remember(permission) {
       permissionHandler.requestPermission(permission) {
           permissionResult.value = it
       }
   }

   return permissionResult
}

sealed class PermissionResult {
   object Requesting : PermissionResult()

   object ShowRationale : PermissionResult()

   object Denied : PermissionResult()

   object Granted : PermissionResult()
}

val AmbientPermissionHandler: ProvidableAmbient<PermissionHandler> =
   staticAmbientOf { throw IllegalStateException("permission handler is not initialized") }

and then provide PermissionHandler as an ambient like so:

Providers(AmbientPermissionHandler provides permissionHandler) {
    ....
}

My idea was to be able to use it like:

val permissionResult by requestPermission(permission = Manifest.permission.CAMERA)
when (missionResult) {
     PermissionResult.Requesting -> Text("Requesting")
     PermissionResult.ShowRationale -> Text("Show Rationale")
     PermissionResult.Denied -> Text("Denied")
     PermissionResult.Granted -> FragmentContainer(fragment = SelfieFragment())
}

The problem is when I get to the Composable where the code above is located, I get the error:

LifecycleOwner ... is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
        at androidx.activity.result.ActivityResultRegistry.register 
        ...

Not really sure how to solve the issue. It seems to me that at the point of a @Composable the lifecycle is already at LifecycleState.RESUMED and so registerForActivityResult(...) throws the error.

I was wondering whether you've already implemented permissions and maybe have some idea on how to solve this or do it a different way?

@vinaygaba
Copy link
Owner

@dri94 I am aware of this gist by one of the developers on Compose that shows how they are thinking about permission handling - https://gist.github.com/objcode/775fe45127fd40f17932f672ee203f72#file-permissions-kt-L78

@qrezet
Copy link

qrezet commented Oct 15, 2020

I think you tagged the wrong person but Thanks! It was the right call to ask about it here. Thank you very much.

@vinaygaba
Copy link
Owner

Oh my bad 🙈

@vinaygaba
Copy link
Owner

Update: You should probably be using Accompanist for permission support in Compose - https://github.com/google/accompanist/tree/main/permissions

@5AbhishekSaxena
Copy link
Contributor

5AbhishekSaxena commented Jan 9, 2023

I'd like to work on this issue, please assign it to me. @vinaygaba

@5AbhishekSaxena 5AbhishekSaxena linked a pull request Mar 7, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
example request Request for a new example in Compose
Development

Successfully merging a pull request may close this issue.

4 participants