Hour 17. Contacts and Calendar: Accessing Device Data


What You’ll Learn in This Hour

Image Understanding calendar data

Image Using the calendar content provider

Image Using a calendar intent

Image Understanding contacts

Image Using the contact content provider

Image Using a contact intent


Android ships with a set of core applications that include calendar and contact applications. When these applications have publicly documented APIs, they are considered part of the Android Application Framework. Apps that are part of the Android Application Framework include test cases that appear in the Compatibility Test Suite that Google makes available to Android hardware partners. The idea is that these APIs will work the same way across devices. Unlike private undocumented APIs, they are much less likely to change or be removed in the future. A contacts API has existed in Android since API level 1. The calendar public API became available with Ice Cream Sandwich (API Level 14). In this hour, you learn to use the calendar and contact APIs to retrieve calendar events and contacts. This hour also shows how to make simple applications to display the data and how to use intents to insert and update this data.

All About the Calendar

Most people know that a calendar divides time into specific periods of days, weeks, months, and years. People have personal calendars to schedule events in their lives. A calendar app, like Apple’s iCal and Google Calendar, enables calendar owners to add and track events on their calendar. An event might be a meeting with an agenda and attendees or it might be a personal event such as “soccer game tonight.” In either case, events on the calendar include a time period and a description. Events may include a list of attendees, and many calendar apps use reminders, warnings that a meeting is about to start in 5 minutes, for example.

Calendar apps can include a recurring event, which occurs at specific intervals and is scheduled into the future. A recurring event might be a weekly status meeting at work that occurs 2 p.m. each Monday or a drama class that occurs each Saturday at 10 a.m.

The calendar app in the Android Framework includes a database that provides for all of this information. That data is made available through the calendar content provider.

Calendar Data on Android

Understanding the structure of calendar data for Android is important to being able to query the data and successfully use the calendar content provider.

More than one calendar might be available. You might have a personal calendar and have access to view another person’s calendar. In that case, two calendars are available for viewing when using the calendar content provider.

The calendar API is basically a data model, with tables, columns, and relations specified so you can use them through content provider queries. You implement the calendar content provider in the android.provider.CalendarContract class. The class refers to five tables containing calendar data comprised of CalendarContract.Calendar, CalendarContract.Events, CalendarContacts.Attendees, CalendarContacts.Instances, and CalendarContacts.Reminders.

The following list describes the tables in CalendarContract (android.provider.CalendarContract):

Image Calendar: Details for a single calendar including the account type, account name, calendar id, and calendar display name.

Image Events: Event-specific information including the event title, description, start date, and end date.

Image Attendees: Event guests and whether they have responded to indicate that they are attending the event.

Image Instances: Each row in the instance table represents one occurrence of an event. For recurring events, multiple entries are in this table. For non-recurring events, there is one entry.

Image Reminders: Reminder information for an event. An event might have multiple reminders.

Figure 17.1 shows the relationships between these tables. Calendars have events, and events have attendees, instances, and reminders.

Image

FIGURE 17.1 Calendar data diagram

Calendar Data via the Calendar Content Provider

Calendar data is available to use via the CalendarContract content provider. In Hours 1416, you saw how to develop a content provider to work with Flickr photo data and take advantage of LoaderCallbacks to display that data in fragments. You can use the same approach to query and display calendar data.

This hour shows how to create a simple application to read calendar data. You’ll also see how to interact with calendar data via intents. The third way to interact with calendar or contact data is via a sync adapter. A sync adapter syncs remote data with a local content provider, which could be the calendar or contacts provider.

Permissions are required in the Android manifest XML file in order to use the calendar content provider. The read and write permissions are as follows:

<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />


Note: Sync Adapter and Calendar Content Provider

A sync adapter has write access to more columns than an application. An app can change a calendar name, display name, and whether the calendar is synced. A sync adapter can change these and in addition can update the account name, account type, maximum number of reminders, and many other columns. For the full list, see http://developer.android.com/reference/android/provider/CalendarContract.Calendars.html.


