(light electronic jingle)
Alright, so in the previous video,
we saw how our app will handle the situation
where the user denies permissions.
If they just deny the permission,
then we can request access again
when they next try to perform the function
that needs the permission, viewing the contact
details in this case.
If the user denies the permission and ticks the box
saying they don't want to be asked again,
our app then takes them for the settings for the app
so that they can tap the permissions
and grant the access that our app needs.
So we've got a good solution now.
And remember we used a snackbar to display
the message to the user and also to provide
them with a link they could tap
to go ahead and grant the permission.
A snackbar is limited to two lines of text,
so there's not very much information you can provide
to the user.
If you do want to give them a more detailed
explanation of why the app needs a particular permission,
then you may be better off using a dialogue box
rather than a snackbar.
The principle is exactly the same though,
so let's continue with our example app
so you see now how to proceed
when that permission is denied.
So that needs to do one of two things
when it detects that the permission hasn't been granted.
It either needs to request the permission or it needs
to take the user to the apps settings.
Now both of these tasks are really simple.
We've already seen how to request the permission
using the request permissions method,
and launching the app setting screen is done
using an intent and the start activity method
that we've seen before.
The tricky bit though is working out
which task to perform.
So how can we tell if the user had permanently
denied the permission by ticking the box
or just tapped deny in the dialogue.
But it turns out that it isn't very difficult at all.
Google have thought about this
and they provide a method with the catchy name,
wait for it, Should Show Request Permissions Rationale.
So we're going to use that in our action code
instead of just showing the toast message
as it does at the moment.
So I'll type this code in and then we'll go through
it and explain what it does.
First we're gonna start off by changing the text
that the user will click,
in other words, the action text itself.
Alright, so what we want to do first is I'm gonna
change the code, the text rather,
that's shown on the screen that the user will click on,
the action text, gonna change that to say grant access.
Then within the code that's gonna be executed
when that's tapped, I'm gonna start with a log entry,
log.d TAG comma
snackbar onClick starts.
Then we're going to add an if here.
If parenthesis, then it's ActivityCompat dot
Should Show Request Permission Rationale.
And in parenthesis, this comma READ_CONTACTS.
So I'm going to close off those bracers
then open a code block.
And in the else, for now I'm just going
to make that an empty else, and I'm going to delete
the toast message.
And within the first if there what I'm going to do
is put log.d parenthesis TAG comma,
and it's going to be snackbar onClick
calling request permissions.
And just to be consistent I'll change that.
What I'm gonna do is copy the line of code here
from line 38, because we're gonna be executing
that exact code and that's gonna be pasted
immediately below that log in there.
And just so we can read that I'm gonna put that
onto the next line.
And that's also below the else, put a log in there,
log.d parenthesis TAG comma snackbar onClick ends.
Just so we're clear what's happened there.
Alright, so what have we done here?
Well you saw me add some logging firstly on line 64
so that we can see the snackbar's
onClick method being called.
Then we're calling, Should Show Request Permission Rationale
on line 65, then we're testing to see if it returns
true or false.
Now I'll explain that method next
but if it just returns true,
then we just, in this case we're going to log
that entry and then we're going to call this method
to request the permissions again as we've done previously.
So that's pretty simple.
If it returns false though, we're gonna take the user
into the settings page for the app,
so let's actually have a look and see what this method's
all about, so I'm going to just click on that
and bring up the documentation.
Now this method is in the compatibility support library
in the package Android.support.v4.app.activityCompat.
So we can use it in the code that's intended to run
on Android versions before Marshmallow.
Now you might be wondering why we don't
just use the framework version
because this method will never be called if the app
is running on APR22 or earlier.
But the code might compile if I try to use
the framework version when my minimum SDK is less than 23,
hence the reason for using the compat version.
And just to check that, what we can do is we can
just take a copy of that line,
temporarily comment out that line and change this
removing the activity compact,
and you can see we've got an error there.
Core requires APR level 23, current minimum is 17.
By the way that's something I do recommend you do
if you want to test out something like this.
Duplicate the line of code and then go ahead
and actually check it and you've still got
your original code to go back to.
Alright, so I've done that as you can see there.
So Android Studio basically warned us that the code
wouldn't compile unless we set the minimum SDK
to APR23 or higher, hence the reason that I'm using
the activity compat or the compat version
that supports versions earlier than Marshmallow.
So this method should show request permission rationale.
It's used to decide if we should show some sort of user
interface to the user explaining why we need the permission.
So the methods intended to be used as the documentation
says only if you do not have the permission
and the context in which the permission is requested
does not clearly communicate to the user what would
be the benefit from granting this permission.
So I guess that sort of implies that we shouldn't
be using it, because that is pretty obvious already
why our contacts app needs to access
the phones contacts database.
It may be obvious but equally obviously
if the user has denied access then we do need
to prompt them in some way, so don't take that comment
as a rule that it must be obeyed no matter what.
Treat it as a guide.
So here in our case, we need to do something
if they can't access the database,
and if that involves misusing this very useful method
then so be it.
One thing lacking from this documentation
is what the returned value signifies.
It just really says whether you can use the
Show Permission Rationale UI so you get a true or false,
but there's no real indication there of why.
Now unfortunately you can't check the source code
to find out what's going on because it eventually
ends up calling a method on the abstract package
manager class and abstract methods don't have source code.
So the documentation we've been
using though is quite informative.
Now I'm just gonna bring that link up on the screen again.
That's quite informative as I said, but unfortunately
it's changed now and it's actually
a little bit less informative.
Now come over here we've got this, in our contents,
explain why the app needs permissions.
If I click on that, that actually describes
the behaviour, you can see on the screen there now.
Basically describes the behaviour of the show,
Should Show Request Permission Rationale.
Now it used to have this notice
which I'll bring on the screen.
But that note's now been removed.
The behaviour hasn't changed, and we've got to rely
on that false return value in our code.
Now we've locked a documentation issue with Google
so the note may well be back by the time
you visit this site again.
So we've established at this point
that the method will return true
if the user has previously denied the request
which is what we want here because we want to ask
them again to grant access.
Rather cleverly, it will allow false if the user
has chosen the don't ask again option
by ticking the box.
So that means it provides a way to tell
if the user has just denied the request,
will get true returned or if they tick the box,
in that case we'll get false.
Now we have to be a bit careful and don't just go
calling this method all over the place,
because it will also return false
if we already have the permission.
And that makes sense if you think about it,
if the user has granted permission,
then there's no point explaining to them why the need it.
So once you've checked and discovered that your app
doesn't have the permission though,
this method lets you decide whether you can just
ask again or you need to do more,
such as taking them in our case to the settings
or perhaps displaying a dialogue with more information
or whatever you need to do.
So if you're writing apps for the corporate world
where the app will be running on devices
that are centrally controlled by a company's IT department,
then you would have to be a bit more careful.
That's because the Should Show Request Permission
Rationale method will return false if the IT
department had deployed a policy permitting users
from granting the permission you need.
So in that scenario, taking them into the settings
is futile, but we're not covering device policies
in this course, but it is useful just to be aware
of that comment if you do end up producing corporate apps.
Alright, so let's go back to Android Studio.
So we shouldn't have got back to this bit of code
in the first place unless permission was denied.
And we've now checked and discovered that the permission
has only been denied, not permanently denied.
And that's the way that the activity compat
Should Show Request Permission Rationale
method returning true.
So therefore, you saw that I just pasted in
the same code we used earlier to request
permission from the user again.
So hopefully at this point now that they've seen
our snackbar message when trying to display
the phone's contacts, they'll grant access.
So that's if it returned true, but if the method
returns false, this is the Should Show Request
Permission Rationale, then we assume they've ticked the box.
So in that case, we just use an intent to stop
the app settings activity, so let's add the code
to the else part to do that.
That's gonna be here.
So the code we want for that, we put a comment firstly
that the user has permanently
denied the permission to take them direct to the settings.
So the actual code, we're gonna start with the log,
so log.d TAG comma, and the message is gonna be snackbar
onClick colon launching settings.
And we're going to do val intent equals Intent,
capital I and then parenthesis.
Then we're gonna do intent.action is equal to settings dot
and it's gonna be action and it's called application
details underscore settings.
I'm going to do val uri is equal to Uri
with a capital U.fromParts.
Then in parenthesis, in double quotes,
lower case word package comma
this.packageName, then no as the third argument.
Then we're gonna do intent.data equals URI.
Then we're gonna do this.startActivity,
but in parenthesis intent.
So we've seen code like this before in the YouTube
app for example, we've created the new intent
then called startActivity to start the activity described
by the intent.
This intent is slightly different because we're specifying
the action we needed to take.
The set action method needs a string specifying
the action to be performed, and here we want
to launch the settings app and have it open
at our application's settings.
Now being Kotlin we access the action property course
rather than going by the setter.
If you wanted to you could bring up the documentation
for the set action method but it really
doesn't say any more than what I've just mentioned.
Now the documentation for this constant
here is Action_Application_Details_Settings
is a lot more informative, so I'll bring up
the documentation for that.
So it's telling us here that it shows details
about a particular application,
and also we have to use the package schema
and specify the application package name
in intent's URI.
Now getting the application's package name is easy,
we just call the getPackageName method on a context.
Now the easiest way to get a context from inside
the listener is to use this to refer to the current
instance of main activity.
Hence the use of his over here.
Now the URI consists of a scheme such as HTTPS
or file or in this case package.
The schema is followed by an SSP or a scheme specific part.
In our case that's just the package name.
See here, and obviously in here with this first part
was the schema that I talked about.
Now I use the URI fromParts method to build up
the URI that we need to pass this data to the intent.
Now we're gonna be using URI's quite a bit soon.
So I'm gonna discuss them in more detail
later in the course.
For now though, what I'm going to do is also
take the opportunity to log the URI that gets created
just so we can see what it looks like.
So I'm gonna put a log message in there.
Log.d parenthesis TAG comma
double quotes gonna be a snackbar colon
space onCLick colon,
URI, or $URI.
So finally then we call online that's 80,
we call the start activity method
to launch the settings for our app.
Now that should be all we need,
but unfortunately this code does
something that we really should avoid doing.
And in the next video we'll run the app
and see what that is and how it behaves.
So I'll see you in the next video.