Alright, so in MainActivity, we need to
add the code to use the menu that we
created in the previous video. Now if you
chose the Basic Activity template when
creating this project, then you'll
already have a stub for the two methods
we need, but so we can get Android Studio
to generate them for us anyway. So what
I'm going to do is, I'm going to go back
to our MainActivity, and I'm going to
put the cursor after the
onCreate method, down here, and then I'm
going to do a Ctrl-O, and the methods
that we want here are the onCreate
)ptionsMenu, and also the onOptions
Items Selected. So here, the search
feature in this dialogue is really helpful,
so if I type onoptionsmenu, it finds
matches even if this actually
represents two different words. Now there's a
few actual entries with onOptionsMenu
in their names, and the highlighting
makes it very easy to spot the ones we
want. So I'm just gonna press the down
arrow until we get to the one we want -
we've got onCreateOptionsMenu, on
OptionsMenu, onPrepareOptionsMenu. So
onCreateOptionsMenu is the one we
want, then the other one we want is on
OptionsItemSelected -
that's this one here. So I'm actually
going to command click that on a Mac,
and I've now got that one selected, and I've
also got the other one selected. You do a
Ctrl click if you're on Windows or Linux,
click on OK, and we've got our two
functions now created, or overridden for
us. Now onCreateOptionsMenu - that
function's called when it's time to
inflate the activities menu, and that
means that, which means create the menu
objects from the XML file. Now we've got
this basic stub now, so I need to change
it slightly. Now when we try to inflate a
view in the adapter, we have to get an
inflator from the context. But an
Activity or appCompatActivity is a
context, so we can just call getMenu
Inflator
to get an inflater directly, then call its
inflate method and give it the resource
ID of the xml file, containing the menu's
xml. So let's have a look at doing that.
So this is in the onCreateOptionsMenu,
and what I'm going to do here is remove
the super call. So the call's going to
be menuInflator.inflate, and then
parentheses, R.menu. and it's going
to be feeds_menu comma space and
I'm just gonna type menu as the second
argument. Then we're going to return
true. Now as this is Kotlin, we don't have
to use the getters and setters, when
calling the Java code in the Android
Framework. We can refer to the menu
Inflator property directly and use its,
or call rather, it's inflate method. We
then have to return true to tell Android
that we have inflated a menu, and let's
just try this. We're going to run the app
in the emulator, and just confirm that we
can actually see the menu.
Okay, and if we come over here now and
click on the little three buttons,
we can see our menu is actually showing
up there now. So that's good - we've got a
menu in our application. Alright, so back
to the code. So creating and displaying the
menu is really very easy. Now the menu
doesn't do anything yet - we have to write
some code to, you know, specify what
happens when the various items are
selected, but we're well on the way. Now
you've probably guessed that the place
to specify what the menu items do, is
that onSelected, onOptions rather, Items
Selected function. Now it's called
whenever an item's selected from the
options menu. Now as different options
are selected, we're going to be changing
the url that's used to access the feed.
So we need to store the url in a
variable. So let's have a look at doing
that. So again, what I'm going to do is remove
the return call. We're gonna start on
this with val feedUrl colon String. Then
I'm gonna type, on the next line, when
parenthesis item.itemId, right
parenthesis and open a code block. But, as
you can see, we're actually getting an
error here. So Kotlin's objecting
because we're accessing a property of a
nullable type. Now item's passed in as a
MenuItem? type, so it can
be null. Now once again, it wouldn't
surprise me if Google and an @NonNull
annotation to that, so you may well not
be getting this error, depending on when
you're watching this. At the moment
though, we can either use the safe call
operator when accessing itemId, or we
can modify the function signature. Now, I
previously said that modifying these
function signatures isn't something
you should do without a lot of thought,
so now I'm suggesting we do it for a
second time. How can you be sure, or how
can we be sure that it's a safe thing to
do here? Now if you go ahead and modify the
function signature so that the item
parameter is of type MenuItem, rather
than the nullable MenuItem? -
that's the one with the question
mark after it, up here - then we need to be
absolutely certain that item can never
be null.
Can we be certain? Well actually, yes we
can. This onOptionsItemSelected
function's called, when a user selects an
item from the menu. Now if
they've selected the item, then there must
be an item, otherwise, they couldn't have
selected it in the first place. But with
that said, I'll say it again, don't go
changing these override function
signatures without a full understanding
of what's happening behind the scenes. If
you're not sure, then the best thing to
do is call the safe call operator.
Alright, so I'm going to come back and
do that - I'm going to put the question
mark after the item, down here in the
when call, and that actually removes the
error. Basically, Kotlin is going to
ignore this when expression if item is
null, and that's perfectly acceptable. So
if you're working with other framework
functions, use the safe call operator on
nullable types. Alright, now I'm going
to modify the signature here, though, so
I'm going to undo that change, but I'm
just, sort of, showing you there was an
alternative to this. So I'm going to undo
that change, and instead, what we're going
to do is remove the question mark from
the menu, or from the parameters that was
passed to this method - we talked about
the reason why that's valid. Al
right, so that's fixed the error, and we can
now add the branches to the when. Now
let's go ahead and do that, so we're
going to go through each menu option and
what to do. So I'm gonna start here, so
within the when clause itself, when, it's gonna
be R.id. and we're gonna do menu
Free first, then add our arrow token. Then the
next line, I'm gonna put feedURL is equal to.
Now I'm just gonna copy this link, okay, and
I'm going to paste it in there, like so.
So that's the Top 10 Free applications.
The next one's gonna be R.id.
mnuPaid, arrow token again, feedUrl
equals, and you can get these links from
either the RSS feeds folder.
Alternatively, what you can do is just,
we'll have a link or have them accessible,
as in a text file in the resources
section for this video, and you can do
the same thing there.
I'll just fix up that error there.
Alright, so that's for the Top 10 Paid
applications, and for the last one, R.id.
mnuSongs, arrow token, and it's going to
be feedURL equals. And for that one, I'm
going to grab
the top songs - RSS feed link - and
paste that in. So that's three options.
We're going to add an else, arrow token and
it's going to be returned super.on
OptionsItemSelected item, and then
we're going to delete this line, and
outside of the when, we're going to do a
return super.onOptionsItemSelected
per item.
Now again, with the urls, I copied
them off-screen, but you could just go to
the Apple's website and paste them in - the
RSS feeds that we've looked at
previously - or there's going to be a text
file in this video video's resources
section and you can just grab them from
there. So now that we've done this, when a
menu item's selected, feedURL will hold
the address of the corresponding RSS
feed. So we can then pass that url to a
downloadUrl function to download the
data. I'm going to write that in a moment,
but first, let's just finish off the on
OptionsItemSelected function. So in
fact, what we'll do here is we'll get
rid of this return, and we're going to
change it to downloadUrl
parentheses feedUrl, and then we're
going to return true.
Now this else branch is very important,
and you should always include it when
creating code to react to menu choices.
Now at the moment, it should never
execute, and that's because, on the three
lines above, we've matched every one of
the, or each of the possible entries in
our menu. But it's possible to create
sub-menus, though, and we're actually going to be
doing that soon. Now when you go into one
of those sub-menus, Android triggers a
call to this method when the sub-menu's
opened. If you don't return from the else
branch, then any code after the when will
execute, which could cause problems. And
here, in fact, it would call the download
URL method with an empty string,
which isn't very good - which isn't good
at all. So you definitely want to check for
that - have that else branch present.
Alright, so what's this downloadURL
function for? Well what we're going to do,
is once the URL has changed, is exactly
what we currently do in the
onCreate method. We're going to create a
new DownloadData object. We're going to
call its execute method with the new URL,
and because duplicating code is a bad
idea, I'm actually going to move those
few lines into their own method and call
that method in onCreate, and also here in
the onOptionsItemSelected. So let's have
a go at doing that now. So I'm going to
actually put this above the, we'll actually put
it just below the onCreate menu, and it's
going to be private fun downloadURL
parenthesis. It's gonna be feedURL :
as a String. Alright, so what we're going
to do then, is actually move these three
lines here, out of the onCreate method.
We're gonna cut those out. Okay, so move them out
of there, and paste them into the
downloadUrl method. Let's change the
text here, or the message, and we'll go
with downloadUrl starting AsyncTask,
and instead of onCreate done, let's go
with downloadUrl done. And now that
obvious change is to change the
hard-coded parameter here, for execute. I'm
going to delete that, and that should now
be feedUrl. What I'll do is, before that,
I'll undo that change, because I need
that string - you'll see why shortly. Then
I'll just go back and delete it again and
put feedUrl in there. Then back in
here, in our onCreate, we still
want to call this function. So what I'm
going to do is do a downloadUrl
parenthesis, and I'm going to paste that
in there, like so. So we're calling that
method, and I'm going to add a Log.d, so
Log.d TAG onCreate done.
I guess i could've left that in there,
instead of typing it again. Okay, so you
can see what we've done there now.
WE've changed the two logging lines to
log the name of the new method, but
otherwise you can see, it's pretty much
the same code that we had, with the URL
now parameterised. Now if that last bit
looked a little bit too easy, to be true,
well it is. We should now be able to
choose one of the three feeds from the
menu, so I'm going to try and test that
and see that that works - have our app running.
alright, so there's our app running, so
so far, so good - it's working okay. What's
not so good, though, is what happens when
I select one of the menu items. So if I come
up here, and just click on one of
them - Free apps - you can see we've got a
crash, right away,
and the problem is that you can't use an
AsyncTask more than once.
There's no way to restart one. Now you can
create as many instances as you want but
each instance can only be used once. And
just to confirm that, if we go back to
the log cat and have a look, and you can see
the error message there; Cannot execute
task: the task has already been executed
(a task can only be executed once) only
once. So fortunately though, we can fix
this easily by just creating a new
DownloadData instance, each time we want
to run the task.
The only real complication here is that
we need downloadData to be visible
throughout our Activity, so that we can
cancel it if necessary, in the
onDestroy method. What that means is we
can't initialize it when we declare it,
back up here, up here on line 35. Now we
have to set it to null here, because
variables have to be initialized to
something in Kotlin. Now sometimes -
and this is one of them - you just can't
avoid using nullable types in Kotlin, and
generally, that's when you're using Java
classes. Now the Asynctask class was
written in Java, well before Kotlin was
created. So if you want to be able to
cancel our task when the Activity gets
destroyed, we're going to have to use a
nullable type here for that. So I'm gonna go
ahead and change that now, so private val
downloadData : We're gonna change this
all here - let's just delete that
entire reference there. So it should
now be private val downloadData :
DownloadData? equals null.
The other thing it should also be is a
var now, and not a val. Now that we've
done that, we can create a new instance
in the downloadUrl function. So we can come
along here and put downloadData equals
DownloadData this, then xmlListView.
Then on the next line, we can change it
to downloadData? dot
execute feedUrl. So whenever we call
functions on our downloadData instance,
we have to use the safe call operator,
and that's because it could now be null.
So you can see that I've added the safe
call in the downloadUrl, on line 48, and
also need to go down to the
onDestroy function, and do the same thing
to that as well - safe call operator
with the question mark there. So, as a
general rule, we should avoid using
nullable types wherever possible, but
sometimes, as I mentioned, we just have to
use them. Alright, so with those changes
now, the code should work, so let's just
check our app to see that it does. So at
the moment, we've got the Free one
working. Let's try another Paid app, or
Paid apps, I should say. You can see we've
got a different set of output there,
Songs, then going back to Free apps. So you can
see that's now working nicely. I'm
choosing, I've chosen different feeds there, as
you saw, and the app displays the data for
the Free apps, Paid apps and Songs. Now
that's working fine, and in case you wondered
when I selected Songs, the Songs feed
doesn't have a summary tag, but the
program doesn't crash so it doesn't put
any text in - it just doesn't put any
text into it. So that's how easy it is to
implement a menu in your
apps. So I'll stop the video here, and in
the next one, we'll add options to toggle
between the Top 10 and Top 25 feeds.
So see you in the next video.