The ULTIMATE Permission Handling Guide (Showing rationale + Permanently Declined)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi guys and welcome back to a new video in this video I will show you yeah basically a full guide on how you can manage permissions in Jetpack compose and maybe you already saw my earlier video about permission handling and compose the reason I made this new one is that the old one is a little bit outdated so the old videos still use the accompanist Library which was kind of a helper library for jet to compose but this Library gets more and more out of date kind of so I won't make this new video and show you my approach to permission handling just using the normal framework functions this will really not only be that I show you hey this is how you make a permission request or so now I will show you my whole approach to managing permissions and that includes showing a permission rational uh leading the user to the app settings screen if they permanently decline the permission and as you can already see on my screen requesting a single permission what happens if you want to request multiple permissions at once what happens if you want to show multiple request permission rationals at once um so I will show you a scalable approach in this video that you can reuse in all of your projects so to just quickly show you what we will build if we click on request one permission and then yeah I just request the record not the camera permission it's called which just a single one here and let's say we decline this then we will see permission required okay we see a dialog that the camera provision is required so that can that others can see in a call or whatever you use this permission for if we then click OK it will request the permission again but as you maybe know in Android if the user declined a permission twice then this permission dialog won't show up again so in that case the only place where the user could still Grant the permission to our app is in the app settings so they would need to do that manually so what we could do in that case so if we decline this again we will see another dialog permission required it seems you permanently declined camera permission you can go to the app settings to granted and if we now click Grant permission our app will send us to the app settings and here we can then click on permissions and actually say okay the camera permission allow allow only while using the app and that way we can then still grant our permission so that's really the only thing we can still do to send the user to the app settings and telling them hey please enable it back and the same of course works if you want to request multiple permissions at once so very commonly for example record audio and Camera permission together for video call in my case it's just random permissions this is record audio and phone calling permission let's say we Grant record audio and say while using the app then it comes to phone calling let's say we don't allow that then we will again see our dialog which yeah this time refers to phone colleagues or to be able to call your friends please Grant permission to this app clicking OK if we then hit allow then it will be just fine and if we click don't allow then yeah we can get this dialog that we permanently declined phone call permission and we can click Grant permission to enable it back in the app settings right here so allow and if all these are showing allow then these buttons won't do anything anymore since we then have these permissions now I'll just show you an approach that really scales so even if you want to request four permissions at once how you can yeah show all these different dialogues with a minimum amount of code and for that let's dive into an empty jetpack compose project where we just want to start adding our review model and dependency in our Gradle Builder Gradle app file scrolling down and adding this line here feel free to write that off or copy it from my GitHub repository down below others just so that we can get a view model reference in Jetpack compose let's close this again and then start to implement our view model which will be very simple in this case let's call it main view model critic class make that a view model of course and well what will we have in here what kind of states in this case because we want to show different dialogues one after another because the user could potentially decline all these permissions that we that we request at once we kind of need to queue in these dialogues and what would better for a queue than a queue data structure so let's Implement that queue I will do that as a normal State list of compose so it's not a real Q data structure but we'll kind of simulate it and let's call that Val visible permission dialog queue and that is immutable State list of strings so here we just pass the permission strings um of course this way you will bind your review model to Android dependencies which permissions are so this won't be locally unit testable if you want to avoid that it's a bit more work and a bit more code but you can still do that then you would need to kind of have your own enum for every single permission you request and then pass that instead but for the sake of Simplicity let's just keep it like this and it will be fine if you don't plan to local a unit test this view model what we then need is a function to actually dismiss a dialog so when we either click on OK or outside of the dialog what we then want to do is then we basically just want to pop the last entry of our queue so the way a queue works is well so the first entry that gets in the queue is also the first entry that leaves the queue that is what we call fifo so first in first out so let's say um this would be our queue here and we would for example first request our um let's say camera permission then we would simply add that to our queue of course if we would then request record audio permission for example then we would insert that at the start of our queue so record audio like this and if we then show the dialog then we will first of all show the dialog of the entry that got added in here first if we then dismiss that camera dialog what we'll do is we will remove that entry so the last entry from our list so that the next dialog that gets shown is our record audio one so in the end we just want to say we have a visible permission.q and we remove last yeah we just want to remove the last entry which will be the entry that got added in this queue first and we also want to have a function on permission result which we call when we get permission results that takes in the permission we got the result for and whether that was granted or not and if that was not granted we want to add that to our Queue at the first index so visible permissions download Q add at the index of zero I want to add our permission and that will then yeah queue in our permission dialog at the given position let's not go to main activity or actually first the Manifest to add our permissions here or declare them users permission record audio duplicate that twice we want to have it for camera permission and phone column so call phone permission if you have that it seems like we also need to synchronize Gradle here if you have that we're going to go to main activity declare overview model view model this green one and we just say that's our main view model we then have our dialog queue which is simply new model visible permission dialogue so let's start with requesting one permission only because that's of course easier how does that work we first of all want to build our little UI with our two buttons which we'll put in a column we say we have a modifier of modifier fill Max size we say the vertical Arrangement is Center and the horizontal alignment as well so Center horizontally and in here we will then have a button like this let's remove the unclick listener actually the content I'll let no let's keep it let's just have a text here for that button that says request one permission we can then copy this button and put it below a spacer here so we just have a little bit of spacing of 16 DP here in our layout in our column and then put our button down below which will be for multiple permissions so when we now click this top button to request one permission we of course want to do exactly that how can we now do one permission request in Jetpack compose for that what we'll use is a so-called activity result launcher so we will launch another activity to get a certain result in this case the other activity would kind of be the permission dialogue you can think of and to declare these result launches we can go up here and say we have a camera permission result launcher and that's equal to remember a launcher for activity result and here we need to define a contract so the contract basically defines which activity gets launched for what kind of results so we basically just have a contract between our activity and the activity that gets launched and then we get a result out of that and the contract we can Define with activity result contracts which is just a collection of these common contracts like capture video to get a video or we use request permission you can see there's also request multiple permissions but let's start with request one permission declare that in here and then in on result that gets cold when the user actually selected the corresponding permission or rather granted it or declined it so here we get whether that was granted or not and we can say viewmodel on permission result the permission we requested was camera in this in this case so manifest permission camera and is granted it's just yeah it's granted what we get in here now right now we only declared this launcher we of course also need to launch it to fire off the permission request which you can then do in our own click listener right here so camera permission result launcher dot launch and the input will be our permission that we want to request so manifest permission camera and if we Now launch this app here on our device we should already be able to accept or decline camera permission but we are not able yet to actually see the permission ratio that explains to the user why that permission is needed and asks them to accept it again so if we now click request one permission we will see our permission dialog and yeah whatever we now click it will stay at showing that dialog just once so let's just dismiss this here by clicking outside and see how we can now Implement our mechanism to show our different rational dialogues and for that I will go to our root package and create a new composable which we'll call permission dialog select file here make that a composable function call it permission dialog and this will require some yes on the one hand the permission we want to show the dialog for um so that will just be responsible for um deciding which text we actually show in that dialog we then want to Boolean is permanently the client which is a Boolean um we want to have an a listener for dismissing this so on dismiss when the user clicks OK actually not okay if the user clicks outside of the dialog to Simply hide it we want a listener to when the user clicks on OK to request the permission again on OK click I want to listen now that is for on app or on go to app settings click so when the user declined the permission twice and we now want to send them to the app settings to yeah to Grant it again and then we have a final modifier that we can pass to the root of our dialog the way this will work is we will use an alert dialog composable on this Miss request is just our dismiss function let's put this on separate lines remove the trailing and Lambda here and first of all we will have a buttons Block in which we Define how our buttons look like we then want to also have a title in which we Define what our what our title is so the title text composable and we will have a description or content how it's called text which will be the description of our dialog and last but not least our modifier that we can directly pass from our parameters up here cool so what will we start with first of all for the buttons we want to have a column since we want to show yeah um let me quickly launch my other app again to show you what I mean we want to show okay I granted the permissions now of course Let's uh go to our app settings very quickly turn this back off down to low for the camera permission go back launch this again and then if we click request one permission we'll actually see the dialog again if we click don't allow then we'll actually see this dialog so um the button section will be this section down here and I want to have this divider followed by this grant permission button or the button that says whatever we want and for that we want to have a column which simply Stacks this divider on top of our text so this column will take a modifier of modifier fill Max width and here we will have our divider and then we have our text composable the text will be dependent on if the permission is permanently declined if it is the button should say Grant permission and else it should simply say okay so clicking on OK will then request the permission again we then want to specify the font weight set it to bold we want to specify the alignment of the text which will be Center we want to specify a modifier which is modifier film x width I want to make sure that it's clickable here what we trigger in this clickable block again depends on our Boolean so if it is permanently declined we want to trigger our on go to app settings click Lambda and else we trigger our on OK click Lambda and finally let's add some padding of 16 DP to our button and for DP pressing Alt Enter let's get to our title next which is simply a text composable and the text will be permission required so that can just be a generic title for all kind of permission dialogues or you can configure that based on the permission as you like just a description here differs so that would be a text and here we will have a huge Wind Block based on our permission if that permission is a manifest permission camera we can now say what the text should be for the camera permission and if we would do it like this then this wouldn't be a really scalable approach and also via the solid principle since we then always need to change this permission dialog when we add a new permission to our app a better approach would be to Simply have a little interface called for example permission text provider which will then simply have a vowel for oh it could be for the title but we don't have different titles here we could simply have a description which is a string and then instead of our actual permission that we pass here we will pass the permission permission text provider and then we can take this and replace this stuff with permission text provider that description and the advantage of this approach is that we are able to extend this permission text provider with more with as many permissions as we like and also if we wouldn't own this code here this permission dialer code so if it would come from a library for example we could still Implement our own permission text providers to change the description of our text composable here which would not be possible if we hard code this in one expression here so we could then have something like a class camera permission text provider which is a permission text provider at the description in here and then let's actually make this dependent on whether we permanently decline it or not so instead of this we could say we have function get description is permanently declined and returned to string actually we don't want to implement that here and then instead of this simple variable we can say get description and in here we return if is permanently declined we say something like it seems you permanently declined camera permission you can go to the app settings to Grant it something like this and if it's not permanently declined we say this app needs access to your camera in order or actually so that your friends can see you in a call like this we can then copy this and do this for our other permissions as well in here so we have a record audio permission text provider so it seems you probably declined um microphone permission this app needs access to your microphone so that your friends can hear you uh can yeah can hear you in a call and we have the same stuff for um call so um phone call permission text provider this will be it seems to be only the client um phone calling permission this app needs access to your this app needs phone calling permission for example so that you can talk to your friends something like this and then dependent on what we want to which permission we want to display in the dialog we can then pass the corresponding provider here we want to replace this with get description which is done which takes out Boolean is prominently declined and that is not a much cleaner approach that does not violate our solid principles so we can now go back to our main activity below our column right here and here we can now refer to our dialogue queue and we can Loop over it so for each permission we get here from our Cube we now want to show our permission dialog and it's actually download q a DOT reversed I'm just noticing since it's a queue and the first dialog we want to see is the dialog that is last in our list so we could simply fix this with reversed or we need to adjust our viewmodel that we um directly reverse the list there let's just leave it here um format that a bit and now yeah Implement these parameters the permission text provider is now dependent on our permission and here's totally fine now to make this dependent on our permission string since at some place we have to do it but this is more scalable since here this is really code that we own so we could definitely extend this code here um let's specify manifest permission camera here in that case I want to camera permission text provider duplicate that two more times say that is for record audio like this record audio permission text provider let's keep it consistent to also have blocks for these and this will be called phone so phone calling phone call permission text provider something like this and in the else case we simply want to return out of for each so then we have a permission that we haven't implemented this yet cool um that's fun with that is permanently declined what will that be so the thing is Android actually does not give us a way to find out if a permission is really permanently declined so the the framework simply does not support that there are no libraries that support that because it's just a framework limitation a way that works however is if we say um if we don't sh if we shouldn't show a request permission rationale for a given permission then in our case at least we can be sure that it has been permanently the client however this is not reliable in all scenarios because this function will also return false if we haven't ever requested the permission at all so sometimes you might want to actually uh first of all tell the user what a given permission is for and when they then click OK on the dialog then you want to request that permission in this case this kind of function would return false for the very first call as well when you have never requested the permission before and there is no way to actually distinguish between the very first call or if it was already permanently declined so I really recommend this approach of first requesting the permission that you know that when these dialogues show the permission has at least been requested once in the past and in that case this is a reliable way to detect if the permission has been permanently declined usually you would also want to check if the permission is actually declined but in this case this would be redundant since Yeah in our review model we already make sure that in our dialogue queue there are only permissions that have been declined in on this Miss we want to call our viewmodel on dismiss dismiss dialog on OK click here we now want to first of all also dismiss the dialog and then if the user clicks OK we simply want to request the permission again because now that we told them what it's used for the user might want to yeah user might think okay now I actually know why I should grant that permission and they will do it so then we can now trigger our multiple um or we haven't created that yet we want to create a multiple permission launcher of course let's copy this yeah let's copy this paste it down below call it multiple permission result launcher replace the contract with request multiple permissions here the result will actually not be a Boolean since we get multiple permissions instead it will be a map as you can see a map of strings so the permission that maps to the corresponding Boolean so whether that specific permission was granted or not then here the permission uh you know we actually first of all want to Loop through our permissions so permissions.keys for each so for every single permission we get a result for we now want to call this function in review model first of all actually not first of all we just pass our permission and is granted is perms at the um with the key permission and if that's equal to true then we know that permission was granted and now we'll take this multiple permissions result launcher and we will do something similar as here just that we launched this down here now on OK click we say launch and this now takes an array of strings so an array of permissions we just want to request a single one since we hard-coded our first single launcher for camera permissions here and we just want to make sure that it's a flexible approach that it works for any type of permission so we say array of permission and then finally if the user clicks on go to app settings then we now of course want to send them to the app settings how do we do that I like to create an extension function for that known here so we can say function activity a DOT open app settings which will just be a normal function and we do that simply by sending out an intent we need to specify an action the action is settings from android.provider dot action application details settings and we want to specify a giri the Yuri is our package name so the package name of the application we want to show The Details page 4 in the settings and that will simply be our own package name of course so we say Yuri from Parts the scheme is packaged since we refer to our package name the actual package name is what we need to pass here so we pass package name and the fragment is nothing since we are not in a fragment and then after we created that intent we can say that also double colon start activity so we just start an activity with the antenna we just created if we then scroll up we can replace this with a call to open app settings like this and now only what's missing is if we scroll up to our second button that we also call our permission result launcher here so here we want to say we have a multiple permission result launcher call that launch and in this case we want to request the call phone permission and we want to oops want to request the record audio permission and I would say I will uninstall my app and reinstall it so that we just have a fresh install with no permissions granted and then we can try this out so there we go this is our app if we now click request one permission and then the dialog will show we already tried this but we haven't tried um what happens if we decline here so let's click download then our dialog will show and this this app needs access to your camera so that your friends can see you in a call um so let's click OK we should now see another permission request yes we do um let's yeah let's click down below and see if we actually see the the other dialog yep now we promote to decline that camera permission we can go to the app settings let's click Grant permission then we get to our app settings and here we see no permissions granted and we could approve or Grant the camera permission here let's do that go back and we could also dismiss this dialog after we click this by the way let's do this by clicking outside if we now click this again we shouldn't see any download which does not happen very good and finally requesting multiple permissions let's don't allow recording audio and allow phone calling then let's actually down below both so we see that the queue is working hopefully at least download these and then we will see okay it actually needs access to your microphone so let's click ok then we get our microphone permission again let's click we now approve this and this should actually not happen um it seems like it's not dismissing the dialog after we prove this I will take a look into that after this um but let's first of all check if it's also showing the dialog for phone calling next so let's click outside and no it does not show that maybe I didn't adjust the text could that be if we scroll down if I did okay that is something I want you to check and then I will get back to you once I fixed it all right welcome back there are actually two little fixes we need to make here on the one hand well that's not really a fix um but I want to do that to have the proper logic here in our view model for our queue and that we swap these remove last with remove first and instead of adding at the first index we add at the last index and the reason why we do this is if we consider our Q then let's say we first of all request record audio permission and then called form permission and then after we request record all your permission this is how our queue will look like and that is also the dialogue that should show up at first and if we then request called phone permission next it will look like this but if we then see the dialog record audio first and we then dismiss it we want to remove the first entry of our queue which is why we have removed first here and we add it at the last index because yeah this new call phone permission should of course then come after our record audio permission when it is about showing dialogues however we still want to keep the Reversed here since if we consider how dialogues are shown in compose they are all shown at once so if in our dialogue the first entry is record audio permission and we want to show the record audio dialog first then we will show it then it will go through the next entry which will be a call phone then the call phone permission dialog will be shown on top of our of our first record audio permission dialog which is of course not what we want and that is why we reverse it so that the dialogue that should show up at first is actually the last one in our list now of course we could already Implement like that in our V model but I like this approach more since this is a very individual thing for how dialogues work The Logical order is still what we have in our review model and that is why I kind of like to have the Reversed function here then the next thing we need to do is that if we consider maps in kotlin like here the sperms is a map that Maps permissions to a Boolean these maps are the keys of these actually don't have a real order since it's not a list so in this case since these are strings they will be ordered alphabetically so we will go through these alphabetically but not in the order we really request these permissions to fix that we need to Define these permissions we want to request at least for the multiple permission launcher permissions to request and that is an array of yeah let's just paste this from below here um these two and Swap this out with our permissions to request scroll up paste this in here and this way we make sure that the record audio dialog will actually also show before our call phone dialog we can then simply Swap this out with permissions to request and one last little fix range to make is in our review model in on permissions result we actually also want to check if that permission is not in our queue yet um so if visual permissions dialogue does not contain our permission then we want to add it because there are certain scenarios where it could happen that the permission gets added multiple times to our queue we don't want that the reason for that is that these launches are actually always cold so even if we don't see a permission request so we still get results here and under certain scenarios we could then see the permanently declined dialogue even though that wasn't the case so I would say we try this one more time and hope for the best let's launch this here take a look on the emulator let's first of all try request one permission um let's say down below and then we do see our permission required dialog clicking OK that will request the permission again let's click down below again then we should see the opponently decline download which we do see and yeah let's um dismiss this for now and you will probably also notice something interesting yes we do get the phone calling permission dialog here why is that that's actually quite an edge case here that is only happening because of how we did this in this video usually in a real life scenario this wouldn't happen so let me explain the reason this happens is that this on result callback of our multiple permission launcher is also called when we get the result from our single permission result launcher so this is really a rare scenario here and in the real world that would not happen since yeah on one screen you would either have a single launcher or a launcher to request multiple permissions this is only happening because I want to show you both ways so you're flexible in your app but let's also try the multiple permissions which is interesting more interesting thing let's not allow both of these don't allow and don't allow now we should see the microphone permission dialog first yes this app needs access to your microphone let's click OK let's click down below for example and then we get the phone calling permission let's click ok now we could for example click allow to allow that and now we get the permanently declined permission for our microphone again and we could grant that if we dismiss the dialog then nothing will show again if we click request module permissions again it will show the permanently decline dialog for those we actually permanently declined so for our microphone and we could then Grant this here in our app settings so I think this is really complex actually if you want to implement such a permission handling system with all these different rationals and stuff like that this is one of the things that I think is way too hard or way harder than it needs to be I think Google could actually hide a lot of this logic that we defined here in the Android framework or rather at least provide an option that lets us Define what should happen in these cases so if the permission was the client wants and what happens if it was declined multiple times and if you really want that fine grain control you can still do this but I think in most cases you really just want to show these dialogues and tell the user what went wrong nevertheless I still hope you now know how to actually use permissions in your app properly and also how to handle multiple permissions and multiple permission requests if you like this video then it will definitely also love my Advanced Android premium courses which you can all find using the first link in this video's description so these really teach you Advanced Android Concepts architecture multiple modules and basically just what you need to become an industry ready and developer so do check these out and apart from that I wish you an amazing rest of your week and I'll see you back in the next video bye bye foreign
Info
Channel: Philipp Lackner
Views: 36,889
Rating: undefined out of 5
Keywords:
Id: D3JCtaK8LSU
Channel Id: undefined
Length: 34min 14sec (2054 seconds)
Published: Sun Feb 12 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.