G'day everyone, welcome back.
To finish moving our save code from AddEditFragment to TaskTimerViewModel
we need to write the saveTask function in the ViewModel.
I'll put it before the deleteTask function, because it seems more logical to have save before delete.
So let's define our function, give ourselves somewhere to store our information,
and implement a little bit of business logic. Remember, if the task name is empty we don't save.
If it's not empty, let's collect up the values from the interface.
Now, if the task id is zero it means we have a new task.
In which case, we need to add the new task.
We'll make sure that we've got a valid URI, or at least, one that isn't null,
and then we'll assign it an ID
and of course, we log all of that just so we can track what's going on.
If we do have an existing task, in other words, the ID was not equal to zero,
then, once more, we just need to log
and update our task.
All of that's quite similar to the code we had in the fragment,
simplified because we're not comparing the widgets to the original task.
To decide whether we're saving a new task or updating an existing one,
we check the ID. If the ID's zero, then the task didn't come from the database and must be a new task.
The logic to prevent a task being saved, if it's name is blank, also appears here.
That's the business logic, and it belongs in the modelled layer not the UI layer.
We need to test that the URI isn't null.
That's what we do on line 73, because the ContentResolver's insert function can return null.
Although we're using Kotlin, we can't escape from null checks completely,
not as long as we're using Java classes.
One thing that may not be obvious, is why we're returning the task.
We got it from the calling code, so why send it back?
The reason is, that it may now have an ID that it didn't have before.
Let's have a quick look at the saveTask function in AddEditFragment.
That's got a Task instance, which may be null for a new task.
Now, at the moment, we've written the app so that the fragment is removed when we save the task.
You may decide not to do that. The app may allow the user to keep making changes, and saving them.
In that case, the fragment's Task instance should reflect the data in the database.
Otherwise, we could keep creating new tasks when we save, instead of updating the one that now exists.
That's why we assign the result of calling ViewModel's saveTask function, back to the task.
Okay, that's the function added. Let's see if it works.
We're upto task 8, so tap the plus icon in the toolbar and enter task 8 for the name,
8th task for the description, and save.
That's looking good.
How about editing an existing task. I'll edit task 10, and change 1st in its description, to first.
Tap save again, and the entry's updated.
Our change works.
At the end of the last video,
I asked you to think about whether the list in MainActivityFragment would be updated,
if both fragments are using their own instance of the ViewModel.
Clearly it does get updated, so here's a mini challenge.
How is MainActivityFragment receiving a new cursor, if it uses a different ViewModel instance to AddEditFragment?
And here's the answer. The reason it works,
is that both ViewModel instances are receiving notifications from a single ContentResolver.
There is only one ContentResolver and it belongs to the application.
It doesn't matter which instance of the ViewModel makes a change to the data,
the ContentResolver will notify all its observers about that change.
In the next video, we'll go through some slides to explain how all this is working.
See you then.