In the last video we found out more about what this ContentResolver is.
So back to our code now. We're going to use this applications ContentResolver.
As I said there's just a single instance of the ContentResolver, and it's responsible for resolving
all requests for content by directing the requests to the appropriate Content Provider. Now in our code down
here on line 25 where we call contentResolver.query, the ContentResolver extracts the Authority
from the URI and uses that to decide which Content Provider it should send the query request
to.
It then gets a cursor back from that Content Provider and returns the cursor to our calling code.
Alright so we're going to write a bit more code now and we're going to store the contacts in a list.
So let's start by doing that.
So below the line now where we've retrieved our cursor, we're going to add some more code. So what we're going to do
first is add a new line and then we're going to put val contacts is equal to ArrayList. And then we're going to
use the diamond operate with string, so that's the type string we're getting back,
or at least what's going to be in the ArrayList and parentheses on the end.
And just a note there.
That's basically going to be "create a list to hold our contacts".
Alright.
Then on the next line we do cursor question-mark dot use, left and right
curly braces. And we'll just put a note here,
"loop through
the cursor". OK, so within the block itself, this time we're going to use while parentheses. We're going to use it this time
correctly, so it.moveToNext closing parentheses.
Open a code block again, then we do a contacts, which is our ArrayList dot add parentheses.
it.getString. Then in parentheses again we're going to put it.getColumnIndex parentheses again,
ContactsContract.Contacts. Then it's going to be dot DISPLAY_NAME_PRIMARY,
then we've got three closing right parentheses at the end of the line.
Alright so we're storing the contacts in a list which I've called contacts, and we're adding each contact to the list
and we'll use the list shortly as the data for an adapter.
Now we can't guarantee that we'll get any data back.
So we're using the safe call operator before the use function
with a question-mark.
Now the call to use and the while loop use the same approach that we've used in our earlier example.
Using a cursor is the same, even though we're now getting the data back
via a ContentResolver and ContentProvider.
So the last thing we need to do now in this code to read the data, is to create an adapter for the
ListView.
So let's go ahead and do that.
I'm going to put that immediately below the line we've just finished typing.
So we're going to type val adapter is equal to, we're going to type ArrayAdapter, then a diamond operator, string
in the middle of that, so type string. Then parentheses
this comma R.layout.content_detail comma then R.id.name comma then contacts being our
ArrayList. Then next we're going to do contact
underscore names.adapter equals adapter. Now by the way if you are asked to select the import for contact
underscore names, choose a synthetic import, and that's Kotlin X dot android.synthetic.main.content underscore
main dot star.
And you can see in my case there, because I've got auto-imports on,
it's found that, well processed that automatically for us. Alright, so moving on. And by the way we've seen this code before
in the top 10 downloader app for the ArrayAdapter.
Alright so at this point the code's completed, almost.
What we still need to deal with though is permissions.
So Google changed the way permissions work with Android Marshmallow.
So I want to spend a bit of time discussing that. Now we should really finish off what we're doing with
these ContentResolvers and ContentProviders first, though.
So what I'm going to do is run this on an Android lollipop,
the API 21 emulator, so that we can see it working.
But then in the next video we're going to spend some time looking at permissions.
Whichever version of Android we use, we still need to request the permissions in the Android manifest
at least, for the app.
So let's go ahead and do that.
So we're going to open the project on the left, we're going to open up manifest.
So the permission we want here is READ_CONTACTS, so let's go ahead and do that.
So we're going to come down here above the application tag and we'll create, or we'll add rather, a user's
permission tag here.
You can see that Android Studio is helpfully helping, or filling that in for us. And the one we want,
as I mentioned, is READ_CONTACTS, so I'm just specifying that and pressing enter
so there's no chance of me accidentally mistyping that. So I'm going to close off the tag there, so
we've got the permission now set up for reading contacts defined in our Android Mainfest file.
So once we've done this now the app should work on any version of Android before API 23.
So let's run it on the API 21 device that we set up a few videos ago.
So this is the one here that I set up, API 21 Contacts, so let's run that.
Alright, so once the app is running, which it is now, we should be able to tap the floating action button
and see the contacts displayed in the ListView.
And just remember to choose the emulator that you've added the contacts to in the earlier video,
otherwise of course you won't be able, or you won't have any contact records to display.
So I'm going to click on the floating action button, and you can see we've got two records showing there. Now if we had enough
records we could scroll through them, as you've seen us do with a ListView in earlier apps.
In fact now we know that it's working,
you could run it on your phone and you'll see all your contacts appear in the list.
Alright but at this point that's working fine,
and that's how we can use data using a Content Provider.
Now obviously this is a very basic application, but it does give you the concepts of a Content Provider and
also using a Content Resolver.
In this case we were able to access the contacts built into the phone, and as you can see if we go back to the
code,
the actual code to query the data is very similar to what we had when we accessed the database directly.
So using a Content Provider doesn't really introduce any more complexity into our code, beyond having
to obtain the content_uri from the ContentProvider class, so that we can pass that to
the Content Resolver. We query the Content Resolver, and that passes the query onto the Content Provider.
The Content Provider then fetches the data from its data source or, in the case of an update, writes the
data to the data source and either returns a cursor to the Content Resolver or reports back how many
records were updated.
There's a direct mapping of methods from a Content Resolver to the Content Provider.
For example, when we call the Resolver's query method, that has to call a query method in the Provider,
and the same applies to Inserts and Updates. Every method we call in the resolver has a corresponding method
that the Resolver calls in the Provider, and we'll see that all in more detail when we come to create our own
Content Provider.
And in the next app we'll do just that, and we'll look at creating our own Content Provider to provide
access to our apps database.
Now this is more complex than just using the database directly, but it does mean that other apps will
also be able to access the data in the same way as we've just done with the phone's contacts data.
And it also frees us from having to cater for things like the activity life-cycle. We're not accessing
the database directly, and don't have to worry about opening and closing it. The Content Resolvers object's
global, and doesn't get destroyed when our activity does.
Of course we still have to program our activity to handle its life-cycle events.
If I rotate the emulator the ListView's cleared.
So going back to the emulator, so as you can see if I rotate the device
the ListView is cleared, and we have to click on the fab button to bring the data back again.
So that does result in the query getting executed again unsurprisingly, then we'll be seeing how a
loader can be used to prevent that though.
The point I want to make though about the life-cycle, is that we don't have to concern ourselves with coordinating
the data access with the activity life-cycle. The Content Resolver and Content Provider handle that all for
us.
If we query the database using a CursorLoader, we also don't have to worry about creating background
threads, and we'll be saying how to do that in the next section.
But coming up in the next video we're going to look at the changes that Google made to permissions in API
23, and how to get this app working with Android Marshmallow and above.
See you in the next video.