Chapter     10

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:

  1. Get permission to read the database, and possibly to write to the database.
  2. Query (find) the data.
  3. Access (read) the data.
  4. Modify (add, change (overwrite), append to, or delete) the data.

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 ).

9781430247883_Fig10-01.jpg

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

Interface Contents
Contacts.OrganizationColumns Organization
Contacts.GroupsColumns Groups
Contacts.PeopleColumns People
Contacts.PhonesColumns Phone numbers
Contacts.PhotosColumns Contact photographs
Contacts.PresenceColumns IM presences
Contacts.SettingsColumns Phone settings
Contacts.ContactMethodsColumns Contact methods
Contacts.ExtensionsColumns Phone extensions

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

Interface Contents
ContactsContract.CommonDataKinds.CommonColumns For subclassing databases
ContactsContract.ContactsColumns Contact main information
ContactsContract.ContactOptionsColumns Contact options
ContactsContract.ContactStatusColumns Contact status
ContactsContract.PhoneLookupColumns Phone numbers
ContactsContract.GroupsColumns Group definitions
ContactsContract.PresenceColumns IM presences
ContactsContract.SettingsColumns Account settings
ContactsContract.StatusColumns IM visibility

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

Interface Contents
MediaStore.Audio.AlbumColumns Album information
MediaStore.Audio.ArtistColumns Artist information
MediaStore.Audio.AudioColumns Audio information
MediaStore.Audio.GenresColumns Audio genre information
MediaStore.Audio.PlaylistsColumns Audio playlist information
MediaStore.Files.FileColumns Fields for master table for all media files
MediaStore.Images.ImageColumns Digital images
MediaStore.Video.VideoColumns Digital video
MediaStore.MediaColumns Generic media store

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 image Project image 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.

  1. Then select File image New image Project and choose Android Application Project to open the New Android Application Project series of dialogs.
  2. Fill it out as follows (and shown in Figure 10-2 ).

    9781430247883_Fig10-02.jpg

    Figure 10-2 .  Creating the ContentProviders Android 4.1 project in Eclipse

    • Project name : Name the project ContentProviders.
    • Build target : Set this to Android 4.1.
    • Application name : Name the application ContentProviders.
    • Package name : Set this to sixth.example.contentproviders.
    • Create custom launcher icon : Check this box and accept the defaults in the follow-on dialogs, as we have been doing in the earlier chapters.
    • Minimum SDK version Enter 8, which matches the recommended minimum SDK version of 8, and thus supports Android 2.2 through 4.1 and thus the more modern (complex or detailed) database structures.

Defining Security Permissions

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:

  1. Right-click the AndroidManifest.xml file in the Project Explorer navigation pane under your ContentProviders folder (near the bottom), as shown in Figure 10-3 , and select Open, or just hit the F3 key on the keyboard once the file is selected.

    9781430247883_Fig10-03.jpg

    Figure 10-3 .  Adding a permission in the ContentProviders manifest using the Eclipse visual editor

  2. In the ContentProviders Manifest tab, click the Permissions tab at the bottom of the window (see Figure 10-3 ).
  3. Click the Add … button in the right pane (also shown at left in Figure 10-3 ).
  4. Select the Uses Permission entry at the bottom of the list, and then click OK (also shown on right in Figure 10-3 ; I get a lot of mileage out of my screenshots).
  5. You’ll now see the uses-permission tag in the Permissions pane (see Figure 10-4 just left of center). From the drop-down menu that lists permissions on the right, select android.permission.READ_CONTACTS (see Figure 10-4 on right side). Next click the Add … button to add another Uses Permission Tag, which once you click on it will add the READ_CONTACTS Android Permission to the left pane list of tags.

    9781430247883_Fig10-04.jpg

    Figure 10-4 .  Selecting the READ_CONTACTS Permission entry from the drop-down menu

  6. Selecting the Uses Permission type on the right should update the pane at the left, but currently it does not, so we go ahead and proceed to click the Add … button to add another Uses Permission option and then the WRITE_CONTACTS tag (as we did with the READ_CONTACTS just prior), which will force the visual manifest Permissions Editor to update the left pane with the proper uses-permission tag setting.
  7. Once you select the WRITE_CONTACTS option from the drop-down menu, then click on the Up button to add the android.permission.WRITE_CONTACTS option (see Figure 10-5 ) to the permissions list pane. Alternately you could click the Add button and then simply not add another permission if you like.

    9781430247883_Fig10-05.jpg

    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" />