This section shows how to create a ListFragment called CalendarListFragment to list available calendars. The goal is to list all available calendars on the device. You use a SimpleCursorAdapter to display calendar data in a list. To display the account name and display name, you use the Calendars.ACCOUNT_NAME and Calendars.CALENDAR_DISPLAY_NAME columns.

The four projects that accompany this hour are the following:

Image Hour17AppCalendar, the basic calendar app

Image Hour17CalendarIntent, for using intents with the calendar

Image Hour17AppContacts, the basic contact app

Image Hour17AppContactIntent, for using intents with contacts

Listing 17.1 shows the CalendarListFragment definition and onActivityCreated() method. The SimpleCursorAdapter mAdapter is instantiated in the onActivityCreated() method. The Hour17AppCalendar project contains this source code.

LISTING 17.1 CalendarListFragment Using a SimpleCursorAdapter


1:  public class CalendarListFragment extends ListFragment
2:               implements LoaderCallbacks<Cursor>    {
3:    SimpleCursorAdapter mAdapter;
4:    @Override
5:    public void onActivityCreated(Bundle savedInstanceState) {
6:      super.onActivityCreated(savedInstanceState);
7:      getLoaderManager().initLoader(0, null, this);
8:      mAdapter = new SimpleCursorAdapter(getActivity(),
9:          android.R.layout.simple_list_item_2,
10:         null, //cursor
11:         new String[] {Calendars.ACCOUNT_NAME, Calendars.CALENDAR_DISPLAY_NAME},
12:         new int[] { android.R.id.text1,  android.R.id.text2,}, 0);
13:     setListAdapter(mAdapter);
14:  }


Listing 17.1 is similar to Listings 15.1 and 15.2. Hour 15 showed you how to create a simple ListFragment to display Flickr photo data. Listing 17.1 also uses a ListFragment, but with the goal of displaying calendar data. On line 2, you see that this code implements LoaderCallbacks. Line 7 calls initLoader(). A SimpleCursorAdapter is instantiated in lines 8–12. Line 11 specifies the calendar columns that should be used to retrieve data; in this case, the Calendars.ACCOUNT_NAME and Calendars.CALENDAR_DISPLAY_NAME.

Because you implement LoaderCallbacks, you must add the three required methods: onCreateLoader(), onLoadFinished(), and onLoaderReset(), as shown in Listing 17.2.

LISTING 17.2 LoaderCallback Methods


1:  @Override
2:  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
3:    CursorLoader cursorLoader = new CursorLoader(getActivity(),
4:        Calendars.CONTENT_URI,
5:        null, null, null, null);
6:    return cursorLoader;
7:  }
8:  @Override
9:  public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
10:   mAdapter.swapCursor(cursor);
11: }
12: @Override
13: public void onLoaderReset(Loader<Cursor> loader) {
14:    mAdapter.swapCursor(null);
15: }


The onLoadFinished() and onLoaderReset() methods call the swapCursor() method, as you have seen previously. The work in Listing 17.2 occurs in the onCreateLoader() method. A cursorLoader is created using the Calendars.CONTENT_URI on line 4. You are adding no additional parameters, thereby retrieving all data for the calendar.

When the data from the Calendars.CONTENT_URI is available, the onLoadFinished() method runs and the cursor becomes available to the SimpleCursorAdapter mAdapter. As specified in Listing 17.1, data from two fields in the cursor will be displayed.

Figure 17.2 shows the result. Two calendars are available: the main account calendar and a calendar that was made visible to the main account.

Image

FIGURE 17.2 CalendarListFragment display

Now that a list of calendars is on the device, you can consider displaying available events for a specific calendar. To do that, you implement the onListItemClick() method of the CalendarListFragment. Listing 17.3 shows the onListItemClick() method and Listings 17.117.3 include the code for all the CalendarListFragment.

LISTING 17.3 Clicking on a Calendar Item


