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;
+    }
 }