At the end of the previous video,
we ran the app and we got a security exception
because the app didn't have permissions
to access the Internet.
Now, we had a look at the stack trace
and how it's a useful way to work out
what went wrong, and we're just gonna
run the app again, just to get the same error
and we can see the links in our own code
appear in blue when I do that,
so let's just start that up.
Okay, so you see we've got a crash there
and if we have a look at Logcat,
we can come over here and scroll over
and we can see our links in blue.
I can click on that first link
and that takes us to line 51.
You can see that the error happened
when we tried to do anything with the connection,
in this case, even just reading the response code.
The problem was with the Internet connection
and the fact that it's a security exception
really provides all the remaining information we need.
Just to confirm that it is a security exception again,
let's scroll up and have a look.
Java.lang.SecurityException permission denied
and I got a suggestion there for
missing Internet permissions, perhaps.
Before we look at fixing the problem
which is obviously something we need to do,
let's catch that exception and make
all those error messages a bit tidier
instead of showing the full stack trace.
I'm going to close down the Logcat for now.
We're going to add another catch here
and we're going to put the original exception
back again now, but above that,
what I'm going to do is add another catch
for our security exception.
The catch, e: SecurityException.
Then a code block, and we're gonna do
a Log.e, parentheses tag, comma.
That's gonna be downloadXML: security exception,
needs permission, then we'll put
a dollar sign, left and right curly braces, e.message.
Let's try running that again now.
It should prevent us from getting that
big exception, big stack trace, rather
but we should still see our error logged.
Now it's no longer crashing, but if
we have a look at our Logcat,
you can see I've got a security exception,
needs permission, and then we've got
the message that's coming back from
the actual exception itself,
permission denied, missing Internet permission.
Then of course we've got error downloading as a result.
While we've got some good errors in the Logcat,
I'm gonna digress briefly and show you
a bit more about the Logcat and how you can
make it easier to find information in it.
I'm gonna start by clearing the filter
by clicking on X over here,
and now you can just see how busy the Logcat gets.
There's lots of other information
that we've been filtering out
but we can filter that using the box
near the top right of the AndroidMonitor pane.
Again, you've seen this before,
but if I type in downloaddata,
then we'll only see messages logged
by our downloaddata class, but curiously,
this is sort of case sensitive.
If I delete this and I start the word download
with a capital D, download,
to match the tag that appears in the log,
and you have to continue matching
the case exactly, so downloaddata
will display the entries logged by the class,
but Downloadd, so you can see here.
I don't need to type anymore because
you can see straight away it's not matching anything.
The best thing to do here is to
type all your filters in lowercase,
so downloaddata, which then works again.
Now that I've done that and typed in
downloaddata into the filter box,
we can see the doInBackground starting.
Starts with, and then the link
and the URL that received, obviously,
and it also logs an error after the error
from downloadXML catching the security exception.
Finally, onPostExecute is logging
its parameter as an empty string
which is what we'd expect if doInBackground gave an error.
Again, when I delete the contents or clear it
by clicking on the X, we get heaps of messages again.
Now, this would contain far more
on a physical device, but even an emulator
is continuously logging all sorts of stuff.
In fact, if I change this dropdown here
to the right to no filters and scroll up,
you can see now that there's an absolute load
of things that have been logged here.
Heaps of things, and you can see
there's just pages and pages and pages of stuff there.
I'm gonna change that back to show only
selected applications again, application I should say.
We're just getting stuff that's relevant for us.
The selected application it's referring to
by the way is the one in the second dropdown
from the left, this one here,
which is obviously our app,
our academy.learnprogramming.top10downloader.
Now Android devices have quite a few things
going on at once, so you can actually
look at logs for other applications if you want
but unless you're getting involved
in Android system programming,
you generally just want to leave it to display
just the entries for your app.
If you do want a full stack trace to be printed
when you've caught an exception, you can do that.
I'll just go back here, back to our code,
close down the Logcat.
You can do that by calling
the printStackTrace method of the exception,
so I'm gonna add that to the catch block
for the security exception just to show you.
I'm gonna actually do that before the log.
It doesn't really matter where we do it
but we'll do it before.
We'll do it e.printStackTrace.
Let's go back to the Logcat and make sure that
I have cleared the filter so it doesn't
filter out most of the stack trace.
Basically, I'm gonna get more data by doing that.
Now that I've made that change
let's try running it again.
What I'll do first is I'll actually
clear the filter as well there, clear the Logcat.
We'll run that again.
You can see now that we've done that,
we've printed out quite a bit of stuff there.
Basically, that's the stack trace
that we had before I caught the exception.
You can see scrolling up that it's actually done that.
Android Studio doesn't colour the messages red
and that's because they're created
deliberately by our app now rather than
by the Java Runtime, but the links
in the the code, if we scroll down
and have a look over here to the right,
they're still a different colour.
We can still click on mainactivity.kt
and get to line 51 and see where the error actually is.
Basically, it's the same line of code
that our response equals, connection.responseCode.
Something else we can do, as I mentioned earlier,
is filter out more than one term
and it'd be useful to get some idea
of what's happening through MainActivity
and we can do that by filtering
from MainActivity or downloaddata.
We have seen this before,
but we'll just briefly show that again.
So that's mainactivity.
You've seen the use of this pipe
or bar character before, vertical bar,
which actually means or.
You can type downloaddata,
and now we're getting information
about MainActivity or downloaddata,
so the vertical bar means or, basically.
Alright, so that cleans everything up
and we can see onCreate being started
in MainActivity over here, as well as
the log entries from the DownloadData class.
Okay, so I'm gonna close Logcat for now.
Let's talk about permissions.
By default, an app has no permissions to anything at all.
An app has to get permission from the user
to access resources it needs and this was
usually done when the app's installed.
Now I say was, as you can see on the screen there,
because with Android 6.0, Marshmallow,
Google changed the way that an app
obtained permissions for the resources it needs.
Prior to Android Marshmallow, the user granted
the requested permissions when they installed the app.
If they didn't grant all the permissions,
then the app couldn't be installed.
Once granted, the app had all the requested permissions
for its entire lifetime on the device.
The only way to revoke any of the permissions
was effectively to uninstall the app.
With Marshmallow and above, an app requests
the permissions it needs when it runs
and it has to do so each time it is run.
Now actually Google documentation states
even if the app used the camera yesterday,
it can't assume it still has that permission today.
The documentation isn't as clear as it could be
and if you read it in a certain way,
it could be implying that your app
has to request the permission from the user
each time it runs, but that isn't the case fortunately.
Once the user has granted a permission,
then your app continues to have that permission
unless the user specifically revokes it.
The app still has to request the permission
each time but the Android system
doesn't ask the user every time.
Alright, so let's have a look at
the documentation because apart from
that one source of confusion, it's pretty good.
Google separates their documentation
into reference, guides, and tutorials
and the tutorial pages for permissions explains it all.
I'm gonna just open that in a browser.
Paste the link there.
Now this first page explains that
each app runs in its own sandbox.
Elsewhere in the documentation explains
how this is achieved, which is by
creating a new Linux user ID for each app,
but we don't need to understand that
level of data to work with permissions.
The getting started link down here at the bottom
goes into a bit more detail, so click that,
and it starts by saying the app
has to declare the permissions
it needs in the App Manifest.
We're gonna be doing that for
the Internet permission in a minute.
In this second paragraph is a description
of the different behaviour for Android versions up to 5.1
and for version 6.0 and higher.
Up to 5.1, the user grants permission
when they instal the app.
If they don't grant the permissions,
then the app doesn't instal,
but from Android 6.0 and above,
the user grants permission when they run the app.
It's worth going through this Google tutorial
when you come to create your own apps,
as there are recommendations on
when and how you should request permissions
when your app is running.
Google suggests that you don't request
all the permissions at once, but instead
you wait until the user does something
that needs a permission before asking them for it.
It makes more sense to the user
if your app asks them for permission
to access their contacts when they've just
tried to use your app to share a file
with someone, for example.
But if you request that permission
as soon as they first run your app,
then they may be left wondering why
the app needs to access their contacts at all.
There's a very useful link at the end of
the third paragraph, down here,
which will take you to the definition
of normal and dangerous permissions.
Permission to access the Internet
counts as a normal permission and the system will
automatically grant that permission
as long as we declare it in the manifest.
But scrolling down slightly,
you can see that we've got a list here
of dangerous permissions and permission groups
and we can talk here about permission groups
and I guess it goes into a bit more detail
about the various types of permissions
and this is dangerous permissions as well.
A bit of an overview of what it is.
Looking at the table, it's actually listing
what Android or Google suggests
are dangerous permissions.
We're gonna be looking at how to deal with
requesting permissions when our app is
running later in the course, when we need
access to the contacts and the microphone.
I'm gonna scroll up a bit, basically up to
the normal permissions group here,
normal and dangerous permissions.
There's a link there for normal permissions,
so I'm gonna click on that.
If we have a look you can see if we scroll down,
Internet is defined as a normal permission
so the good news for this app is
we only have to declare that we need
Internet permission in the manifest
and the app will work on all Android versions
that we're actually targeting.
If we go back now to Android Studio,
I'm going to open the Android manifest.
If you're working in Android view,
I suggest you really want to do that,
you'll find the manifest in the manifests folder under app.
If we open it up, you can see that
I'm in Android view here, we've got manifests.
I'm gonna click on that and there's our AndroidManifest.XML.
I can double click on that to open it.
Now, don't worry too much about all this stuff in here.
We're gonna be looking at the application tag
later on and explaining what it means.
For now though, we need to add an entry
to declare that the app uses the Internet.
I'm gonna make some space at the top here,
just after the package, before the start
of the application tag.
I'm gonna start typing the uses tag,
so less than sign and then uses.
I definitely recommend using
the code completion feature here
to make sure you don't make any errors when typing.
If you get the case wrong, you won't see
an error in here but the app won't work
because the permission won't be granted.
I want users-permission, I'll go to tab
to use my arrow keys to go down to that.
Then I'm gonna press enter or I could have
clicked on it to select it.
Notice that when I've done that,
we automatically got this pop-up
and I prefer to use the arrow keys
to select the one I want and that's because
I don't like switching from the keyboard or mouse
but it's your choice as to which one you want.
All the available permissions are shown.
As you can see here, I can select the one I want.
Again, these permissions are case sensitive
so once again, I recommend that you select them
from the list rather than typing them in.
You can filter the list as well,
so if I type in cam for example
you can see that android.permission.camera
is showing on the list, but I don't want that one
so I'm gonna delete cam, type int instead
and you can see we've got some of
the other ones that have got the letters int in them.
You can see that there's the Internet permission there,
the second one, so I'm gonna use
the down arrow key, I'm gonna press enter.
You can see that's added it for us
and I just need to close the tag
with the forward slash, so I'm gonna
click on the forward slash and that closes it
and adds the last bit of the tag
and that's the Internet permission
declared now in the manifest.
This time, the app should run without any errors,
so let's run it and then have a look at the Logcat.
Actually what we'll do is we'll open up
the Logcat first, and we'll run it.
Just clear the log, Logcat,
which I've mentioned before, is a good habit
to get into, even though Android Studio
tends to do that for us at the moment.
Alright, so you can see this time
we haven't got an error anymore.
If we scroll back up to the top,
you can see now right at the top here
we've got downloadXML, the response code was 200.
That's looking good, and that means,
the response code of 200 means OK.
If your Internet connection goes through
a proxy, then you might get a 203 response code
instead but that's also fine.
We can see the XML, you probably saw that
just by scrolling up, and that's the XML
printed by the onPostExecute method
so our download's definitely working fine.
One thing to be aware of is that
there's a limit to how much a log entry can be,
so you won't necessarily see all the XML in the Logcat.
If it hadn't all downloaded there'd be an error
so it looks like everything's fine.
If we scroll down and have a look,
you can see it seems to just chop it off
but again, that's due to the limit of Logcat
and if we saw an error, there was
an error downloading, it would have got
some form of error and we haven't, in this case.
We also logged the number of bytes received.
You can see we received 61601 bytes,
which is about 6k, close to 7k,
showing in the log so that's good.
Obviously it's not all been logged
but it has been downloaded.
By the way, we can see what happens
when things don't work properly.
It's very simple to do, we can just
change the URL to something invalid.
If we close the manifest file now
and just go back up to the top of our file
and we'll just perhaps add another letter.
This axe, let's add ere there to make that invalid.
If we run that again now,
you can see we get a completely different error here.
Unable to resolve host aerex.itunes.apple.com.
We get a traditional error, so basically
it's an IO exception that's being logged there.
We know that because it's IO exception
reading data, which was the message
that we typed in when we specified
the catch block for the IO exception.
You can see here, IO exception reading data.
Now, the extra bit of the error message there,
if we scroll over and have a look.
Unable to resolve host, obviously that's not
something that's come from us,
that's come from this e.message property
over here to the right hand side,
and it's been printed out and basically
we're impending that to the log entry.
Exceptions include various bits of information
and the message is a useful thing to log
because it provides more information
on what may have caused the error.
Alright, so I've now tested that
all three exceptions are caught
but there's one thing more to do.
Firstly, I'm gonna fix line 23.
We're gonna undo that so we're
left with now a valid URL again.
Then what I'm gonna do is turn off
the Internet connection in the emulator.
I'm going to just bring the emulator on the screen.
What I'm gonna do now is turn off
the Internet connection in the emulator.
Unlike a real device where you could just
stop the wifi and mobile data,
you can't actually do that on some emulators.
What we can do though is turn on aeroplane mode,
which prevents the emulator from accessing the network.
Just like a physical device,
you can drag the top of the screen down.
We can do that here to see the common options
but we can also go into settings if we want
and turn on aeroplane mode from there.
Click on that if we wanted to and
we can do a search for aeroplane, select that.
I'm going to turn aeroplane mode on
and so you get the icon at the top of the screen here.
While I'm in there, notice that wifi is off
and that's because the emulator
is using the computer's network connection.
Instead what it does, it sees it
as a mobile data connection.
Mobile data has got an icon on the far left
if we go back to where we were.
I'll just close that down and bring this out again.
We can see over here this is the option
for mobile data connection.
If you click on that it'll click it off
but we're in aeroplane mode.
I'm gonna click it off in any event.
We're gonna leave aeroplane mode on for now
and we come back here and run again.
I'm gonna clear the Logcat just to
get into the habit of doing that.
Let's see what happens with aeroplane mode on
and mobile data turned off.
You can see here we got the error,
IO exception reading data unable to resolve a host.
Without access to the network,
the host then can't be resolved even with a valid URL.
Now, strangely, occasionally you will find
this emulator will work even though
you've set to aeroplane mode.
If that happens, just stop the app and re-run it.
I presume it's some sort of bug,
perhaps with the emulator, but in our case
you can see that it worked here OK.
That's our app downloading the XML for the RSS feed.
What I will do is before we change anything
is just turn aeroplane mode off
and click on to make sure that
my mobile data connection is activated.
We'll just go back and run the app one more time
just to make sure that it is now working again.
You can see we're now getting data
in the Logcat down at the bottom so we're good to go.
Alright, so that's the app downloading
the XML for the RSS feed.
In the next video, we're gonna change
all this code and write it
in a much more Kotlin-like manner,
so I'll see you in the next video.