1:  @Override
2:  public void onListItemClick(ListView l, View v, int position, long id) {
3:    EventListFragment eventListFragment = new EventListFragment();
4:    Bundle args = new Bundle();
5:    args.putLong("calendarId", id);
6:    eventListFragment.setArguments(args);
7:    FragmentTransaction ft = getFragmentManager().beginTransaction();
8:    ft.replace(R.id.layout_container, eventListFragment);
9:    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
10:   ft.commit();
11: }


In Listing 17.3, the parameter id of the onListItemClick() method is put into a bundle and made available to the new fragment EventListFragment. The id is used to retrieve the data for a particular calendar.

Listing Calendar Events

A calendar event includes important data such as title, description, attendees, time, and location to help you be where you need to be for an event.

Names for some common fields are CALENDAR_ID, ORGANIZER, TITLE, EVENT_LOCATION, DESCRIPTION, EVENT_COLOR, DTSTART, DTEND, EVENT_TIMEZONE, EVENT_END_TIMEZONE, DURATION, ALL_DAY, RRULE, and RDATE.

DTSTART and DTEND are the columns for start and end dates. RRULE and RDATE contain information about recurring events.

Additional columns are available for events. The full list is on the CalendarContract.Events page at http://developer.android.com/reference/android/provider/CalendarContract.Events.html.

Your goal is to list all future events, but first look at how an event is defined on the Google Calendar web app and how that event looks on an Android device. Because the underlying data is the same, you can make a connection between what is displayed on the Web and in the app and what is available via the content provider. Figure 17.3 shows a simple event defined in a web browser. Figure 17.4 shows the same event in the Android calendar on a device running Jelly Bean.

Image

FIGURE 17.3 Calendar event defined in a web browser

Image

FIGURE 17.4 Calendar event on an Android device

Figures 17.3 and 17.4 show event details that you can edit that match the columns from the calendar content provider.

To use this data in your own app, let’s keep things simple and create another ListFragment that uses a SimpleCursorAdapter. The ListFragment is called EventListFragment.

Listing 17.4 shows the onActivityCreated() method for the EventListFragment.

LISTING 17.4 EventListFragment onActivityCreated()


1:  public void onActivityCreated(Bundle savedInstanceState) {
2:    super.onActivityCreated(savedInstanceState);
3:    Bundle b = this.getArguments();
4:    getLoaderManager().initLoader(0, b, this);
5:    mAdapter = new SimpleCursorAdapter(getActivity(),
6:      android.R.layout.simple_list_item_2,
7:      null, //cursor
8:      new String[] {CalendarContract.Events.TITLE, CalendarContract.Events.
RRULE},
9:      new int[] { android.R.id.text1,  android.R.id.text2,}, 0);
10: setListAdapter(mAdapter);
11: }


Listing 17.4 uses a SimpleCursorAdapter and line 8 specifies that it should display the TITLE and RRULE for the event. Note that in lines 3 and 4, the bundle passed to this fragment is retrieved and passed a parameter to initLoader().

Listing 17.5 shows the onCreateLoader() method for EventListFragment. The bulk of the work for the fragment happens here. The bundle that was passed in initLoader() is accessed and the calendar id is retrieved. That calendar id is used to query the events table.

The query occurs on lines 7–10. You’re looking for two things in this query:

Image All calendar events that have an end date greater than the current date and time

Image All calendar events that have an RRULE defined

LISTING 17.5 EventListFragment onCreateLoader()


1:  @Override
2:  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
3:  long calendarId = args.getLong("calendarId");
4:  CursorLoader cursorLoader = new CursorLoader(getActivity(),
5:  Events.CONTENT_URI,
6:  null,
7:      Events.CALENDAR_ID +"= '" + calendarId +"' AND " +
8:      Events.DTEND + "> " + new Date().getTime() +
9:      " OR " + Events.CALENDAR_ID +"= '" + calendarId +
10:     "' AND " + Events.RRULE + " NOT NULL", null, null);
11: return cursorLoader;
12: }


