Alright,
so we're looking at permissions on Marshmallow and above. The app won't have been granted permission when
it was installed and we have to request permission when the app runs.
So we added a call to check selfPermission in our code,
in the last video, to find out if we have the required permission or not. If the permission hasn't been granted,
and it won't have been the first time the app runs, then we'll get PERMISSION_DENIED
when we call checkSelfPermission.
So that means we have to request the permission in our code. Now this should be fairly straightforward,
except that the users can deny the permission, and they can also tick a box so that they won't be asked
again.
So the flow chart that I'm putting on the screen now shows the options and the various states that our
code has to handle.
So when we check the permission by calling the checkSelfPermission method, we'll automatically be granted the
permission
on devices running API 22 or earlier. So that bit's easy and there's nothing more we need to do. On
API 23 and above
though, we have to request permission from the user.
Now that's still quite straightforward.
If the user grants the permission, then we can proceed and everything's fine, but the user may decide
to deny the permission.
So let's have a look at what they'll see the first time they're asked to grant the permission. Now if the user allows
the permission, then that's fine,
but if they choose to deny it, we have to decide what we're going to do and how we're going to deal with
the situation.
In some apps you could just disable the particular functionality that requires their permission.
In our app
though, that's not really an option.
All the app does is access the contacts records,
and without the necessary permission to do that it's a bit of a pointless app.
It gets slightly more complicated because the next time the app is launched, after the permission has
been denied, the dialogue offers another choice.
So this time there's a box to tick not to be asked again.
So when the user ticks that box, the only option provided is to then deny the permission.
Now that can be a bit confusing to us as developers, and the documentation doesn't make it very clear
unfortunately.
But when they choose to allow the permission then they won't be asked again.
That's automatic once you've allowed the permission, then it remains allowed unless you go into settings
to change it, and we'll have a look at that later. If the user ticks that box and denies the permission,
they won't be asked to allow it again.
So our app just won't get the permission and we have to cater for that in our code.
And once again, some apps could just disable the particular functionality that needs the permission,
but our contacts app becomes useless without it.
So going back to the flowchart, we can see that the situations where permission is granted are easy to
deal with. We'll leave the situations where permission is denied for now, and we'll come back to that once
we've got the app working.
Alright so let's go back to our code now, and the Android framework provides methods to handle requesting
the permission from the user at runtime.
So the first thing we have to do is check the value we got back from calling checkSelfPermission.
So I'm going to add the code and then we'll go through it. So down here after the log, after the hasReadContact
Permission, our
variables, our variable line.
We could add the code if parentheses hasReadContactPermission,
if that's equal to PackageManager.PERMISSION_GRANTED, then we'll open a code block. And there we're going to put log
dot d parentheses TAG comma and we'll just indicate in the message onCreate colon permission granted. And we're going to put
readGranted is equal to true.
However I'm also going to put a comment here TODO
don't do this!! else,
open up a code block Log.d parentheses
TAG comma in double quotes onCreate colon,
and that's requesting permission, because obviously we got denied.
And that's going to be Activity
Compat.requestPermissions
parentheses
this comma space arrayOf parentheses.
Then it's going to be READ_CONTACTS, then a closing right parentheses comma,
then REQUEST underscore
CODE underscore READ
underscore CONTACTS
then closing right parentheses.
And also take this opportunity to log when the onCreate method finishes. So let's add a log call
down there; Log.d parentheses TAG comma onCreate colon ends.
OK, so looking at the code starting on line 32.
So if the result we got back from calling check
SelfPermission was PERMISSION_GRANTED, then we have the permission we need and we can carry on.
We'll need to know that we have the READ
underscore CONTACTS permission later, before we try to access the contacts when the floating action
button's clicked.
So we're storing true in the readGranted field on line 34.
Now we could just call the checkSelfPermission method every time we need to know if the permission's
granted.
But for now I'm going to store the result.
That's a common thing to do but it's often a bad idea.
So I'm going to demonstrate the problems it can cause us later.
So following on from the code though, if the permission wasn't granted, the else codebook is executed starting
on line 36.
So at that point we need to request the permission, and we can do that by using the compat form of
request permissions and you can say that code on line 37.
Once again there's also a method built into the framework, but that'll only work on API 23 and above.
Earlier devices don't know about this new security model, so they have to use the compatibility versions
of those methods from the android.support.v4.app package.
If you're targeting API 23 and above, the code will be almost the same, but you can leave off the
ContextCompat and ActivityCompat class names when calling the methods.
Now I say almost the same, because you don't have to pass this to the framework methods.
The framework checkSelfPermission is called on the context, and request permissions is called on the
activity,
so there's no need to pass this to provide either the context or the activity. The compatible request
permissions method does need to know the activity though,
and that's why the first parameter is this, again on line 37.
So next we provide an array containing the names of the permissions we were requesting.
Now we only want to request a single permission, but it still has to be provided in a string array because
that's the type of the parameter.
You can check that by clicking on the method name and calling up the documentation,
and you can see we've got permissions here;
it's a string array. Now there's quite a lot of description here in
this documentation, so it's well worth a read to understand how it all works.
You may want to read it after we've got this app working though, because we'll be writing the onRequest
PermissionsResult callback method next, and it may make more sense once you've seen that working code.
Also I suggest you read through the guide that I called up in my browser in the last video.
Now the documentation itself can be a bit confusing, and has resulted in some overly complex code on
the Internet.
Earlier versions of the documentation gave the impression that the app had to call requestPermissions
every time it runs,
which isn't the case as we'll see.
Once you've seen how we're going to write the code for this app,
read through the documentation and it should all make more sense than trying to work from the documents
alone.
OK so the final parameter to requestPermissions
is a result code and I've used a constant that I created earlier. Go back to our code,
this one here.
So the reason for that constant is because when you call the requestPermissions method you don't get
the result
back straight away. The system pops up a dialog, or the dialog, that we saw in the slides earlier, to ask
the user to grant the permissions.
Now if our onCreate method waited for the user to either grant or deny the request,
then our app would be blocking and that's almost always never a good idea with an Android application.
Now we discussed that when we were looking at async tasks in the top 10 Downloader and Flickrbrowser
apps. It's not a good idea for our apps to block the UI thread and prevent the users from answering their
phones.
So instead, the Android framework displays the dialog and the requestPermissions method returns immediately.
To find out whether the permission was granted or denied,
what we have to do is implement a callback method that will be called once the user has made the decision.
So let's actually write that,
and we'll put this straight after the onCreate method.
So I'm going to come down here,
and start it immediately below the onCreate method as I said.
Now we can get Android Studio to generate the stub for us, so
I'm going to do a control o here, then we'll do a search for onRequest - I'm just typing onRequestPermissionsResults.
As soon as we find the one we want, press enter there, and add the stub. I'm going to type this code in and
then talk about it.
For now I'm going to delete that first line, that super call, and I'm going to add a log there,
log entry, Log.d parentheses TAG comma double quotes onRequestPermissionsResult
colon starts. Then we're going to do when parentheses requestCode code block. We're going to do REQUEST_CODE_READ
underscore CONTACTS dash greater than sign,
then a left and right curly brace.
Then within there we're going to put readGranted equals if parentheses grantResults
dot isNotEmpty, and grantResults, left and right square braces and then zero, so index 0 is equal to
PackagerManager dot.
PERMISSION_GRANTED, then right parentheses.
Then open a code block there.
So at this point, in terms of our code, we can note here that permission was granted but the error will go away
shortly, so "permission was granted,
Yay" and then "Do the contacts-related task we need to do."
So we're just going to return true there.
Otherwise we'll run else
then the code block again;
"permission denied",
at this point we get to this part of the code, denied, "boo!!"
So we want to "Disable the functionality that depends on this permission".
and we'll just add a log entry there;
Log.d parentheses onRequest, or sorry TAG comma double quotes onRequest
Permission, PermissionsResult colon permission refused.
OK, and what I should probably do there, I put a typo there,
let's also put a log entry there above the true, so we can see whether the permissions were successful
or not.
So we'll put permission granted for that one, and here we're going to return false, so false.
Then right down the bottom here, just before the end of the function,
we're going to put Log.d parentheses TAG comma double quotes onRequestPermissionsResult. PermissionsResult colon
ends. Now I've lifted this code almost verbatim from a Google training document.
I just want to quickly bring that up on the screen,
and if we scroll down, the section that we're looking for is Handle the permission, the permissions request
response, this part of the code, this is the code here. Now you can see the code is very similar to what's showing in there; it's
almost verbatim.
Now we're going to be adding a bit more to it later, to deal with the permission being denied,
but this will do for now just to get an idea of what's happening.
Basically when Android calls this method for us, it'll pass in the response code that we provided when
we called the requestPermissions method. And going back to our code,
that's what the constant REQUEST_CODE_READ_CONTACTS is for. Now in a larger
app, we may well be requesting several permissions; an app that records video, for example, would need to
get access to the camera,
the microphone and also the storage, so that it can save the video. By providing a unique response
code when calling requestPermissions,
we can tell which particular request we're getting the results back for. Now here
there's only one request,
and we don't need to switch statement to check which one the results apply to,
but it is useful to see how to handle multiple requests from a single activity.
Alright, so that's the request code,
the first parameter. The next one is permissions -
well, actually the next two arguments are both arrays. We've got permissions
and we've also got grantResults as you can see. The first contains the permissions we have requested and the second
contains the result for each permission.
It's possible that the user could grant some permissions and deny others, if we'd requested several at once,
so this allows us to check which ones were accepted and which were denied.
Now again we've only requested a single permission - READ_CONTACTS - so we're only interested in
the first item in the grantResults array,
and you can see the code now for that on line 69. If that permission's granted, then we can set the Boolean
field readGranted to true,
so we know we do have the permission. We'll look at things we can do if
a permission is denied, a bit later.
For now though if the permission is denied, then readGranted will be false, and once again, we really shouldn't
be doing that.
As I mentioned, it may be an obvious thing to try to do. When you do it and get in a mess it's not immediately
obvious what's causing the mess.
And if you've done any web development work then you'll know exactly where I'm going with this.
Don't try and save state.
If that doesn't mean much to you don't worry - it'll soon become clear.
So that should work and we've got some logging code now that'll let us see what's going on.
I'll stop the video here, and in the next one we'll run the app and see what happens when the user accepts
and denies the permission.
So you in the next video.