9781430247883_Fig10-06.jpg

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

image

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:

  1. Run the 4.1 emulator as usual by choosing Run As image Android Application.

    Note  Another way to start the emulator is to select Window image 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.

    9781430247883_Fig10-07.jpg

    Figure 10-7 .  The AVD Manager Start... and Launch buttons used to start the Android 4.1 Emulator

  2. Press the Home button. You will see four icons on the bottom of the home screen (shown in Figure 10-8 on the left side) with icons signifying Dialer (Phone), Messaging, Contacts, and Browser (Globe) in that order. The middle icon opens the desktop icon holder view. The icon called Contacts on the left (second one in) is a front-end to our Contacts database, and will allow us to add in the records shown in Table 10-4 .

    9781430247883_Fig10-08.jpg

    Figure 10-8 .  Adding new contacts to the Android Contacts database via the Contacts utility

  3. Click on the Contacts icon to launch the Contacts database, which is initially empty. The screen tells us that we do not yet have any contacts. Click the “Create a new contact” button to add new contacts to the Contacts database (as shown in the middle of Figure 10-8 ). In the next screen select “Keep local” because we are doing this exercise in the IDE and emulator, and we do not want to influence your real Android account.
  4. On the next screen, enter the first contact name (Bill Gates) using the onscreen keypad, and use the “Next” button at the bottom right when you are done, to advance to the Phone Number entry field.
  5. Add a Phone Number just to practice adding contact info, and then click the “Done” button at the top right of the screen to see the record you created. Note that you can also use the Add New option to create multiple records at one time, if you like.
  6. On the Data Record Display Screen, click the back arrow at the top left of the screen (shown in the middle of Figure 10-9 ) to see all your contact records by alphabetical order.
  7. Select the Add New Record Icon (a + next to a new member head icon) to continue adding the other three records (as shown in the right side of Figure 10-9 ). Notice the Toast Notification telling us we are going to create a new entry! Even the Android OS uses Toast!

    9781430247883_Fig10-09.jpg

    Figure 10-9 .  Adding a record to the Contact database

  8. Repeat Steps 4 through 7 to add the three other names in Table 10-4 , and maybe a few of own if you wish. When you are done, close the 4.1 emulator, there is no need to save or exit, as records are written to the Database as you add them, as long as you left the “Wipe User Data” option unchecked in the Emulator launch dialog shown in Figure 10-7 (if you even used that more complex emulator launch methodology).