This simple application illustrates getting all future events and all recurring events. The RRULE contains rules for recurring events. Using it in this way ensures that you capture recurring events in the query.

This example does a specific query based on the dates of future events. Line 8 compares the end date of the event to the current date.

This listing also checks for recurring events via line 10, which checks the RRULE value. If this value is not null, then the event is returned in the query.


Note: Recurring Event Definitions

The RRULE is one component of defining a recurring event. Recurrence relies on the start date, the recurrent rules, and exceptions to recurrence rules. RFC 5545 contains a detailed definition of RRULE. See http://tools.ietf.org/html/rfc5545#section-3.8.5.3.


Figure 17.5 shows the EventListFragment. You can see a recurring event that is a reminder to check bank statements, an event to work on Hour 17, and the lunch meeting that is referred to in Figures 17.3 and 17.4.

Image

FIGURE 17.5 EventListFragment showing three events

Drilling Further into Calendar Data

Given the calendar id, you can retrieve a list of events as shown earlier. You can access attendees, instances, and reminders for a particular event in a similar way.

One option to get all additional event data would be to create a single fragment with multiple cursor loaders. Multiple initLoader() methods would be called with different identifiers for different types of data.

Using Intents to Update a Calendar

You can use the calendar provider to insert and update new data into a calendar. The calendar application provides a set of intents that can be used to view, update, and insert calendar data.

Doing these tasks does not require any additional permissions for the app. Listing 17.6 shows an example of using an INSERT intent on the calendar to schedule a New Year’s Eve party. The code is in the onCreate() method of an activity. When the activity runs, the calendar app appears with this data pre-populated, as shown in Figure 17.6. The Hour17CalendarIntent project contains this source code.

LISTING 17.6 Inserting an Event Using an Insert Intent


1: @Override
2: protected void onCreate(Bundle savedInstanceState) {
3: super.onCreate(savedInstanceState);
4: setContentView(R.layout.activity_main);
5: Calendar beginTime = Calendar.getInstance();
6: beginTime.set(2013, 11, 31, 10, 30);
7: Calendar endTime = Calendar.getInstance();
8: endTime.set(2014, 0, 1, 2, 30);
9: Intent intent = new Intent(Intent.ACTION_INSERT)
10: .setData(Events.CONTENT_URI)
11:        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.
getTimeInMillis())
12:        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.
getTimeInMillis())
13:        .putExtra(Events.TITLE, "New Years Party")
14:         .putExtra(Events.DESCRIPTION, "Have fun")
15:.putExtra(Events.EVENT_LOCATION, "Our house")
16:         .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
17:         .putExtra(Intent.EXTRA_EMAIL, "[email protected]");
18:startActivity(intent);
19:}


Image

FIGURE 17.6 Inserting an event via a calendar Intent

The event is defined on line 9 with additional data being added in lines 10–17 using the putExtra() method.

The result that the calendar app opens with this data pre-filled, as shown in Figure 17.6.

Understanding Contacts

Contacts are easy to understand conceptually. Contacts represent a list of people you know and how to reach them. You might know details about a person such as where he or she works. You might have multiple ways of reaching a person such as through email, work phone, or cell phone. People might also have multiple email addresses that include personal and work. You might also contact them on different types of services or accounts such as Facebook and Twitter.

On Android, you handle these distinctions regarding data and people using a three-tiered data model. A raw contact represents a person’s data coming from a single account type and account name. Gmail is an example of account type. The Gmail address of the contact is the account name. You can have more than one raw contact for a single individual. A Facebook account and user name would represent an account type and account name.

Raw contacts exist in the ContactsContract.RawContacts table.

When one or more raw contacts are aggregated and presumed to be for the same person, the result is an entry in the ContactsContract.Contacts table.

The ContactsContract.Data table holds specific data such as telephone number or email address for a raw contact.

Additional tables of the ContactsContract class include groups, status updates, and phone lookups.

The following list describes the tables in ContactsContract:

Image Contacts: People who can be contacted.

