(futuristic electronic music)
(keyboard keys clack)
So at this point now,
there's only a few more changes we need to make to the app
now that we've moved the non-UI functionality
out of MainActivity.
The first thing I want to do, though,
isn't directly related to this change.
I recorded the original top10downloader videos
before Kotlin allowed top level constants.
We have covered that now in an earlier video,
and I've been using them ever since,
so I'm gonna start by moving our constants
from our MainActivity class
out of the Main class.
Go up to the top there,
and the ones we want to move here are TAG.
We also have STATE and STATE_LIMIT,
so I'm gonna start by moving TAG
above the class definition,
and also, for STATE_LIMIT and STATE_URL,
we'll do the same thing there.
Paste them up there.
We'll also make them const,
like so,
and what I'll also do is take the opportunity
to delete the toString function from the FeedEntry class.
We used to, in a very early version of this app,
before we created the proper layout for the dialer,
so it's fairly redundant now.
Plus, that cleans things up a little bit as well.
All right, so on to what changes
we need to make at this point.
Well, firstly, looking at the onCreate function,
it's attempting to download the data.
So it should just now be subscribing
to the ViewModel's LiveData instead,
so I would probably start by creating
our feedViewModel instance in onCreate.
So I'm gonna do that for now.
I will do that after this code
for the savedInstanceState.
So val
feedViewModel
equals
as ViewModel,
and notice, because we've added the entry in gradle,
Providers is now an option we can select.
So it's ViewModelProviders.of.
It has this and then a right parentheses,
.get.
Then in parentheses again,
FeedViewModel::class.java.
Then on the next line,
we're going to do a feedModel,
or feedViewModel, I should say,
.feedEntries.observe
parentheses this,.
On the next line,
we're gonna type Observer,
and the top there will be
List of
FeedEntry,
and then we're gonna create a lambda function,
so left and right curly braces,
and it's gonna be feedEntries,
then our arrow,
feedAdapter
.setFeedList,
then in parentheses, feedEntries.
Close your parentheses.
Then we got our right curly brace
and then a closing parentheses.
Now, this is very similar to what we've done before.
Instead of setting the text in a widget,
we're setting the data in our Adapter.
That was originally handled by the DownloadData class,
but now it's the job of MainActivity.
The Adapter populates the List View,
which is part of the UI,
and we got an error there,
unsurprisingly because we haven't yet created the Adapter.
So let's go ahead and do that.
We'll add that just after the onCreate call
before the check for,
if savedInstanceState is not equal to null.
So do a val,
feedAdapter is equal to
and FeedAdapter,
and in parentheses, this,
and it's gonna be R.layout.list_record,
comma,
then EMPTY_FEED_LIST.
That creates the Adapter,
and we're gonna do an xmlListView.adapter
is equal to,
our newly created feedAdapter.
So that's fixed one error.
What we're doing when we observe a change in the log data,
is setting the new data to the Adapter.
Our Adapter hasn't been set up to do that.
We just created a new one
in the earlier version of this app.
We need to make a small change to the Adapter,
so that it provides a way for its data to be changed,
and I'm talking about the setFeedList function
that we're calling on line 52.
So let's do that now so that this code makes more sense.
I'm gonna add it to the start of the FeedAdapter class.
Let's go ahead and open our FeedAdapter class,
and we're gonna add it there,
before our getView.
'Kay, so it'll be fun setFeedList
parentheses feedList:,
and we're defining a List of FeedEntryObjects there.
FeedEntry.
Close your parentheses,
and open up a left and right curly braces.
Then we're gonna type this.applications
equals feedList.
Then we're gonna do a call to notifyDataSetChanged.
So that's pretty straightforward.
It just replaces the applications List
with the new data that we provide as an argument,
but we have got an error on line 28
because applications is a val in the constructor,
and you can see that's defined on line 21.
If we gonna change it, obviously we need it to be a var.
So we'll go ahead and change it to var there,
and that fixes that error.
So that's the only change to our Adapter,
and that should've fixed another error in MainActivity,
so let's go back and check that.
It has fixed that, but it's now giving us another error.
So Android Studio is now objecting to passing feedEntries
because that function wants a non-null type,
and feedEntries could be null.
Now, we've taken steps to make sure it isn't.
We've initialised it, if you recall, to be an empty List.
So there are a few ways to fix this.
The easiest, and the one I like the least,
is to use the bang bang operator.
So we could do something along the lines of,
we've got feedAdapter.setFeedList,
feedEntries in parentheses, add the bang bang operator.
We know here that it's safe to do that
because we've made sure that our LiveData object
will always contain a List.
The Kotlin language is still evolving,
and you may need to resort to using the two exclamations,
the bang bang operator, from time to time,
and that's especially true
when using Kotlin with Java classes,
and most of the Android framework, if you recall,
is currently written in Java,
but, in this case,
I think it's safer to use the Elvis operator
and pass an empty List if,
and only if, feedEntries is null.
So what I'm gonna do is duplicate that whole line there,
well, those two lines,
comment out one,
and instead, we're gonna change that
to use an Elvis operator,
and so basically, within the parentheses for setFeedList,
we're gonna put feedEntries,
get rid of the bang bang operator,
and put ?:, which is the Elvis operator,
EMPTY_FEED_LIST.
So basically, if feedEntries is null,
we'll pass that EMPTY_FEED_LIST
that we declared in our feedViewModel.
So even though it would be safe here,
I like to avoid using the bang bang operator
as much as possible.
That approach really does make sure
that our code won't crash.
Now, once you're happy with this,
you may prefer to create your own LiveData
subclass that can't return null.
If you do a bit of Googling,
you can find some solutions to that.
One such solution
that we found Googling,
and it gives you a bit of an entry
and a way there to actually resolve that,
but, with that said,
I suggest you stick with things like the Elvis operator
until you're really comfortable with LiveData,
and that's because creating subclasses
of things that you're still learning about
probably isn't wise.
You can end up writing code that you don't fully understand,
and that, in turn, makes debugging a nightmare.
All right, so getting back to our code again.
Back to MainActivity.
We've still got some entries,
or three errors rather,
but luckily, they're all trivial to fix.
Well, trivial apart from the other error that you'll get.
So, our call to downloadUrl, though, first.
That could be changed
to call the downloadUrl function
of our ViewModel instead,
so instead of downloadUrl,
it can be
feedViewModel.downloadUrl.
That's one, and there's another one
down here in onOptionsItemSelected,
so let's change this one down here,
ignoring the error on line 89 for now.
So feedViewModel.downloadUrl,
but notice when I do that, unresolved reference.
The problem we've got here is that feedViewModel
is local to onCreate.
We'll go back and have a look at that.
We're to find that, on line 50, for onCreate, it's a local.
We have seen how to fix things like this.
We just move it into a class variable
and then use a lazy delegate to resolve it.
So let's go ahead and do that,
so I'm going to cut this out
of our onCreate function.
Paste it in as a class variable.
We're gonna make it, instead, a private.
That's feedViewModel.
Then we're gonna add a definition,
so it's :,
the type is feedViewModel.
Then we need to type by lazy.
Instead of the equal sign,
we're going to add a left and right curly braces.
We'll have the left curly brace there and the right one
surrounding the code that we need to be called,
and that's now defined, as you can see,
and scrolling down, looking at our error now,
we can see we fixed the error that was
present there on line 93.
We've still got an error here, though, on line 89,
relating to trying to invalidate the URL.
If you recall, our ViewModel has got a function to do that,
and we've now got a reference to it that we can use.
So let's actually change this altogether
to be a call to
feedViewModel
.invalidate.
That should've fixed all the errors,
and we're just about done,
but one strange thing that can happen
is when we move the log TAG into the top level,
as we did earlier in this video,
sometimes, you can find that there's a weird import
that Android Studio will add.
It'll be something like
import android.content.contentValues.
So if that does pop up,
you'll need to remove that
because you'll have a duplicate entry for TAG,
and that may be causing you some errors.
In my case, I haven't actually got that error there.
All right, so does it work now?
We've now made changes to all our code,
or to most of our classes.
The time now has come to actually start testing the app out.
So let's work on testing that app in the next video.