Add some basic editing support, and caller ID lookup.
This adds the ability to insert contacts and data rows. Data rows
get some light processing to keep the phone_lookup table up to date.
The phone_lookup table was added to do fast caller ID lookups.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 86f73d6..4b0230e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,13 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.providers.contacts2"
- android:sharedUserId="android.uid.shared"
>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<application
- android:process="android.process.acore"
android:label="ContactsProvider2"
>
<provider android:name="ContactsProvider2"
diff --git a/src/com/android/providers/contacts2/ContactsContract.java b/src/com/android/providers/contacts2/ContactsContract.java
index 041d718..86a683f 100644
--- a/src/com/android/providers/contacts2/ContactsContract.java
+++ b/src/com/android/providers/contacts2/ContactsContract.java
@@ -26,7 +26,9 @@
* TODO: move to android.provider
*/
public final class ContactsContract {
+ /** The authority for the contacts provider */
public static final String AUTHORITY = "com.android.contacts";
+ /** A content:// style uri to the authority for the contacts provider */
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
private interface ContactsColumns {
@@ -52,7 +54,7 @@
* The phonetic version of the family name for the contact.
* <P>Type: TEXT</P>
*/
- public static final String PHONETIC_FAMILY_NAME = "phonetic_given_name";
+ public static final String PHONETIC_FAMILY_NAME = "phonetic_family_name";
/**
* The display name for the contact.
@@ -117,6 +119,22 @@
* person.
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
+
+ /**
+ * A sub-directory of a single contact that contains all of their {@link Data} rows.
+ * To access this directory append
+ */
+ public static final class Data implements BaseColumns, DataColumns {
+ /**
+ * no public constructor since this is a utility class
+ */
+ private Data() {}
+
+ /**
+ * The directory twig for this sub-table
+ */
+ public static final String CONTENT_DIRECTORY = "data";
+ }
}
private interface DataColumns {
@@ -173,7 +191,28 @@
* The content:// style URI for this table
*/
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
+ }
+ /**
+ * A table that represents the result of looking up a phone number, for example for caller ID.
+ * The table joins that data row for the phone number with the contact that owns the number.
+ * To perform a lookup you must append the number you want to find to {@link #CONTENT_URI}.
+ */
+ public static final class PhoneLookup implements BaseColumns, DataColumns, ContactsColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private PhoneLookup() {}
+
+ /**
+ * The content:// style URI for this table. Append the phone number you want to lookup
+ * to this URI and query it to perform a lookup. For example:
+ *
+ * {@code
+ * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, phoneNumber);
+ * }
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "phone_lookup");
}
/**
@@ -186,6 +225,26 @@
/**
* Columns common across the specific types.
*/
+ private interface BaseCommonColumns {
+ /**
+ * The package name that defines this type of data.
+ */
+ public static final String PACKAGE = "package";
+
+ /**
+ * The kind of the data, scoped within the package stored in {@link #PACKAGE}.
+ */
+ public static final String KIND = "kind";
+
+ /**
+ * A reference to the {@link Contacts#_ID} that this data belongs to.
+ */
+ public static final String CONTACT_ID = "contact_id";
+ }
+
+ /**
+ * Columns common across the specific types.
+ */
private interface CommonColumns {
/**
* The type of data, for example Home or Work.
@@ -215,11 +274,11 @@
/**
* Common data definition for telephone numbers.
*/
- public static final class Phone {
+ public static final class Phone implements BaseCommonColumns {
private Phone() {}
/** Signifies a phone number row that is stored in the data table */
- public static final int KIND = 5;
+ public static final int KIND_PHONE = 5;
/**
* The type of data, for example Home or Work.
@@ -249,22 +308,16 @@
public static final String NUMBER = "data3";
/**
- * The normalized phone number
- * <P>Type: TEXT</P>
- */
- public static final String NUMBER_KEY = "data4";
-
- /**
* Whether this is the primary phone number
* <P>Type: INTEGER (if set, non-0 means true)</P>
*/
- public static final String ISPRIMARY = "data5";
+ public static final String ISPRIMARY = "data4";
}
/**
* Common data definition for email addresses.
*/
- public static final class Email implements CommonColumns {
+ public static final class Email implements BaseCommonColumns, CommonColumns {
private Email() {}
/** Signifies an email address row that is stored in the data table */
@@ -279,7 +332,7 @@
/**
* Common data definition for postal addresses.
*/
- public static final class Postal implements CommonColumns {
+ public static final class Postal implements BaseCommonColumns{
private Postal() {}
/** Signifies a postal address row that is stored in the data table */
@@ -294,7 +347,7 @@
/**
* Common data definition for IM addresses.
*/
- public static final class Im implements CommonColumns {
+ public static final class Im implements BaseCommonColumns {
private Im() {}
/** Signifies an IM address row that is stored in the data table */
@@ -309,7 +362,7 @@
/**
* Common data definition for organizations.
*/
- public static final class Organization {
+ public static final class Organization implements BaseCommonColumns {
private Organization() {}
/** Signifies an organization row that is stored in the data table */
@@ -348,7 +401,7 @@
/**
* Notes about the contact.
*/
- public static final class Note {
+ public static final class Note implements BaseCommonColumns {
private Note() {}
/** Signifies a free-form note row that is stored in the data table */
diff --git a/src/com/android/providers/contacts2/ContactsProvider2.java b/src/com/android/providers/contacts2/ContactsProvider2.java
index 050e0ee..080cfe4 100644
--- a/src/com/android/providers/contacts2/ContactsProvider2.java
+++ b/src/com/android/providers/contacts2/ContactsProvider2.java
@@ -16,10 +16,13 @@
package com.android.providers.contacts2;
+import com.android.providers.contacts2.ContactsContract.CommonDataKinds;
import com.android.providers.contacts2.ContactsContract.Contacts;
import com.android.providers.contacts2.ContactsContract.Data;
+import com.android.providers.contacts2.ContactsContract.CommonDataKinds.Phone;
import android.content.ContentProvider;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
@@ -27,6 +30,8 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
import android.util.Log;
import java.util.HashMap;
@@ -38,27 +43,36 @@
public class ContactsProvider2 extends ContentProvider {
private static final String TAG = "~~~~~~~~~~~~~"; // TODO: set to class name
- private static final int DATABASE_VERSION = 4;
+ private static final int DATABASE_VERSION = 8;
private static final String DATABASE_NAME = "contacts2.db";
- private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int CONTACTS = 1000;
private static final int CONTACTS_ID = 1001;
+ private static final int CONTACTS_DATA = 1002;
private static final int DATA = 2000;
private static final int DATA_ID = 2001;
+ private static final int PHONE_LOOKUP = 3000;
+
+ /** Contains just the contacts columns */
private static final HashMap<String, String> sContactsProjectionMap;
+ /** Contains just the data columns */
private static final HashMap<String, String> sDataProjectionMap;
+ /** Contains the data and contacts columns, for joined tables */
+ private static final HashMap<String, String> sDataContactsProjectionMap;
static {
// Contacts URI matching table
- final UriMatcher matcher = sURIMatcher;
+ final UriMatcher matcher = sUriMatcher;
matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
+ matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
+ matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
HashMap<String, String> columns;
@@ -91,6 +105,12 @@
columns.put(Data.DATA9, Data.DATA9);
columns.put(Data.DATA10, Data.DATA10);
sDataProjectionMap = columns;
+
+ // Data and contacts projection map for joins. _id comes from the data table
+ columns = new HashMap<String, String>();
+ columns.putAll(sContactsProjectionMap);
+ columns.putAll(sDataProjectionMap); // _id will be replaced with the one from data
+ sDataContactsProjectionMap = columns;
}
private OpenHelper mOpenHelper;
@@ -105,10 +125,13 @@
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "Bootstrapping database");
+ // Public contacts table
db.execSQL("CREATE TABLE contacts (" +
Contacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
Contacts.GIVEN_NAME + " TEXT," +
+ Contacts.PHONETIC_GIVEN_NAME + " TEXT," +
Contacts.FAMILY_NAME + " TEXT," +
+ Contacts.PHONETIC_FAMILY_NAME + " TEXT," +
Contacts.TIMES_CONTACTED + " INTEGER," +
Contacts.LAST_TIME_CONTACTED + " INTEGER," +
Contacts.CUSTOM_RINGTONE + " TEXT," +
@@ -116,21 +139,34 @@
Contacts.STARRED + " INTEGER" +
");");
+ // Public generic data table
db.execSQL("CREATE TABLE data (" +
Data._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
Data.CONTACT_ID + " INTEGER NOT NULL," +
Data.PACKAGE + " TEXT NOT NULL," +
Data.KIND + " INTEGER NOT NULL," +
- Data.DATA1 + " TEXT," +
- Data.DATA2 + " TEXT," +
- Data.DATA3 + " TEXT," +
- Data.DATA4 + " TEXT," +
- Data.DATA5 + " TEXT," +
- Data.DATA6 + " TEXT," +
- Data.DATA7 + " TEXT," +
- Data.DATA8 + " TEXT," +
- Data.DATA9 + " TEXT," +
- Data.DATA10 + " TEXT" +
+ Data.DATA1 + " NUMERIC," +
+ Data.DATA2 + " NUMERIC," +
+ Data.DATA3 + " NUMERIC," +
+ Data.DATA4 + " NUMERIC," +
+ Data.DATA5 + " NUMERIC," +
+ Data.DATA6 + " NUMERIC," +
+ Data.DATA7 + " NUMERIC," +
+ Data.DATA8 + " NUMERIC," +
+ Data.DATA9 + " NUMERIC," +
+ Data.DATA10 + " NUMERIC" +
+ ");");
+
+ // Private phone numbers table used for lookup
+ db.execSQL("CREATE TABLE phone_lookup (" +
+ "_id INTEGER PRIMARY KEY," +
+ "data_id INTEGER REFERENCES data(_id) NOT NULL," +
+ "contact_id INTEGER REFERENCES contacts(_id) NOT NULL," +
+ "normalized_number TEXT NOT NULL" +
+ ");");
+
+ db.execSQL("CREATE INDEX phone_lookup_index ON phone_lookup (" +
+ "normalized_number ASC, contact_id, data_id" +
");");
}
@@ -141,6 +177,7 @@
db.execSQL("DROP TABLE IF EXISTS contacts;");
db.execSQL("DROP TABLE IF EXISTS data;");
+ db.execSQL("DROP TABLE IF EXISTS phone_lookup;");
onCreate(db);
}
@@ -156,6 +193,15 @@
return true;
}
+ /**
+ * Called when a change has been made.
+ *
+ * @param uri the uri that the change was made to
+ */
+ private void onChange(Uri uri) {
+ getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
+ }
+
@Override
public boolean isTemporary() {
return false;
@@ -163,7 +209,78 @@
@Override
public Uri insert(Uri uri, ContentValues values) {
- throw new UnsupportedOperationException();
+ final int match = sUriMatcher.match(uri);
+ long id = 0;
+ switch (match) {
+ case CONTACTS: {
+ id = insertContact(values);
+ break;
+ }
+
+ case CONTACTS_DATA: {
+ values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
+ id = insertData(values);
+ break;
+ }
+
+ case DATA: {
+ id = insertData(values);
+ break;
+ }
+
+ default:
+ throw new UnsupportedOperationException("Unknow uri: " + uri);
+ }
+
+ final Uri result = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
+ onChange(result);
+ return result;
+ }
+
+ /**
+ * Inserts an item in the contacts table
+ *
+ * @param values the values for the new row
+ * @return the row ID of the newly created row
+ */
+ private long insertContact(ContentValues values) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ return db.insert("contacts", Contacts.GIVEN_NAME, values);
+ }
+
+ /**
+ * Inserts an item in the data table
+ *
+ * @param values the values for the new row
+ * @return the row ID of the newly created row
+ */
+ private long insertData(ContentValues values) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long id = 0;
+ db.beginTransaction();
+ try {
+ // Insert the data row itself
+ id = db.insert("data", Data.DATA1, values);
+
+ final String packageName = values.getAsString(Data.PACKAGE);
+ // If it's a phone number add the normalized version to the lookup table
+ if (CommonDataKinds.PACKAGE_COMMON.equals(packageName)) {
+ final int kind = values.getAsInteger(Data.KIND);
+ if (kind == Phone.KIND_PHONE) {
+ final ContentValues phoneValues = new ContentValues();
+ final String number = values.getAsString(Phone.NUMBER);
+ phoneValues.put("normalized_number",
+ PhoneNumberUtils.getStrippedReversed(number));
+ phoneValues.put("data_id", id);
+ phoneValues.put("contact_id", values.getAsLong(Phone.CONTACT_ID));
+ db.insert("phone_lookup", null, phoneValues);
+ }
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ return id;
}
@Override
@@ -173,7 +290,7 @@
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
+ throw new UnsupportedOperationException();
}
@Override
@@ -182,7 +299,7 @@
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- final int match = sURIMatcher.match(uri);
+ final int match = sUriMatcher.match(uri);
switch (match) {
case CONTACTS: {
qb.setTables("contacts");
@@ -197,6 +314,13 @@
break;
}
+ case CONTACTS_DATA: {
+ qb.setTables("data");
+ qb.setProjectionMap(sDataProjectionMap);
+ qb.appendWhere("contact_id = " + uri.getPathSegments().get(1));
+ break;
+ }
+
case DATA: {
qb.setTables("data");
qb.setProjectionMap(sDataProjectionMap);
@@ -210,6 +334,28 @@
break;
}
+ case PHONE_LOOKUP: {
+ if (TextUtils.isEmpty(sortOrder)) {
+ // Default the sort order to something reasonable so we get consistent
+ // results when callers don't request an ordering
+ sortOrder = Contacts._ID;
+ }
+
+ final String number = uri.getLastPathSegment();
+ final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
+ final StringBuilder tables = new StringBuilder();
+ tables.append("contacts, (SELECT data_id FROM phone_lookup WHERE (phone_lookup.normalized_number GLOB '");
+ tables.append(normalizedNumber);
+ tables.append("*')) AS lookup, data");
+ qb.setTables(tables.toString());
+ qb.appendWhere("lookup.data_id=data._id AND data.contact_id=contacts._id AND ");
+ qb.appendWhere("PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", ");
+ qb.appendWhereEscapeString(number);
+ qb.appendWhere(")");
+ qb.setProjectionMap(sDataContactsProjectionMap);
+ break;
+ }
+
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -224,11 +370,11 @@
@Override
public String getType(Uri uri) {
- final int match = sURIMatcher.match(uri);
+ final int match = sUriMatcher.match(uri);
switch (match) {
case CONTACTS: return Contacts.CONTENT_TYPE;
case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
}
- return null;
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
diff --git a/tests/src/com/android/contacts2/ContactDetails2.java b/tests/src/com/android/contacts2/ContactDetails2.java
new file mode 100644
index 0000000..eaa0197
--- /dev/null
+++ b/tests/src/com/android/contacts2/ContactDetails2.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts2;
+
+import com.android.providers.contacts2.ContactsContract.Contacts;
+import com.android.providers.contacts2.ContactsContract.Data;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+/**
+ * Simple test activity to display the data entries for a contact in the contacts2 provider.
+ */
+public class ContactDetails2 extends ListActivity {
+
+ static final String[] PROJECTION = new String[] {
+ Data.PACKAGE, // 0
+ Data.KIND, // 1
+ Data.DATA1, // 2
+ Data.DATA2, // 3
+ Data.DATA3, // 4
+ Data.DATA4, // 5
+ Data.DATA5, // 6
+ Data.DATA6, // 7
+ Data.DATA7, // 8
+ Data.DATA8, // 9
+ Data.DATA9, // 10
+ Data.DATA10, // 11
+ Data._ID,
+ };
+ static final int COLUMN_INDEX_PACKAGE = 0;
+ static final int COLUMN_INDEX_KIND = 1;
+ static final int COLUMN_INDEX_DATA1 = 2;
+ static final int COLUMN_INDEX_DATA2 = 3;
+ static final int COLUMN_INDEX_DATA3 = 4;
+ static final int COLUMN_INDEX_DATA4 = 5;
+ static final int COLUMN_INDEX_DATA5 = 6;
+ static final int COLUMN_INDEX_DATA6 = 7;
+ static final int COLUMN_INDEX_DATA7 = 8;
+ static final int COLUMN_INDEX_DATA8 = 9;
+ static final int COLUMN_INDEX_DATA9 = 10;
+ static final int COLUMN_INDEX_DATA10 = 11;
+
+ DetailsAdapter mAdapter;
+
+ /**
+ * Simple task for doing an async query.
+ */
+ final class QueryTask extends AsyncTask<Uri, Void, Cursor> {
+ @Override
+ protected Cursor doInBackground(Uri... params) {
+ final Cursor c = getContentResolver().query(params[0], PROJECTION, null,
+ null, null);
+ if (c != null) {
+ c.getCount();
+ }
+ return c;
+ }
+
+ @Override
+ protected void onPostExecute(Cursor c) {
+ if (isFinishing()) {
+ if (c != null) {
+ c.close();
+ }
+ return;
+ }
+
+ mAdapter.changeCursor(c);
+ }
+ }
+
+ /**
+ * Simple list adapter to display the data rows.
+ */
+ final class DetailsAdapter extends ResourceCursorAdapter {
+ public DetailsAdapter() {
+ super(ContactDetails2.this, android.R.layout.simple_list_item_1, null);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final StringBuilder text = new StringBuilder();
+
+ text.append("Package: ");
+ text.append(cursor.getString(COLUMN_INDEX_PACKAGE));
+ text.append("\nKind: ");
+ text.append(cursor.getLong(COLUMN_INDEX_KIND));
+ text.append("\nData1: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA1));
+ text.append("\nData2: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA2));
+ text.append("\nData3: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA3));
+ text.append("\nData4: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA4));
+ text.append("\nData5: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA5));
+ text.append("\nData6: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA6));
+ text.append("\nData7: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA7));
+ text.append("\nData8: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA8));
+ text.append("\nData9: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA9));
+ text.append("\nData10: ");
+ text.append(cursor.getString(COLUMN_INDEX_DATA10));
+
+ ((TextView) view).setText(text);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ mAdapter = new DetailsAdapter();
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ new QueryTask().execute(Uri.withAppendedPath(getIntent().getData(),
+ Contacts.Data.CONTENT_DIRECTORY));
+ }
+}
diff --git a/tests/src/com/android/contacts2/Contacts2.java b/tests/src/com/android/contacts2/Contacts2.java
index e31ec4a..59a2c30 100644
--- a/tests/src/com/android/contacts2/Contacts2.java
+++ b/tests/src/com/android/contacts2/Contacts2.java
@@ -16,12 +16,27 @@
package com.android.contacts2;
+import com.android.providers.contacts2.ContactsContract.CommonDataKinds;
import com.android.providers.contacts2.ContactsContract.Contacts;
+import com.android.providers.contacts2.ContactsContract.PhoneLookup;
+import com.android.providers.contacts2.ContactsContract.CommonDataKinds.Phone;
+import com.android.providers.contacts2.ContactsContract.Contacts.Data;
import android.app.ListActivity;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class Contacts2 extends ListActivity {
@@ -73,4 +88,52 @@
new QueryTask().execute((Void[]) null);
}
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.setClass(this, ContactDetails2.class);
+ startActivity(intent);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, 42, 0, "Add data");
+ menu.add(0, 43, 0, "Phone lookup");
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case 42: {
+ final ContentResolver resolver = getContentResolver();
+ final ContentValues values = new ContentValues();
+ values.put(Contacts.GIVEN_NAME, "Bob");
+ values.put(Contacts.FAMILY_NAME, "Smith");
+ final Uri contactUri = resolver.insert(Contacts.CONTENT_URI, values);
+
+ final Uri dataUri = Uri.withAppendedPath(contactUri, Data.CONTENT_DIRECTORY);
+ values.clear();
+ values.put(Phone.PACKAGE, CommonDataKinds.PACKAGE_COMMON);
+ values.put(Phone.KIND, Phone.KIND_PHONE);
+ values.put(Phone.NUMBER, "512-555-1212");
+ values.put(Phone.TYPE, Phone.TYPE_MOBILE);
+ resolver.insert(dataUri, values);
+
+ return true;
+ }
+
+ case 43: {
+ final ContentResolver resolver = getContentResolver();
+ final Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, "555-1212");
+ final Cursor c = resolver.query(uri, null, null, null, null);
+ final StringBuilder sb = new StringBuilder();
+ DatabaseUtils.dumpCursor(c, sb);
+ Log.i("!!!!!!!!", sb.toString());
+ }
+ }
+ return false;
+ }
}