G'day everyone, welcome back.
In the last video we got our cursor adapter working.
Well we think it's working. We know it hasn't broken anything
and that it works fine when there is no data,
but we can't test it further until we've created the view model class that's going to provide it with data.
So let's get started.
Right click on the project and choose New Kotlin File/Class.
I'll call it TaskTimerViewModel
and select class from the drop-down.
We saw how view models worked in the previous section.
This one's going to be a slight variation.
Instead of extending the ViewModel class we'll extend an Android view model.
Both classes work the same way. AndroidViewModel is a subclass of ViewModel,
but it also has an application property.
That's going to be useful if we want to access the database,
because we'll need an application to get a ContentResolver.
So let's complete our TaskTimerViewModel declaration.
Additionally, of course, let's put a tag in to help with our logging.
It's worth control clicking on the AndroidViewModel to check out the source code.
That's command click on a Mac.
Once again, this is Java code.
So it's slightly more verbose than Kotlin but it's easy enough to understand.
AndroidViewModel is a subclass of ViewModel.
The Java keyword, extends, is used to specify the superclass.
As we said, it has an application property.
The application property is set in the constructor, which is a separate method in Java.
You can write constructors that way in Kotlin, but it's more convenient to just use the primary constructor,
and list the properties when defining the class.
Being Java, it also has a getter for the application, the getApplication function.
This complicates our Kotlin code slightly
because it allows the application argument to also be a subclass of application.
That's what the T extends Application means.
It can return any type as long as that type is an Application, or a subclass of it.
You'll see the slight complication that this causes us, shortly.
Ok, back to our ViewModel.
We want it to make a cursor available via a LiveData object.
We've seen that before. When the cursor changes, observers will be notified of that change.
In this case, the observer will be MainActivityFragment.
When it gets notified of a change, it'll swap the cursor in the RecyclerviewAdapter.
We'll make our database cursor a private variable of type MutableLiveData.
We'll define our cursor as a LiveData cursor
and we'll return the database cursor in the get method.
Our ViewModel will load the data from the database then update the database cursor LiveData object.
We'll add that code in a separate function.
We'll define our load tasks method.
Our projection array will define the data table's columns.
We'll put a comment in to remind us of what we're doing,
and we'll define our sort order.
Then we'll define our cursor using to projection array and the sort order.
Then we'll set the database cursor value.
That's the standard code for fetching data from the ContentProvider.
What might look a bit strange is the way we have to get the ContentResolver on line 25.
As we're using Kotlin you may expect that we could refer to the application property directly,
rather than calling the Java getter method.
That's the slight complication I mentioned before.
Because we have to specify the exact type of application inside the diamond operator,
we must use the getter.
It's a minor inconvenience, but does make the AndroidViewModel class more flexible,
and it does it in a type-safe way.
If you want to store the application parameter that was passed via the constructor,
then you could create a property to store it.
That would work, but I wanted to show you this strange syntax.
I've also jumped the gun a bit here.
On line 30, I've called the postValue function to set up our database cursor's value.
This is instead of assigning it directly to its value property.
You may remember, back in the previous section,
that postValue was used when you're updating the value on a different thread
to the one that the MutableLiveData object was created on.
I won't say any more about that just yet
because I'll be giving away the answer to a mini-challenge if I do.
Ignore the warning about closing the cursor, if you get one on line 30.
The warning's correct, it is our responsibility to close the cursor.
We can't do that now though. If we do, it will be invalid when the adapter tries to get the data from it.
I mentioned that briefly when we wrote the swap cursor function in our adapter.
This is what I was referring to.
The adapter doesn't own the cursor, so it shouldn't close it.
We'll see how to deal with this when we've finished writing all the code.
All right, we're just about done.
All that remains is to call that loadTasks function.
We can do that in an init block, which will be executed when the ViewModel instance gets created.
I like to put init code at the start of the class, after the property and the variable declarations.
We'll make sure that we log the start
and then call our load tasks function.
In the next video we'll subscribe to the ViewModel and see it working.
See you in the next one