We've now written a ContentProvider
that allows us to query our database.
There's still a bit more work needed
before our ContentProvider's finished
though. We need to be able to insert new
rows, update existing data and delete
rows as well. So going back to the App
Provider class that I'm in on screen
now, we've got stubs for these methods
down the bottom; insert, update and delete.
They were created for us, if you recall,
by the Android Studio code generator
when we implemented the abstract methods
of the ContentProvider class. Before we
write the code for those methods though,
let's have a quick look at this getType
function which we've got at the top here,
above the query method starting on line
62, because we also need to implement
that. Now this function's used when
something needs to know the MIME type of
the data available at the URIs that
our ContentProvider recognizes. Now we
don't need this function to return
anything for this app, and because we're
not returning any recognized MIME types,
the information returned won't be much
use to any external apps either. But we
have to implement it and because of that we
may as well do it properly. So what's a
MIME type?
Well the Android documentation on mime
types doesn't really say much at all, but
if you search online for mime type you'll
find a lot of information. Now there's a
Wikipedia page which has got a good
explanation. I'm just going to bring that up
on the screen there now. This is a good
page to visit. As well as a good
explanation it's also got some examples
of common MIME types shown, and you can
see those when we scroll down a little bit
further down here, under common examples.
So they're basically - MIME types - there to
let other systems know what kind of data
that they can expect to get back from
our ContentProvider. For example, if we
created a ContentProvider that returned
png images - ping images - then the CONTENT
underscore ITEM_TYPE would
be image/png. So the idea is, that a
program can request the MIME type of our
URIs and use it to work out what to
do with the data returned. So if we
returned a MIME type of image/png
the
program would know that it received a
png image, so could then process it or do
something else with it.
Remember that Android has been designed
to be very flexible, and a Content
Provider can provide all sorts of
content. For our app the URIs passed
to our query function will result in a
cursor being returned, so they're not
really returning anything that fits a
generally recognised MIME type. Now we
could just return null, but frankly
that's a little hazy. So let's have a
look at the Android documentation for
some more information here. And if we
just do a bit of a search here for vnd,
under getType here as you can see, it
tells us here that the type should start
vnd.android.cursor.item
for a single record, and vnd.android
dot cursor.dir for multiple items, such
as a whole table. So I've used those
values in our TasksContract class, and
added our content Authority and a
table name to the end. So these are
custom MIME types and won't mean
anything to any other app but ours. Now
one way that these types may be used, is
when displaying our data using
intents. So our app could include an
activity to display reports, and we could
allow that activity to be launched by
another app using an intent. So in that
case, the other app would pass the
appropriate content type to the intent,
so we'd know whether it was asking us to
display the entire table or just a
specific row. Now it's probably not often
that you'll implement something like
that, so I suggest just using this next
function as a template and not really
worrying too much about it. So what I'm
going to do is copy some code and paste
it in there, and you'll be able to grab
this code from the resources section of
this video. So we'll go back to Android
Studio and I'm going to paste it in,
replacing the TODO for getType. I'm
just going to make a bit of a space, a few
spaces here, so it's a bit more readable.
It's probably suffice, will suffice for
now. So you can see here we're doing a
very similar thing to the query method,
using a uriMatcher to decide what URI
has been passed, or we've been passed. We
then return the appropriate MIME type.
Now I'm going to comment out the code
here for TIMINGS and for TASK_DURATIONS
because we haven't written those yet. But
in general don't worry about these MIME
types at the moment. I've added this
typical code rather than give the
impression that it's okay to return null.
However, our app won't be using these types. So
that explains the reason for the other
two constants in our TasksContract
class. Alright, so now that everything
in the TasksContract class has been
explained, it's time for a challenge. So
your challenge is to create a contact
class for the Timings table. Now the
Timings table will be called Timings with a
capital T, and will have the columns that
we saw in the database diagram at the
start of this section. Now to save having
to modify your code, it'll be helpful if
you use the same names as me for the
columns constants. So the names I'll be
using are; ID, TIMING_TASK
_ID, TIMING_START
_TIME and TIMING
_DURATION. Now just as a hint
here, you can copy the TasksContract
class and modify it to produce the new
contract.
Alright, so pause the video now and see
if you can figure that out, and when you
come back we'll go through it, step by
step. Pause the video now.
Alright so hopefully you went well with
that challenge. What you'll find is that
all the contract classes are pretty much
the same. Once you've created one you can
use it as a template for all others. So
what I'm going to do is open up my code and
go into the learnprogramming.academy
dot tasktimer package, and I'm going
to copy the TasksContract - control C,
control V or command C and V on a Mac.
That brings up the Copy object dialog
and I'll change that to timings. So
the name of the file will be Timings
Contract. Alright so now that Android
Studio's helpfully changed that name for
us, here under the object, we just need to
rename some of the other things in this
file. So firstly for tasks, I'm going to
change that to Timings's - the name of the
table in our database.
I'm going to take a copy of that because
I'll be pasting that in a few other
places. So the URI to access the,
that should be Timings table, then down
here it's going to be Timings fields
instead of Tasks fields. Now in terms of
the actual constants we've got ID, which
is correct. We'll leave that as it is. The
next one is TIMING_TASK_ID,
so it's TIMING_TASK
_ID. We're going to set that to
the name TaskId. Alright so the next
one is going to be TIMING_
START_TIME. I'm going to set
that equal to StartTime, capital S
capital T. And the last one will be
TIMING DURATION, TIMING_
DURATION. I'm going to set that equal to
Duration with a capital D. So contracts
classes for different tables may have
more or less columns, but apart from that
they're very easy to create. Alright so
now that we've actually done that and
completed those changes, we can go back
to our AppProvider class. We can now
uncomment some code in that class. So
firstly, I'm already here in the getType
function, so I'm going to uncomment the
code that we created but commented out,
or that we pasted and commented out, and
I'll uncomment that now because we've
actually got the TimingsContract
implemented. Then we'll go up to the
buildUriMatcher function. We're
going to uncomment the two lines
relating to TimingsContract, and I'll
comment out the next, uncomment the next
line out as well. And then finally, go
down to our query method, we've also got
some comments in there that we can
uncomment. So I'm just going to take a
copy, or select rather, everything
relating to TIMINGS, as well as the
comment on the blank line after, and
uncomment that all. So we're not sure what
fields we're going to have in our
database view, so we can't really create
the DurationsContract class
yet, but when we do it'll be just about
the same process. There's one
unimportant difference that I'll mention
when we create it though. Alright so
I'm going to stop the video here. In the
next one we're going to work on the code
for the insert, update and delete
functions in this AppProvider class.
So I'll see you in the next video.