Working with a 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).

  1. Right-click the activity_main.xml file, which as we know is located under the /res/layout folder. Then open the Eclipse Graphical Layout Editor and drag the TextView (HelloWorld) up to the top so that you get an AlignParentTop and centerHorizontal parameter added for you.
  2. Next, add a button via drag-and-drop, as you have done in many of the previous examples. Make the button centered as well and aligned below the TextView with a 50dp marginTop parameter. Here is the code that shows the parameters for the Button and TextView that the GLE writes for us inside of our standard RelativeLayout tag (see Figure 10-10 ):
    <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" />

    9781430247883_Fig10-10.jpg

    Figure 10-10 .  Adding a Button to the RelativeLayout in our activity_main.xml file

  3. Next, right-click on the /src/sixth.example.contentproviders/MainActivity.java file and select Open.
  4. First, we will add in our Button object declaration and our onClick event handling code, as we did in the previous chapter. Later, we’ll write our custom query method, once our UI is in place. To get things going, let’s add the following three import statements (or allow Eclipse to do it for us later) that we need to define our Button:
    import android.widget.Button;
    import android.view.View;
    import android.view.View.OnClickListener;

    Note  Remember that you use the import statement to pull in the classes that you are going to leverage (utilize) within your code.

  5. Now declare our Button object, like so, with some fairly standard code:
    Button queryButton = (Button)findViewById(R.id.button1);
  6. Next, let’s use a setOnClickListener() method to add the ability to handle events for this button, using the lines of code shown below (and shown in Figure 10-11 ). First, we will attach the new OnClickListener to our queryButton, and then inside the onClick event handler, we will assign the queryContact() method (which we will code next), to be run when an onClick event is encountered. Note in Figure 10-11 that queryContact() is underlined in red in Eclipse, which tells us that the method we are calling does not (yet) exist.
    queryButton.setOnClickListener(new OnClickListener() {
            public void onClick(View arg0) {
                    queryContact ();
            }
    });
     

    9781430247883_Fig10-11.jpg

    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.

  7. Next, let’s add the three new import statements that we need (shown at the top in Figure 10-12 ). Remember that you can also just write in your Java code, and let Eclipse insert these import statements for you automatically, if you prefer. The first brings in the database structure that we will be accessing via the android.provider.ContactsContract Class. The second imports the all-important database Cursor class android.database.Cursor that allows us to traverse (access or look at) the data within all of the Android databases. Finally, our familiar android.widget.Toast class needs to be imported to allow us to easily display our data via the Toast message broadcasting widget:
    import android.provider.ContactsContract;
    import android.database.Cursor;

    import android.widget.Toast;

    9781430247883_Fig10-12.jpg

    Figure 10-12 .  Java method for our queryContact() method and the database related import statements

  8. Now we can write our queryContact() method, to query the database (also shown in Figure 10-12 ).
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:

  • The .getColumnIndex() method gets the internal reference or index number for the ContactsContract.Contacts.DISPLAY_NAME_PRIMARY column for the current record.
  • The .getString() method gets the string data from that location in the results and puts it into the contactName string variable that we declared on the left side of the equals sign.

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 image 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.

9781430247883_Fig10-13.jpg

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.

  1. First, copy and paste the first Button tag in our activity_main.xml file or use the Eclipse Graphical Layout Editor to drag a second button onto your current UI, and center it 20dp underneath the first button. The text label of the button should be set to @string/append_data (see Figure 10-14 ).
    <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"/>

    9781430247883_Fig10-14.jpg

    Figure 10-14 .  Adding in our second button tag in activity_main.xml to use to add a new contact

  2. Next let’s add in the <string> tag with a name value of append_data and its text value that specifies the text label for our second button that reads: “Click to add to Contacts Database."
  3. Next, let’s add in the code to implement the second button for our UI by copying the button1 Button object declaration and the onClick event handling code and pasting it immediately underneath the existing UI code in our MainActivity class to create our database Add button.
  4. Change the queryButton variable name to read: addButton, and change the R.id to point to our new button2 ID. Also, set our method call to the new addContact() method that we are going to write (see Figure 10-15 ). Here is the new code:
    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.

    9781430247883_Fig10-15.jpg

    Figure 10-15 .  Adding the Java code to add in the second append data button functionality

  5. Next, we are going to add the new method addContact().
        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;

9781430247883_Fig10-16.jpg

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 image 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.

9781430247883_Fig10-17.jpg

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:

  • Knowledge of SQLite database design, functionality, and access—in and of itself a topic that can easily span several books, and usually does
  • The Android concept of content providers and setting up their permissions tags within the AndroidManifest.xml file
  • The Java programming language iterative (looping) constructs that are necessary to access and manipulate these database structures
  • How to read and write database records for the Android contacts database

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
35.171.45.182