Image RawContacts: Individual accounts for people (for example, a specific Gmail account).

Image Data: Specific contact data such as telephone numbers.

Image Groups: Contact groups such as a Gmail group.

Image StatusUpdates: Social status updates, including IM availability.

Image PhoneLookups: Use for caller id, which can identify a contact by phone number.

Querying Contacts

You can again use a ListFragment to show some simple contact queries. You can see the columns available for the contacts at http://developer.android.com/reference/android/provider/ContactsContract.ContactsColumns.html.

You use the DISPLAY_NAME and HAS_PHONE_NUMBER columns to get data to display in a list using a SimpleCursorAdapter as shown here. The Hour17AppContacts project contains this source code:

mAdapter = new SimpleCursorAdapter(getActivity(),
    android.R.layout.simple_list_item_2,
    null, //cursor
    new String[] {Contacts.DISPLAY_NAME, Contacts.HAS_PHONE_NUMBER},
    new int[] { android.R.id.text1,  android.R.id.text2,}, 0);
setListAdapter(mAdapter);

To get all available contacts, you would use the Contact.CONTENT_URI in the onCreateLoader() method.

With all parameters set to null, all contacts are retrieved and used:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
   CursorLoader cursorLoader = new CursorLoader(getActivity(),
   Contacts.CONTENT_URI, null, null, null, null);
  return cursorLoader;
}

The ContactListFragment displays the DISPLAY_NAME for the contact and a 0 or 1 to represent the HAS_PHONE_NUMBER column.

You can use the Contacts.CONTENT_FILTER_URI to filter the results by specifying a full or partial name to filter on. You append the name to the CONTENT_FILTER_URI. You can use this to filter contacts that contain “Scott.”

Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "Scott"),  null, null, null,
null);

Getting additional details for contacts requires using the retrieved data and drilling into the other contact tables.

Permissions are required in the Android manifest XML file in order to use the contact content provider. The read and write permissions are as follows:

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />

Using Contact Intents

A number of actions are available via intents for contact data. You can pick a contact, insert a contact, edit a contact, or display a contact picker that also provides for adding a contact.

The specific intents available are as follows:

Image ACTION_PICK: Pick from a list of contacts.

Image ACTION_INSERT_OR_EDIT: Pick an existing contact or add a new one.

Image ACTION_EDIT: Edit a contact.

Image ACTION_INSERT: Insert a new contact.

To define an intent to pick from a list of contacts, you must set the data for contacts in the intent. The Hour17AppContactIntentproject contains this source code:

//PICK A CONTACT
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(Contacts.CONTENT_URI);
startActivity(intent);

To define an intent to insert or edit a contact, you must set the mimeType to the contacts type:

//PICK OR INSERT A CONTACT
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);

Figure 17.7 shows the result of the intent to pick a contact and the intent to insert or edit a contact.

Image

FIGURE 17.7 Results of contact intents

Summary

In this hour, you relied on your knowledge of ListFragments, CursorLoaders, and SimpleCursorAdapters to explore the data provided by the calendar and contact content providers. Calendar and contact data are accessible via content providers. You can also use intents within your apps to read and update this data.

Q&A

Q. Does accessing calendar and contact data from a content provider or an intent make more sense?

A. One answer is that it depends on your app. Intents are useful and show the user a familiar interface for performing a common task. If your app needs to occasionally interact with the calendar or contacts app, then using intents is a good idea.

Workshop

Quiz

1. What is the INSTANCES table used for?

2. What is RRULE?

3. When working with contacts, how do you use the CONTENT_FILTER_URI?

Answers

1. You use the INSTANCES table in the calendar to handle recurring events.

2. RRULE is a column in the calendar data. It represents rules for recurring events.

3. You use the CONTENT_FILTER_URI with an appended path to filter data from the content provider. You can include a name or phone number as a part of an appended path. That appended value is used to filter the returned data.

Exercise

Create a fragment to display additional data about an event. Experiment by using multiple loaders to load data from different tables. You may use a DialogFragment.

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

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