In the previous video we created the layouts to display the contact records using a ListView.
Now it's time to write the code to retrieve the contact details.
Now before we get into the coding there's been a change in the way the private access modifier works
in Kotlin. When used on a top level object
it used to mean package private.
So that meant that any private constants we declared would be visible throughout the package.
Now that would obviously cause problems with our log tags, because most of the Kotlin files we've created,
or create, have a tag. But that's no longer the case,
and when private is used for a top level object, it means private within the file it's in.
But just to demonstrate what I mean, I'm going to create the usual tag string that we've been using so far
throughout the course. That of course is in the, within the class. We're going to do a private val TAG in
uppercase equals double quotes MainActivity. Now that results in a warning from Android Studio,
because the name tag doesn't conform to the naming conventions.
If I just come over here,
you can see we've actually got two warnings. The first one is the TAG isn't being used which we'll rectify shortly. But the
second one you can see there, it's not matching a particular file naming convention or the regular expression.
So the change in the way that Kotlin treats private for top level objects, means that we can move our
TAG outside the class and make it a const. So let's do that.
So we'll do private - we need to make it a const now.
And you can see now if you hover over the warning, we've only got the warning about the property TAG not being
used at this point in our application. So basically this is now exactly what we want.
TAG is now private to this file and it won't conflict with any private TAG constants in other files.
Alright so on with the code.
So we're going to put the code to access the contacts inside the onClick method of the floating action
button, so this code here. So I'm going to delete that code first.
I'm going to add some logging, so Log.d parentheses.
I'm having a hard time typing today, TAG comma double quotes. We'll just put fab onClick colon starts.
OK.
So there's our log. Now we're going to start by cheating. I'm going to replace some code now with a declaration of a string
array
after this logging method. So it's going to be val projection equals, going to be arrayOf parentheses.
And it's going to be ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
and closing right parentheses.
Now that's cheating because I already knew which column stores the contacts name, but how would I have found
this out if I didn't already know that. Now we've seen how to call up the documentation as I've outlined before.
So I'll go ahead and do that by selecting the contacts in ContactsContract.Contacts, so this part
here. Then open the quick documentation.
So that's useful, and if we scroll down and have a bit of a look, we can see in the columns we eventually get to
this DISPLAY_NAME_PRIMARY, the display name for the contact. But that only works if you
already have the object name on the screen to click on unfortunately.
Now if you want to find the documentation for something like accessing contacts data, your best friend
is probably the Google search engine.
So think of the key things about what you're trying to do,
then google those words. Let's go and open up a browser.
So in this case, probably would be appropriate to type something along the lines of android contacts content
provider,
because we're looking for the Content Provider for the Android contacts. Now
obviously you could phrase that in a number of different ways. Unless you manage to come up with
something really obscure, you should probably get some useful results.
You can see here in my case, it's returned some interesting looking links.
They all link, as you can see here, to developer.android.com, so they are the official source of
documentation from Google for Android development.
So I'm going to go to this first link up here, the one that links to Content Provider under topics. And it is
definitely worth getting into the habit of checking out the Google documentation. They provide reference
docs, which can be a bit dry and hard to read, as well as guides and training docs that are more tutorial
style in nature.
So this one's a guide document, and that'll show you more on how to access the contacts database via the Content
Provider.
Now by the way we're going to be looking at a lot of this stuff covered here in our next app -
things like retrieving a single row, for example - but do take advantage of the Google documentation to
learn more about how Android works in general.
Alright.
So I'm certainly not going to read all of this. Scrolling down a bit though, we can see that there's this diagram here showing
how the contact details are organized. Then in the description below you down here, you can see that we've got Contacts
Contract.Contacts, and it mentions here that it's a row or rows representing different people based on
aggregations of raw contact rows. So for our purpose they probably sound ideal,
so I'm going to click on that link now.
That brings up the ContactsContract dot
Contacts class here,
as you can see. There's obviously a lot of information on the class. But all we're really after here at the
moment are the column names we can use. So if we scroll down a little bit,
you see we do get a list of the various columns and there's our trusty DISPLAY_NAME_PRIMARY
field, column name. So it's not so much the names, or it's really, to be technically correct
it's the constants that we should use when referring to the columns. Now the column names in the database aren't
really of interest when using a Content Provider.
In fact when you use a Content Provider, you're not interested in anything about the database.
Now we know that Android uses sqlite to store the contact details,
but Google may decide to change that in the future. As long as we use the constants defined in this
class,
it won't matter if they use a completely different database with totally different names for the fields.
If they do that, they'll update this class,
and because of those static column names being the same, everything
will continue, or our code will continue to work.
So this fourth entry in the table is DISPLAY_NAME_PRIMARY and that sounds like
the field we want. Alright so back to our code now, and we now know where that DISPLAY_NAME_PRIMARY came from.
Now this is where it gets fun -
if dealing with multiple levels of abstraction is your idea of fun.
So what I'm going to do is type the code in and explain what's going on.
So after that val projection line we're going to do a val space cursor equals,
and it's going to bet be contentResolver.query. In parentheses it's going to be ContactsContract.Contacts
dot CONTENT_URI, this one down here.
And I'm going to do comma and move to the next line.
Then we're going to do projection comma, then we do null comma null comma.
Then we're going to end up with ContactsContract.Contacts dot, then we're going to enter the DISPLAY underscore,
or we'll select DISPLAY_NAME_PRIMARY. Then we'll just end this with another log entry -
Log.d parentheses TAG comma double quotes
fab onClick ends.
Alright, so we get a contentResolver, in case you're wondering how this was accessed. We get that from the activity
using the getContentResolver method, but because this is Kotlin, we can use the property directly instead
of going through the getter.
So that's how I've been able to use this contentResolver here.
So the contentResolver's query function returns a cursor containing the data we want.
So let's start now by looking at those parameters we passed to the query function.
Now we've got something we can query for data;
the contentResolver that returns a cursor.
The parameters we're passing to the query method are firstly the uri,
that's this first entry here. Now,
so this somehow identifies the data source that we want to get data from.
This could be a database and a table for example, but this code doesn't need to know where the data's
coming from. At the moment
we don't care about that either.
Now we'll find out soon enough, but for now we're just going to accept that this uri identifies the
source of our data.
Next we've got projection.
This is just a string array holding the names of the columns that we want to retrieve.
Now we're just asking for the DISPLAY_NAME_PRIMARY here, or DISPLAY
underscore NAME underscore PRIMARY here column here, but we could put more fields into the array if we want to.
Next we've got selection - that's a string containing a filter to determine which rows are returned - and
think of this as the WHERE clause in a SQL statement.
In fact that's exactly what it is without the SQL keyword where, and in this scenario because we're passing
null,
we get all rows returned. selectionArgs is the next one and
this is an array of values that will be used to replace placeholders in the selection string.
So there are a number of advantages to using the selectionArg parameter to specify the values we want, and
we will look at those.
But basically your selection might be name equals question-mark and the selectionArgs could contain the
single value bob.
The query would then use the filter, the WHERE clause
if you like, name equals Bob. Because we're not doing any filtering again, we're just parsing null
for this as well.
And the last entry is the sort order,
and this is just a string containing the names of the fields you want the data sorted by.
So it's just a sequel
ORDER BY clause, without the actual keywords sequel keywords order by. contentResolver will then execute our
query against its data source and give us back a cursor.
We can then use the cursor just like we did earlier.
We'll just use a loop to loop through all the rows in the cursor and display the contact names. So that
bit's easy.
But what's the contentResolver all about.
Well I said that there were several levels of redirection here, and we'll look at that in the next video.