Welcome back.
All right we've created the adapter for our reports.
Now we need to check if it works.
We'll be using a ViewModel eventually,
but it's easier to test the adapter by creating an activity to use it.
We need to create the activity anyway.
And if we write our functions correctly,
we can just move them into the ViewModel when we're ready to create it.
We've already created the layouts for the activity,
but we can still use the activity wizard for this class,
we'll just delete the layouts that the wizard creates.
Right click the package name,
and choose new, activity, basic activity.
Call the class DurationsReport.
Remember to set the hierarchical parent to main activity.
It's not the end of the world if you forget,
you'll just have to edit the manifest and add the parent activity.
Click finish to continue.
You may get a dialogue like this one with some warnings about conflicts.
I suspect this is a minor fault with the template code.
Because if you check this class carefully,
the one that's wanted is the same as the one that it already is.
If you do get this, you can make a note of it
and refer to it if you get problems.
I think it popped up because there's no space before the closing
backslash in the already is style.
Xml doesn't require a space there,
but it looks like the template codes
just performed a basic search, which hasn't allowed for that.
Anyway enough speculation. Let's proceed anyway.
When android studio has finished creating the activity, close the layout.
We're going to delete contentdurationsreport.xml soon
because we've already created the layout that we'll use for the reports.
All right. This is going to be a bit quick and dirty.
The code we'll reuse will be fine,
but the oncreate function probably isn't how you should write the code
in a production app.
I'll mention that when we get to it. When we do this properly,
we'll want to allow the data to be sorted on the various columns.
I'll create an enum for the column names
to make it easier to see what we're doing.
I'll also add the usual log tag.
Next, we need to declare a variable for our adapter
and also the cursor that will hold the data.
I'll make the sort order default to the name column.
We're also going to display records between a range of dates
that will either be for an entire week or a single day.
We'll do that by changing the selection arguments
that we pass to the content resolver.
I've hard coded the date range there.
We'll be allowing the user to choose dates once everything's working properly,
but for now I've just used the timestamps for the 1st
to the 31st of May. In
the oncreate function, we'll delete the floating action button code.
I'll replace it with a call to setDisplayHomeAsUpEnabled
and pass true.
That adds the up bar to the toolbar to let us return to main activity.
The code to initialize the adapter
and associate it with the RecyclerView is standard.
We've done the same thing a few times now.
There's one more change to make to oncreate.
We need to load the data into our cursor.
I'm going to create a function to do that. I'll call it load data
and add it below oncreate.
This is very close to the final version of the function
that we're going to use in our ViewModel class.
It starts by setting order to the appropriate column that we want to order by
using a when expression.
We pass order to the content resolver's query function
to sort the data.
The call to query shouldn't need any explanation.
We've done this a few times now.
Remember that passing null for the projection
will include every column in the data.
That's usually what you want to do when you're using a database view.
If you don't want any of the columns,
you probably wouldn't have included them in the view.
Of course, there are times when you want to use the same view
for different purposes.
In that case, you can provide a projection.
But it's handy not having to bother setting one up.
We wrote our SwapCursor function to return the old cursor
when we provide a new one to the adapter.
That allows us to call close on the old cursor
to avoid leaking the resource.
That's an acceptable thing to do.
The cursor was created by the DurationsViewModel class,
but that class doesn't use it.
This DurationsReport class uses it for the adapter
and knows when the old cursors can be closed.
But what happens when we close the activity
and return to main activity? Will our cursor get closed?
The answer is no, it won't.
It's our responsibility to free up any resources that we create.
You may be tempted to close the cursor in the ondestroy function
but that will cause problems.
If you want to see what they are,
add the ondestroy function at the end of the class.
Calling swap cursor with null
will return a reference to the existing cursor.
If that's not null, we call its closed function
to free up the resource.
If you fully understand the various life cycles, you'll spot the problem here.
Don't worry if you're not quite that comfortable with the life cycles yet,
you'll spot the problem when the app crashes.
If you want to try that,
rotate the device while viewing the reports to cause the crash.
Remember that the cursor is created by the DurationsViewModel
and that will survive the configuration change,
that means the database won't be re-queried
and the adapter will try to read from this cursor,
the one that we've just closed.
I'll talk a bit more about this when we tidy the app up in the next section.
For now, I'll just comment out this function.
That will leave us with the resource leak, but we'll fix it later.
Okay. Three more changes, and we're ready to test.
The first is to call load data in our oncreate function.
That's the bit I don't suggest you do in your final code.
We're just testing our adapter here and our layouts too
because we haven't seen them running yet.
If you examine the logcat when we run the app,
you'll see that the database is queried every time the device is rotated,
that's fine for this test, and we're going to move load data into the ViewModel shortly.
Okay. The next change is to use an intent
to launch this activity when the menu item is tapped.
We do that in main activity
because that's where the main menu code is.
We'll add another branch in OnOptionsItemsSelected.
To tidy up, delete the unused content durations report
xml layout file.
That will cause an error in the activity durations report layout.
You need to tell it to include task durations instead.
Open up activities durations report,
and select the include widget
in the component tree.
Using the symbol next to layout,
select task durations.
While we're here, delete the floating action button
from the component tree.
Okay. The app should now run.
Tap the report icon the one that looks like an eye,
that looks good and we've got a load of data for May.
Scrolling the report should show different tasks.
We can't sort the report at the moment,
but the adapter looks to be working and our portrait layout lines up nicely.
Rotate the emulator into landscape to check if landscape is also correct.
If you get any problems,
add logging to the adapter code to work out what's going wrong.
If the data doesn't line up,
check the margins and padding carefully in the layouts.
The last test in this video
is the up button on the toolbar.
Tapping that should take us back to main activity.
In the next video, we'll change the emulator to a different locale
to make sure the dates are displayed correctly.
I'll see you there.