Understanding Content Providers
In this chapter, we are going to take a look at how to provide content within your application. We’ll cover how to share that content, and how to access, add, and display the data that represents that content.
We have gotten significantly more advanced as we have progressed from chapter to chapter, and this chapter is no different. Data access is significantly more complex than event handling and UI design. This is because it involves database design, and thus you also have to know how the database is structured (how it was originally designed) to be able to access the data correctly. Database use in Android also involves requesting security permissions for database access.
In Android, requesting permissions is done via the Android Manifest file that bootstraps every Android Application Launch, kind of like index.html does with a website. In fact, starting with this chapter, we will need to modify the application’s AndroidManifest.xml file, so be warned that we are getting into some fairly complicated concepts and code here all the way around.
We’ll begin with an overview of exactly what Android content providers are, and what they do for your Android user. After that, you will learn how to use these SQLite-based native Android OS content providers to read and write database content for your Android applications.
An Overview of Android Content Providers
Content provider is a term that is unique to Android development that means nothing more than a datastore of data values, most often found in the form of a SQLite database that is already part of the Android operating system (OS). You can also create your own content providers for your application.
An Android content provider provides you with access to sharable data structures commonly called databases. The basic procedure is as follows:
In accessing data, you might simply read the data, write to the data (i.e., change the values of the existing data); append (add) new data onto the database structure; or delete existing data, based on the type and level of security permissions that have been established in the AndroidManifest.xml file.
Data can be in Android internal (system) memory, in a SQLite database, or in external memory such as an SD card, or even on an external server that is remote to the Android device itself.
Databases and Database Management Systems
The usual way for content providers to provide data structures for Android applications is via a database management system (DBMS). A DBMS manages a database by providing ways for users to create databases, as well as to populate them with data via reading and writing operations.
There is a complete open source DBMS right inside the Android OS called SQLite. SQL is a relational DBMS (RDBMS). An RDBMS is based on relationships that can be drawn between data arranged in tables. Later in this chapter, you will see how to write data into these tables in the SQLite RDBMS. If you wish to research SQLite a bit more on your own, which would be a good idea if your application needs to use databases; it has its very own website located at: www.SQLite.org.
The SQL in SQLite stands for structured query language. The “Lite” or “Light” part delineates that this is a “lightweight” version of the DBMS, intended for embedded use in consumer electronics devices, and not a full blown version of SQL, as would be used on an advanced computer system such as a database server.
Later in this chapter, we will take a look briefly at how Android allows you to access the Android Contacts database data records and the data contained within their individual data fields. All you really need to know about SQLite is that it is a part of Android and that you can use it for data storage. Android takes care of the DBMS functions for you via classes that are already written for you and that do most of the work. All you have to do is learn how to use them properly, which is not a simple task, due to the database complexity (read: power) that has evolved in Android API Level 16 (also known as Android “Jelly Bean” version 4.1).
In a DBMS, the highest level of data storage is the database itself, which contains tables of data in rows and columns. Each table is two-dimensional, where a row is called a record. Within each record are fields, organized into columns, which contain the individual data items that make up the records. Fields can contain different data types, such as numbers, text, or even references to data that is stored somewhere else. However, each field must contain the same data type as the other fields in that same column (see Figure 10-1 ).
Figure 10-1 . A MySQL RDBMS database
Note that there can be more than one table in a database (and there usually is, for both performance and for organizational reasons). As long as there is a key (a unique index) for each record in each table, information for a single data entry can span more than one table using that key, called an ID and designated via the constant “_ID” in Android SQLite databases. For instance, if your key or _ID value is 217, your personal information and phone information can be in two different tables stored under that same key value.
Caution After the record structure, and data fields that define this record structure, are set-up, don’t change the record structure later, if you are designing your own databases. This is because the currently loaded records and fields may not fit into the new database structure definition correctly. So, it’s best to ascertain and design what your database record structure will be up-front, making the DBMS design process especially critical to the success of any given project going out over time.
The content provider classes that are provided with the Android OS all use SQLite because it is compact and open source, and a part of the Android OS, so we are going to focus on those in this chapter. There is also an Android library of SQLite classes that can access SQLite directly, for more advanced use. If that is of interest, there are several books regarding using SQLite libraries inside of Android that focus only on this topic.
Android Built-In Content Providers
A significant number of SQLite-based database structures are “hard-coded” into the Android OS to handle things that users expect from their phones, iTVs, e-readers, and tablets, such as contact directories, address books, calendars, camera picture storage, digital video storage, music libraries, playlists, and so forth. The most extensive of these SQLite database structures is the Contacts database, which contains many different tables (subdatabases) for contact names, phone numbers, e-mails, preferences, social media settings, and so forth. This structure is very complex, and because this book is focused on programming for Absolute Beginners, and not database theory, we will only be working with the primary contact name database, to keep it more about Java programming and Android Content Providers, rather than about database structure and theory.
The base-level interfaces of the android.provider package allow us to access those data structures that define the setup and personalization of each user’s Android device hardware. Obviously, the data within each of these data structures will be completely different for each user’s smartphone, tablet, e-reader, or iTV set.
Contacts Database Contact Providers
Table 10-1 lists the Contacts database interfaces for Android 1.5, 1.6, and 2.0 that can be found on the Android Developer site (http://developer.android.com/reference/android/provider/package-summary.html).
Table 10-1. The Contacts Interfaces for Android 1.x and 2.0 Supports
If you browse the current Android Developer documentation, you’ll see that the interfaces listed in Table 10-1 are all described as “deprecated.” Deprecated means that these classes, methods, or even database structures have been replaced by other classes, methods, or database structures in a newer version of the programming language (such as Java) or API (such as Android). The newer classes, methods, or database structures that replace the older ones are usually more robust or feature filled, or sometimes they differ only in how they are implemented, or in the case of a database, which fields of data they contain, and how those fields of data are organized.
This deprecation is exactly what has happened with the Contacts interfaces between Android versions 1.x and 2.0 (1.0, 1.1, 1.5, 1.6, and 2.0) and Android versions 2.1, 3.x, and 4.x (2.1, 2.2, 2.3, 3.0, 3.1, 3.2, 4.0, and 4.1). So, the database interfaces that work with Android 1.x and 2.0 phones are different than the ones that work on the Android 2.1 through 4.1 phones (more advanced or feature-rich database structures, in this particular case).
If you are going to support 1.5, 1.6, and 2.0 phones, you will need to use the database interfaces listed in Table 10-1 . In this book, however, we have been taking the Android suggested application support default settings of API Level 8 (Android 2.2) through API Level 16 (Android 4.1) so we need to use the more advanced database structures that replaced the original database structures starting in Android API Level 7 (Android 2.1).
The good news is that deprecated does not mean disabled. It more accurately means in this case, “not suggested for general use unless you need to support pre-2.1 OS versions for your Android users.” So, if you need to support Android 1.5, 1.6, and 2.0 phones, you can use the interfaces listed in Table 10-1 , and they will still work on 2.1 (and 3.x and 4.x) smartphones. Note that inside of Eclipse, deprecated structures and program calls are “lined out” in the code, to show that they are deprecated, and that can be a bit unnerving, so because most devices these days are 2.3.7 through 4.1 compatible, we suggest that you take Android’s “advice” and develop for API Levels 8 through 16.
You may not be able to access data from the new database fields or tables unless you add support for the new 2.1, 3.x, and 4.x DBMS structures in your code by detecting what OS the user is using, and have code sections that deal with each (1.x and 2.x vs. 3.x and 4.x) database access structure differently (using vastly different code).
Note If you want to be able to access every new feature, you can have your code detect which version of the OS the phone is using and have custom code in place that delivers the optimal functionality for each OS version, if you wish.
In the case of Android, deprecation (a common problem that developers need to get used to, hence we are covering it here, so that as an Absolute Beginner, you can learn all about it now, and not be blind-sided by this concept later) equates to different versions of the Android OS being able to do different things, and thus having different sets of functionality that can be used for each operating system level or version. With Android, this is especially prevalent, as different OS versions will feature support for different hardware features for newer phones, iTVs, e-readers, and tablets, requiring new APIs, and changing existing APIs to support the newest hardware features.
For instance, Android was initially created for smartphones. Then tablets (and e-readers) came along, and Android 3.x OS features added support for that type of consumer electronics (CE) device. Now iTVs are becoming popular, and GoogleTV (Android 4.x and Android 5.x) is adding even more features and APIs.
Note Over time, versional functionality gets more and more difficult to keep track of. Indeed, we already have sixteen (if you don’t count Android 5.0 currently in beta as of the writing of this book) different OS versions (levels) that our code must work across. Keeping track of all the current programming constructs, database structures, and logic mazes is enough of a challenge for most, without another layer on top that involves remembering which constructs and interfaces work or do not work with any given OS API level version. This is one of many reasons why programmers are so very well paid.
Table 10-2 lists some of the newer versions 2.1 through 4.1 compatible content providers for manipulating contact information. The vastly different content provider database structure differences solidified in API Level 7 and beyond may well be the primary reason that the default in the Android Application Project dialog suggests (defaults to) API Level 8 through 16 support.
All of these replace the deprecated versions that are listed in Table 10-1 and are available from the same Android developer site link: (http://developer.android.com/reference/android/provider/package-summary.html).
Table 10-2. Android 2.1 through 4.1 Content Providers
Android MediaStore Content Providers
The other collections of content providers that you may find important for new media content within the Android OS are the MediaStore content providers. These are listed in Table 10-3 .
Table 10-3. Android MediaStore Content Providers
In the rest of this chapter, we will look at how to declare content providers for use, access them, read them, and write to them. Because we are using the default OS support suggested in the New Project Android Application Project of API Level 8 (OS 2.2) through API Level 16 (OS 4.1) we will use the more modern (i.e., not deprecated) content providers in our code examples.
Defining a Content Provider
Before a content provider can be used, it must be registered for use by your Android application. This is done by using some XML markup in the AndroidManifest.xml file. The <provider> tag so aptly named, allows us to define which content providers we are going to access once our application is launched. Here’s an example <provider> tag for the Images content provider: <provider android:name = "MediaStore.Images.ImageColumns" />
All Android content providers expose to developers a publicly accessible unique reference, or address, if you will, to each database. This address is called a URI, and the Android constant that points to the data location within the database table is always called CONTENT_URI. Providers built-in to the OS do not need a <provider> declaration (such as the examples we are using in this chapter).
A content provider that provides access to multiple tables will expose a unique URI for each table. Here are a couple examples of predetermined Android URI constants:
android.provider.ContactsContract.PhoneLookupColumns.CONTENT_URI
android.provider.ContactsContract.StreamItemPhotosColumns.CONTENT_URI
The first reads “android (the OS) dot provider (the component type) dot ContactsContract (the database) dot PhoneLookupColumns (the table) dot CONTENT_URI (the constant that points to the data location).” Yes, there is a logical method to the madness here.
Note URI objects are used for much more than just Android content providers, as you have seen in Chapter 8. All of the ones that are used to access Android content providers start with content://, just like a web address starts with http://.
Creating the Content Providers Example Project in Eclipse
Let’s set up our ContentProviders project folder in Eclipse right now, so you can learn a little more about the Android Manifest Editor and how Eclipse can automate the Android Manifest XML coding process for us.
If you still have the EventHandling project folder open from the previous chapter, right-click that folder and select: Close Project.
Figure 10-2 . Creating the ContentProviders Android 4.1 project in Eclipse
The AndroidManifest.xml file is usually referred to as the manifest for your application, and it tells the Android OS what you intend to do with your application. It is accessed during the initial launch of your application to set up the memory for the application, and to boot up any system resources or pointers (addresses to things that we are going to talk with, or connect to, that need to be ready in system memory) that are needed for the application to be run successfully.
In this case, that means we will be asking Android for permission to access, and possibly even change (depending on the tags we add), one of the Android databases outlined within the previous tables. The reason that we need to get permissions to use certain areas of the OS is so that Android can implement a robust level of data security within its OS infrastructure.
To define permissions, use the <uses-permission> tag:
<uses-permission android:name="android.permission.READ_CONTACTS" />
This tag allows the application to READ the CONTACTS database. Read-only operations are inherently safe, as we are only looking into these databases, and reading data from them. A read operation is thus termed to be a “nondestructive operation” to, or on, a database.
If we wish to change (overwrite or update, delete, or append to) data in a database, we need to use a different permission tag that tells Android that we are going to write data to an Android OS database. In this case, WRITE_CONTACTS represents the permission to change the Contacts database that we will be using. As you may have guessed, the WRITE version of the tag looks like this:
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
Permission for write operations is a bit more serious matter, due to the fact that we are now able to screw up the database, which is why the industry terms a database Write Operation to be “destructive.” In this case, we are dealing with the Android device user’s contacts data, and we might overwrite data that was there before our app ever accessed it.
Tip There are different permission tags that control different levels of access to services or databases that are part of Android. To see a list of all of them, and to get an idea of what Android will let you access with regard to smartphone; tablet; e-reader; or iTV hardware, features, and databases, check out this link: developer.android.com/reference/android/Manifest.permission.html. You will be amazed and empowered and learn a heck of a lot about what features Android currently offers as well.
Now let’s see how easy it is to use Eclipse to add the necessary permissions. Follow these steps:
Figure 10-3 . Adding a permission in the ContentProviders manifest using the Eclipse visual editor
Figure 10-4 . Selecting the READ_CONTACTS Permission entry from the drop-down menu
Figure 10-5 . Selecting the WRITE_CONTACTS permission and UP Button to display
That’s all there is to adding our read and write permissions. Figure 10-6 shows our AndroidManifest.xml file XML with the two permission tags at the bottom, before the closing tag:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
Figure 10-6 . The XML output for the permission additions we made in the visual Permissions Editor
Tip Anytime you are working with the Eclipse Visual Manifest Editors, you can click the AndroidManifest.xml tab at the bottom of the window and see what this helper is doing as far as writing the actual XML markup code for your project’s AndroidManifest.xml.
Now that we have permissions to read and write to the Contacts database, we can get started working with databases.
Adding Data to the Contacts Database
Android SQLite uses a table-based database model, where rows represent each data record and the columns represent the data fields, each with a predefined constant type used in Android to access the data. In this way, each piece of data in each column is the same exact type or classification, and each row is a unique collection of data of these types spanning across the row.
In this example, we are going to work with the ContactsContract.Contacts table. After we add some sample data to this table, it will look like Table 10-4 .
Table 10-4. ContactsContract.Contacts Database Table (simplified) with Sample Data
The column headers are the names that are used by Android to reference the data held in each column. These are what you use in your Java code to access each field of data within each record. For example, in some Java code we will write, we will refer to ContactsContract.Contacts.DISPLAY_NAME_PRIMARY.
The column names that are prefaced by an underscore character (_ID and _COUNT) are data fields assigned and controlled by Android; that is, you cannot WRITE to these values, you can only READ from them.
Now let’s add the four data records shown in Table 10-4 into our Android emulator. (If you like, you can add more than four records.) We’ll do this using the utilities that come with the Android OS. Follow these steps:
Note Another way to start the emulator is to select Window AVD Manager as shown in Figure 10-7 . Select your 4.1 emulator and press the Start button and then the Launch button. Any contacts you enter should be saved for later, even if you close the 4.1 emulator, unless you specify otherwise within the Launch dialog settings.
Figure 10-7 . The AVD Manager Start... and Launch buttons used to start the Android 4.1 Emulator
Figure 10-8 . Adding new contacts to the Android Contacts database via the Contacts utility
Figure 10-9 . Adding a record to the Contact database
Let’s get started writing our application that will access the Contacts database and Query, Add, and Display our Contact Name data. We’ll do some data queries against the Contact name, query (read) the name data, add (write) some new name data, and display that name data.
Querying a Content Provider: Accessing the Content
First let’s add a button to our example app’s activity_main.xml file that will trigger our database query via an onClick event (as discussed in Chapter 9 regarding events processing).
<TextView
android:id="@ + id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="@string/display_data"
tools:context=".MainActivity" />
<Button
android:id="@ + id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@ + id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="@string/button_title" />
Figure 10-10 . Adding a Button to the RelativeLayout in our activity_main.xml file
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;
Button queryButton = (Button)findViewById(R.id.button1);
queryButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
queryContact ();
}
});
Figure 10-11 . Adding a query button and setting an OnClickListener() method in MainActivity.java
Note that you can have the Eclipse IDE write most of this code for you, including all of the import statements and the onClick() container simply by mouse-overing red-lined elements in the IDE and selecting the import … and add unimplemented … options.
Tip As you’ve seen, when a method does not yet exist, Eclipse puts a red X in the left margin of the code-editing pane, as well as a red underline under the method name. If you want to remove those error indicators immediately, simply hover your cursor (mouse) over the red underline for a second, and select the Create Method option when the list of possible solutions pops up underneath it. Hovering your mouse over underlined (highlighted) code (either red for error or yellow for warning) in this way is a great technique for learning more about Java and Eclipse. Don’t be afraid to explore and experiment with the Eclipse IDE as you work through this book.
import android.provider.ContactsContract;
import android.database.Cursor;
import android.widget.Toast;
Figure 10-12 . Java method for our queryContact() method and the database related import statements
private void queryContact() {
Cursor nameCursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
while (nameCursor.moveToNext()) {
String contactName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY);
Toast.makeText(this, myname, Toast.LENGTH_SHORT).show();
}
nameCursor.close();
}
Let’s decipher exactly what is going on in this queryContact() method that we have written. Our method is declared private (meaning it operates completely inside of the class that contains it) and void, as it returns no values. The first line creates a Cursor object called nameCursor and assigns it the results of the call to the getContentResolver() .query() method. This method uses the ContactsContract.Contacts.CONTENT_URI Uri constant and four nulls that represent more complex (and unused) SQLite operation options that are beyond the scope of an Absolute Beginners book.
Cursor nameCursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
The Cursor object that is now properly populated with our getContentResolver() .query() results (the ContactsContract.Contacts database that contains the Primary Display Name records that we added in Figure 10-9 ) will be used in our iterative code, a while loop that will traverse the records of our Contacts database table that getContentresolver().query() was used to access.
The while part of the statement is true whenever the nameCursor object is able to move to the next record, that is, whenever the next record is not an EOF (End Of File) marker. As long as this is true, and as long as there are more records to read, the contents of the while statement are executed.
while (nameCursor.moveToNext()) {
As long as there is another record that can be moved to next this while statement will equate to true. When it cannot move to the next record (at the end of, or after, the last record in the results), it will equate to false, and will drop out of the loop, which will cease to function, just as we intended. In other words, as long as there is another record to process, we’ll do another run of the code in the loop.
Now let’s look at the two things done in the while loop while there are records to read.
First, we declare and set a String variable called contactName to the value of a fairly complex chained Java statement that gets the indexed data from the column of the ContactsContract.Contacts database record field (column) called DISPLAY_NAME_PRIMARY using the .getColumnIndex() function. That value is then turned into a String variable (made text compatible) value by passing the result of the nameCursor.getColumnIndex() function into the nameCursor.getString() function which then places that value into the contactName String object.
The data that is found in each current record of the results (on the first loop entry, this is the first record; on the second loop entry, this is the second record; and so on) is thus stored in the contactName string variable declared on the left side of the equals (=) sign as follows.
String contactName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
We do this via two key (no pun intended) methods of the nameCursor Cursor object:
Once our contactName string variable is loaded with the data value from the database record, we call our familiar Toast widget, and we display each record on the screen as it is read from the database during the execution of the while loop.
Toast.makeText(this, contactName, Toast.LENGTH_SHORT).show();
Note that this could also be written in two lines of code (if we were not chaining methods):
Toast.makeText(this, contactName, Toast.LENGTH_SHORT);
Toast.show();
Note that outside of the while loop once we are all done cycling through the database records, and drop out of the while loop at EOF, that we “close” our Cursor object with the contactName.close() method call. We don’t want to leave open database access objects in memory, especially as they usually contain many records to be looked through, so this is what is termed “clean” (memory efficient) coding, as we clean out (up) memory space when we are finished utilizing it, freeing it up for a different use!
Now, right-click on your ContentProviders folder, and choose Run As Android Project. Try out the Click to Query Contacts Database button to see it trigger our query method, displaying the Primary Name data that we added earlier in the Chapter. Figure 10-13 shows the app in action.
Figure 10-13 . Running a Contacts Database Name query in the Android 4.1 emulator
Appending to a Content Provider: Adding New Content
Now you’ll see how to add new content to a content provider database. In database terminology adding data records to the end of a database is called “appending” data records, but more important, now we’re going to the next level (albeit a destructive one) and “writing” a new database record into the database. You’ve come a long way, Baby. Next, we will add a new contact name to the ContactsContract.Contacts database.
<Button
android:id="@ + id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutBelow="@ + id/button1"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="@string/append_data"/>
Figure 10-14 . Adding in our second button tag in activity_main.xml to use to add a new contact
Button addButton = (Button)findViewById(R.id.button2);
addButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
addContact("Steve Wozniak");
}
});
Note This line of code calls our addContact() method and passes it a new database record Primary Name data value, so that a new contact entry will be added to the ContactsContract.Contacts database.
Figure 10-15 . Adding the Java code to add in the second append data button functionality
private void addContact(String newName) {
ContentValues myContact = new ContentValues();
myContact.put(RawContacts.ACCOUNT_NAME, newName);
myContact.put(RawContacts.ACCOUNT_TYPE, newName);
Uri addUri = getContentResolver().insert(RawContacts.CONTENT_URI, myContact);
long rawContactId = contentUris.parseId(addUri);
myContact.clear();
myContact.put(Data.RAW_CONTACT_ID, rawContactId);
myContact.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
myContact.put(StructuredName.DISPLAY_NAME, newName);
getContentResolver().insert(Data.CONTENT_URI, myContact);
Toast.makeText(this, "New Contact: " + newName,
Toast.LENGTH_SHORT);
}
We make sure that the addContact() private method is declared with the correct parameters, as follows:
private void addContact(String newName) {
This is quite different from our queryContact() method, as we are passing the method a string parameter: a name (in this case, Steve Wozniak). Because the addContact() method does not return any values, it is still a void method and is declared as such, just like the others.
Now we are ready to write the code that will add a new name to the Contacts database. The first thing that we need to do is to create a ContentValues object called myContact that defines the table, column, and data values that needs to be passed into the content provider. Because this is a new class that we are using in our code, we also need to add an import statement to our list of import statements (see Figure 10-16 ) or have Eclipse do that for us once we type in the new ContentValues() line of code that establishes a new ContentValues() object named myContact.
import android.content.ContentValues;
Figure 10-16 . Writing the Java code for our AddContact() method
After we add the import statement, we can instantiate a new ContentValues object called myContact via the following declaration:
ContentValues myContact = new ContentValues();
Immediately after that, we need to configure that ContentValues object with a data pair via the put() method. This loads the ContentValues object with the table (RawContacts), the columns (or fields of data to operate on) ACCOUNT_NAME and ACCOUNT_TYPE, and the string variable with the name in it, newName.
myContact.put(RawContacts.ACCOUNT_NAME, newName);
myContact.put(RawContacts.ACCOUNT_TYPE, newName);
Next, we use the getContentResolver() method to insert the myContact ContentValues object into the RawContacts table, which is at the location specified by CONTENT_URI constant we discussed earlier in the chapter:
Uri addUri = getContentResolver().insert(RawContacts.CONTENT_URI, myContact);
This writes (inserts) the newName variable that we loaded (via myContact.put) into our myContact ContentValues object into the RawContacts.ACCOUNT_NAME database column that we specified in the same object. So, now our newName variable passed to our method has been taken care of. After this line of code, addUri will hold the location of the newly inserted record. Notice that we declared the addUri object as a Uri type of object and called getContentResolver.insert() all within a single line of compact code. Good job!
The next line of code takes our Uri object named addUri that appends the RawContacts.CONTENT_URI onto the addUri and creates a new, more detailed URI object for the next query. (We are basically setting the location of where to add the new name by using the location of the new name record as a reference.) Now all we need to do is parse the data in the myContact ContentValues object via the addUri for the final data-insertion operation. This is done by calling the parseId() method off of the contentUris Class and putting the value into the long variable rawContactId that we will use in our next line of code.
long rawContactId = contentUris.parseId(addUri);
The next thing we want to do to the myContact object is to clear it, or basically turn it into an empty object with a clean slate. Then, in the next three lines, we use the put() method to load the myContact ContentValues object with the parsed ContentUris URI ID and table and column values for the DISPLAY_NAME field that we wish to write, and the newName primary name string variable data (“Steve Wozniak”), using the following lines of code:
myContact.clear();
myContact.put(Data.RAW_CONTACT_ID, rawContactId);
myContact.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
myContact.put(StructuredName.DISPLAY_NAME, newName);
Finally, we call our powerhouse getContentResolver() method to go into our content provider and insert the new name data into the correct table and data column (data field) location. This is done with the following code:
getContentResolver().insert(data.CONTENT_URI, myContact);
Once our data record is written by the two getContentResolver() operations, we can send a Toast to our users in the usual way, telling them that the write has been performed.
Toast.makeText(this, "New Contact: " + newName, Toast.LENGTH_SHORT);
Figure 10-16 shows the code as it appears in Eclipse with the import statements and our addContact() method highlighted.
Now right-click your ContentProviders project folder and select Run As Android Application. As you will see when you click the second button, the new name in our code is added to the Contacts database and a message confirming this is toasted (isn’t that a cool term? Hope you’re not toasted as well as trying to learn Java programming!) to the screen, as shown in Figure 10-17 . Now let’s go into our Android 4.1 emulator desktop and find the Contact Editor Utility so we can see the name data that we added.
Figure 10-17 . Adding a contact name in the Android 4.1 emulator and seeing it added in the Contacts Editor
To see the new data for Steve Wozniak, select the Contacts icon, hit the Menu button at the bottom of the screen (on the phone), and choose the Search function. Then scroll down the list until you see the Steve Wozniak entry (seen on the right screen in Figure 10-17 ). Even better, let’s click the Top Query Button, once you’ve added Steve Wozniak, and see his name pop up in the end of the query code (that is Toasted to the screen) that you wrote earlier in this chapter! Pretty Cool.
Summary
This is probably one of the most complicated chapters in this book because it combines the following:
You should feel a great sense of accomplishment from getting through all of this unscathed. You are learning how Android deals with advanced database concepts and structures. If you are really interested in database programming I suggest you get a book specifically on DBMS programming for Android, or at least go through some of the steps outlined in Chapter 12 regarding researching more complex SQLite database access coding, specifically using the SQLite library of classes included in the Android OS.
Most of the content providers that you will be working with in Android are already a part of the OS. They provide access to the common functions that users want in their phones, including contacts, music (audio), entertainment (video), and so on. The built-in content providers were listed in this chapter.
We also covered the concept of deprecation because after Android 2.1, the internal content provider database structures were enhanced, making pre-2.1 OS tables deprecated, although still usable, although it’s not recommended that you do. Beware of code samples on the Internet using the older deprecated database table structures, as they will not work for anything other than Android 1.5, 1.6, and 2.0 support.
The primary Java classes in Android that handle content providers are (surprise!) the ContentProvider class, the ContentResolver class, and the ContentValues class. Each plays a critical role in defining (ContentProvider), accessing (ContentResolver), and addressing (ContentValues) a SQLite database structure. If you want to do really heavy SQL database programming in Android learn to use the SQLite database class libraries in Android, which are simply beyond the scope of this book, and probably have a book or two out there of their own!
Although there are other ways to pull in data to your Android application, such as off your SD card or off a remote server, the SQLite DBMS is the most robust approach and the only one that can be accessed between applications. Furthermore, this is the most useful content provider type to learn, because all of Android’s user data is stored and accessed via these SQLite databases. Unfortunately, it’s also the most difficult way to implement content providers (database access) in the Android OS.
35.171.45.182