Contacts have long been the single most important feature on a mobile phone. After all, a phone exists to call people, and you don’t want to memorize the 10-digit phone number for every person you might call.
If you’ve owned mobile phones for a long time, you’ve probably noticed a gradual increase in the functionality of mobile contact applications. In the early days, people could only enter a name and a single phone number, similar to a speed-dial on a land- line phone. Modern phones allow you to enter multiple numbers for each contact, as well as email addresses, photos, birthdays, and more. Not only does this provide greater convenience to consumers, who now can turn to a single source for all related information about a person; it also opens up many new opportunities for application developers, who can take advantage of the wealth of information on a phone to make their apps more useful and relevant.
Android has always offered access to a user’s contacts, but its contact model has changed through different versions. The model is now extremely robust and flexible, offering great features for querying and updating contact information.
15.1.1 Choosing open-ended records
Imagine that you just got a new phone and started entering contacts. You entered three names and phone numbers. The final result would look something like figure 15.1. From a user’s perspective, you have all that you need. But how is this data being stored?
In older phones, the underlying contact model might result in something similar to figure 15.2. This fixed-length record would dedicate a predetermined num- ber of bytes to each contact. Such an approach had hard limits: for example, you might only be allowed 10 digits per phone number, 12 characters per name, and 500 records per device. This led to both undesirable truncation and wasted space.
In addition to the problem of truncation, you can see that this style of contact storage doesn’t allow the user to add new types of data. Figure 15.2 shows that there’s no space between Chris and Frank to insert a
Figure 15.1 Making entries in the native Contacts application
mailing address or job title. Fortunately, Android uses a database to store its contact data, so the storage looks more like figure 15.3. Each contact is a collection of related data, and each piece of data can be as short
or as long as it needs to be. Contacts can omit the data that they don’t require. These are called open-ended records: records can be freely extended to include previously unknown pieces of data.
You can see exactly how the data is stored by querying the contacts database.
See chapter 5 if you need a review of using the sqlite3 tool, with which we’ll query the tables that were created when adding the initial three contacts. In figure 15.4, we con- nect to the sqlite database stored at /data/
data/com.android.providers.contacts/data- bases/contacts2.db. Querying the data table returns the individual pieces of contact information on the device. The contacts table includes individual contact entities.
Joining these two tables results in the infor- mation visible in the Contacts application.
C H R I S K I N G 4 1 5 5 5 5 1 2 3 4
R O B I S E N 5 1 8 5 5 5 5 5 5 5
F R A N K A B E L S O 9 7 3 5 5 5 9 8 7 6 Figure 15.2 Fixed-length storage records
Contacts
<Chris> <Frank> <Robb>
Chris King 415-555-1234 Frank Abelson 973-555-9878 Robi Sen 518-555-5555
Figure 15.3 Open-ended storage records
Why bother with fixed-length records?
Given these limitations, you might wonder why anyone would’ve adopted fixed-length records in the first place. They do have the advantage of being fast. Because each record is the same length, the phone can immediately jump to the exact location in memory where, say, the forty-second record starts, without needing to search for it.
When phones had extremely lim- ited processors and slow flash memory, this design allowed for a much speedier experience. Now that phones have gotten faster, OS designers can afford to be more flexible.
Android’s contacts are highly extensible because they’re stored as records in a data- base. Third-party applications can choose to define and add their own enhanced data fields. For example, a business application may include employee ID numbers, and a gaming application may use online handles. Other phone platforms require develop- ers to maintain parallel data stores to add this kind of data, but Android allows you to insert directly into the main contacts database. This leads to much better consistency, and your enhanced information will automatically be updated when the user renames or deletes a contact.
15.1.2 Dealing with multiple accounts
Most Android phones require users to activate the device and associate it with a Google account. Upon activation, all of the user’s existing Gmail contacts will be avail- able on the phone. This is convenient for people who keep all their information on Gmail, but not terribly useful for people who have multiple accounts or don’t want to use Gmail at all.
The early versions of Android were hampered by this problem, but fortunately, the new Android contact model offers a richer set of features that includes support for multiple accounts. Users still must generally tie the phone to a Google account, but they may also connect with a Microsoft Exchange1 account for business contacts and email, with Facebook for friends’ statuses, and possibly other private accounts as well.
Using multiple accounts offers great convenience but also greater complications.
Consider the following issues:
Differing data—Each account will support disparate types of data. For example, Facebook may offer an Instant Messaging screen name, whereas Exchange may provide a contact’s supervisor.
1 For information on Microsoft Exchange Server development technologies, go to www.outlookcode.com/
archive0/d/exstech.htm.
Figure 15.4 Querying the contacts database
Different formats—Even if the same type of data is supported by multiple accounts, it may display differently. For example, one account may return a phone number as (415) 555-1234, whereas another returns it as 4155551234.
Consolidation—Having multiple accounts isn’t very convenient if you need to search through three versions of each contact to find the one with the informa- tion you need. Instead, you want to have a single contact that includes data from all your accounts.
Linkage—On a related note, the phone needs some way to determine when two different accounts are referring to the same entity. If one refers to “James Madison” and the other to “Jim Madison,” a human could guess that they refer to the same contact, but software requires some more information to determine how they link together.
Ownership—Some contacts may only appear on one account, whereas others may be listed in several. When you change a contact, you want to be sure they’re updated appropriately.
Android resolves these issues gracefully. Each account stores its data in separate rows in the contacts database. The raw_contacts table contains all the contacts that have been received from all of the user’s accounts, including potentially duplicate contacts.
Table 15.1 shows a hypothetical view of a device’s contacts.
NOTE As you’ve seen, the actual contacts data fields will reside in the data table. The raw_contacts table allows you to associate the various rows in the data table with their corresponding accounts.
Splitting out the accounts’ views like this gives Android a great deal of flexibility.
Whenever a piece of data is updated, it knows where that data originally came from. It can store data in the format most appropriate to the original account. And, because each account gets its own set of rows, no account can mess up the data that originally came from another account. This type of loose coupling extends a familiar Android philosophy to the world of contacts and provides the same sorts of benefits that you’ve seen elsewhere: more extensibility, stability, and transparency, at the cost of greater complication.
Table 15.1 Raw contacts including duplicates from multiple accounts
ID Name Email Phone number Status
1 James Madison jmadison@gmail.com
2 Adam Smith asmith@gmail.com
3 James Madison 2024561414 “Negotiating with the
British...”
4 Adam Smith adam.smith@example.com +442055512345
15.1.3 Unifying a local view from diverse remote stores
Storing raw contact data from each account makes architectural sense, but by itself it would lead to a horrible user experience. If you had five accounts, you might have five separate entries for the same person, which would lead to extra complications when- ever you wanted to call them. You want to have just one contact entry for each real contact you have, no matter how many accounts they’re associated with. Android pro- vides exactly this utility.
Figure 15.5 shows how Android consolidates the information received from multi- ple accounts. In this example, the user has two contacts
that are spread across three remote accounts. In the first phase, contacts are retrieved from each account.
Next, Android will associate the matching accounts and consolidate each into a single logical contact. The raw contact data remains on the device, but the user will see and interact with the consolidated contact.
This process works well but isn’t perfect. Android may require additional help to recognize that two con- tacts refer to the same person; sometimes it may mistak- enly combine two records when it should’ve kept them separate. Fortunately, because Android never removes the raw contact data, you can easily join and separate accounts at any time, as shown in figure 15.6. This updates the contacts table and either creates or deletes a row there, but will leave raw_contacts untouched.
Gmail Exchange Facebook
Adam Smith asmith@gm ail.com
James Madison jmadison@
gmail.com
Adam Smith adam smith@example.
com
+442055512345
James Madison (202) 456-1414 1600 Pennsylvania Avenue Married to Dolley Madison
“Negotiating with the British...”
Adam Smith asmith@gmail.com adam smith@example.com +442055512345
James Madison jmadison@gmail.com 202-456-1414
“Negotiating with the British...”
Figure 15.5 Combining raw contact data into unique contacts
Figure 15.6 Joining and separating contacts
CAUTION Android currently doesn’t offer a way to specify what name should be used after joining two records. You may get lucky, or you may be stuck with the wrong one.
15.1.4 Sharing the playground
With all the flexibility of Android’s new contact model comes potential for abuse. A single consolidated database holds all contacts information on the device, and any application can read from or write to it. Users must give apps permission before mod- ifying the database, but within the database, there’s no notion of particular columns or rows being “owned” by a particular app or service.
When you write apps that use contacts data, try to be a good citizen. This includes the following principles:
Only read and write Android’s built-in data fields and fields that you’ve created for your application.
Be sure to provide unique types for new contact data that you define, such as vnd.manning.cursor.item/birthday instead of birthday.
Always ask users for permission before doing anything destructive to their data.
Clearly state if and how your app uses their personal contact data.
If you store contact data taken from the user, be certain it’s well secured. You don’t want to be responsible for addresses falling into the hands of spammers or identity thieves.
Not only does upholding these standards garner good karma, but it also keeps your users happy and more likely to use your app.