Alright, now just as we have to define
our activities in the Android manifest
so that Android knows how to launch the
activity, we also have to define our
ContentProvider in that manifest as
well. So we can do that by adding a
provider tag, so let's open up our
manifest file, AndroidManifest.xml. I'm
going to come down here, after the
closing of the activity - closing activity
tag - before the closing application tag,
and add our provider. So I'm going to type
provider, noting that Android Studio has
filled out some information for us.
So the name we're going to go with is the
name of our package first, so it's
learnprogramming.academy dot a task
timer dot, this is for the authorities,
it's going to be dot provider. That's the
name we've assigned for our ContentProvider.
Then the actual name will be the full
package and class name of our App
Provider. So it's going to be learn
programming.academy.task
timer.AppProvider. You can see
that Android Studio's helpfully filled
that in for us. Then we also want to add an
android colon exported equals double
quotes false. then we'll close off the
provider tag. Alright, so as you saw the
name attribute is the full name of our
ContentProvider class. So we're using
the package name followed by the name of
the class.
Now obviously you'll replace that
package name with whatever is
appropriate to your application, if you
haven't followed the same conventions I
have. And obviously you can see the package
at the top on line 3 if you need to
refer to what it is - how it's been set up
for you. Now the authorities attribute,
ideally, we'd want to be able to use the
constant CONTENT_AUTHORITY
from the AppProvider class for this
attribute. But I haven't, as of the time
of recording this video, found a way to
use that constant. Now the final
attribute is to, or is used rather, to
specify whether the ContentProvider can
be accessed by other applications - that's
the exported equals. Now we can change
that later, but for now I'm keeping it
private by setting that to false. The
main reason for doing that is we don't
have to worry about permissions just yet.
Now there is a description that you can
refer to for more information, and I'll
just quickly open up that link. They're
definitely worth reading through this
page, so that you're familiar with all the
other options
you can use when you come to create your
own providers. Alright, that's the only
change we needed to make to the manifest.
So going back to our MainActivity, and
specifically the onCreate function, I'm
going to remove the test code that uses
the AppDatabase, then replace it with
code to query the database using the
ContentProvider. Now the code will be
very similar to what we used in our
ContentResolver example previously. And
keep in mind we don't need to get our
own instance to that database, and we
call the ContentResolvers query
function instead of the database
function. Let's go ahead and make those
changes. So first thing, I'm going to delete
these two entries here now, and instead
what we're going to do, instead of a db
dot rawQuery I'm going to change that to
contentResolver dot. And it won't be raw
query, it'll be query - that's the name
of the function we created. Now the
contentResolver needs a URI as its
first argument to its query function, and
we're going to get all the records, which
means we can pass null for the remaining
four arguments. Let's go ahead and make
those changes. So I'm going to delete the
rest of that line, and we'll start with
passing it TasksContract.CONTENT
_URI comma. Then I'm just
going to add the four arguments null;
projection, selection, selectionArgs and
sortOrder, all as null, because again
we're actually going to get all the
records at this point in time. So that
should be enough now for us to run the
app, and confirm that it'll actually work
and we should be able to retrieve the
data. So let me just start the emulator. Okay
we'll give it a run, then I'll open the logcat,
and I'm just going to type forward slash
main so we'll only get the information
relating to our app - MainActivity in this
case. And you can see there what's
happened. We can see the three rows that
we added from the terminal in a previous
video, when we were checking that the
database was created properly. Now if you
were testing on a physical device rather
than an emulator, you won't have been
able to run the SQL
insert commands in the earlier video. So in
that case you'll see the number of rows
showing zero. That's fine though because
it's correctly reporting that there's no
rows in your table, but the query is
still working fine. And if you do get any
other errors then you've got a problem,
but zero rows is perfectly okay if you
weren't able to add any for whatever
reason. We'll be actually writing the code
to insert records soon, and then you'll
be able to see everything working
fine on a physical device. But that's
good. At this point in time the Content
Provider appears to be working.
Now this isn't how we'll be using it to
execute queries, by the way. Our query was
executed on the UI thread which isn't
good, but at least we've tested the
Content Provider, and we know now that it
can successfully query the database.
There's a couple more things we can try
before we move on to adding the
insert, update and delete functions. At
the moment we're returning all records
from the column. Now we can change that
by specifying a value for the projection
argument. And we can also change the
ordering to sort on the sortOrder
column, and what I'll also do while we're
making these changes is we'll delete the
floating action button code, because you
won't be using that in this app. So I'll
do that first - get rid of this code down
here, this fab code for the floating
action button, and let's go ahead now and
change those arguments. So what I'm going
to do above the definition for the
cursor, I'm going to create a projection
and a sort column: val projection equals,
it's going to be arrayOf parentheses
TasksContract.Columns.TASK_NAME.
that's our first field we want returned,
comma. Then our second one we want is
the sortOrder, so TasksContract dot
Columns.TASK_SORT
_ORDER. That's our projection,
and then for our sortColumn,
val sortColumn is equal to - we're just
going to specify the actual
column - TasksContract.Columns, because
it's a single one, dot TASK_
SORT_ORDER. Then we'll make a
change here to the code - instead of
passing null for the projection, we'll
actually pass the projection we've
just defined on line 19. And
then for the sortOrder, oops projection, and for
sortOrder we're going to use the
definition on line 20 for that. And what
we'll also do is make some changes here
to the output, because we're no longer
getting the ID. We haven't specified that
in our projection so I'm going to
comment that out. So name in fact will be
the first result returned, and we'll set
the column index to 0 for that.
Description, we're also not getting any
more - I'm going to comment that out - and
as a result sortOrder is now the second
column index, so that'll be number 1. Let's
also take the opportunity to clean up
the result. We're going to delete the ID
component first because that's no longer
being included, and likewise for
description, let's delete that as well,
leaving only name and sortOrder. So we'll
run this app again now. Let's see what we
get returned. This time you can see down
the bottom, it's correctly showing two
entries there; tasktimer - you can see
there, even though the log has made that
a little bit interesting to read, you can
see that we've got our three entries still
but now it's showing only the name and
the sort order, noting also that it's
actually sorted in the correct order. Null
is the first sortOrder 0, and then
2. And again, confirming that Kotlin is
appearing before Android in the results
that were returned. And if we go back to our
query method - let's go back to that
in our AppProvider - the other thing we
could do which we haven't really done,
just go up to the top and check that out.
Actually we have got that outputted there -
we've got query match is as well as query
called with. So let's just, I didn't
actually see that in the output, let's go
back and check that out again. And I think
that's not appearing because I've got
the slash main there so let's delete that,
and we can see it now a bit more clearly. That's
what I was looking for then, because I
had slash main I couldn't see it. That query
was called initially, as you can see
there, with content colon forward slash
forward slash learnprogramming dot
academy.tasktimer.provider slash
tasks, and it's showing it there that the
query match was 100, which is correct in
this case. And then obviously we're using
a code there, queryBuilder, to create a
sqlite queryBuilder instance.
Alright so that's working fine. So
going back to our code now in Main
Activity again. At the moment we're
returning all the records, but the
ContentResolver can also be used to
return specific rows from the database,
by passing a slightly different URI. And
again, the one we can see at the moment is
clearly being sent there, slash tasks,
which is all entries, or all rows I
should say, in the database. But by
changing that slightly, changing our
contentResolver.query line, we can
get a specific record. So let's go ahead
and do that. So if I change this first
line here - TasksContract.CONTENT
_URI - I'm going to change
that to TasksContract. This time
it's going to be dot buildUriFrom
Id and then parentheses, 2. We'll pass
out comma so the rest of the parameters
are accurate. So that's now going to
return just a single entry. Now you
wouldn't normally hard code the ID of a
row in your code, but we're only
testing that the provider works here. So
I've changed that first parameter, as you
saw there, and what I probably should have
done is commented out the original line
because we'll need to go back to that later,
but we'll do that at another time. We'll
just have to retype it in again. And as I
said you wouldn't normally hard code the
ID like this. We're just testing, though,
that we can select a single row.
So what we're going to do now is just
stop it, then we'll actually run it again,
and let's see what happens. So the log
cat should show the URI with the
ID appended to it now. And you can see here
that it is correctly showing that. You
can see it's learnprogramming dot
academy.tasktimer.provider
slash tasks. This time it's got the ID
that we specified, slash 2 on the end of it.
But that's interesting though, because if
we have a look at the actual output here,
we've got no records showing, and also
looking up on the line above that - query
rows in returned cursor equals 0. So
there's no record between the two rows
of asterisks, confirming that we
haven't actually got any data back. So
why is this happening? Well again, going
back to our AppProvider code, we're using
the appendWhereEscapeString function.
What I'm going to do is just change that
back. I'm going to delete the EscapeString, so
that we're going back and using just the
appendWhere function.
And let's stop and run it again and see what
happens, and you can see in that case, in
fact it has worked - same content
provider output as you can see there. We've
got our slash 2 again. This time we've
got records, or rows rather, in returned
cursor equals 1, and in between the
asterisks there we've got this
reading data Name Android N sort order
2, which is clearly the entry for the
row with ID 2. So are you interested at
this point in knowing why this is not
working? Well let's move on to the next
video where I'm going to show you what's
gone wrong, but more importantly, how to
fix it? So I'll see you in the next video.