OSDN Git Service

Use ContactsSource in View
authorEvan Millar <emillar@google.com>
Wed, 2 Sep 2009 15:55:01 +0000 (08:55 -0700)
committerEvan Millar <emillar@google.com>
Thu, 3 Sep 2009 00:39:13 +0000 (17:39 -0700)
Combine BaseContactCardActivity and ViewContactActivity, since
EditContactActivity isn't using it anyway.

Use Entity querying, and ContactsSource in ViewContactActivity.

Change-Id: Idc0fb4d5d4e2637f5a345461362d55d4bfcb6cf2

res/values/strings.xml
src/com/android/contacts/BaseContactCardActivity.java [deleted file]
src/com/android/contacts/ContactEntryAdapter.java
src/com/android/contacts/ContactsUtils.java
src/com/android/contacts/ViewContactActivity.java
src/com/android/contacts/model/ContactsSource.java
src/com/android/contacts/model/EntityModifier.java
src/com/android/contacts/model/HardCodedSources.java
src/com/android/contacts/ui/EditContactActivity.java

index 29a3fa2..a2db4b3 100644 (file)
 
 
 
-<string name="map_home">Map home</string>
-<string name="map_work">Map work</string>
-<string name="map_other">Map other</string>
-<string name="map_custom">Map <xliff:g id="custom">%s</xliff:g></string>
-
-
+<string name="map_home">View home address</string>
+<string name="map_work">View work address</string>
+<string name="map_other">View other address</string>
+<string name="map_custom">View <xliff:g id="custom">%s</xliff:g> address</string>
+
+<string name="chat_aim">Chat using AIM</string>
+<string name="chat_msn">Chat using Windows Live</string>
+<string name="chat_yahoo">Chat using Yahoo</string>
+<string name="chat_skype">Chat using Skype</string>
+<string name="chat_qq">Chat using QQ</string>
+<string name="chat_gtalk">Chat using Google Talk</string>
+<string name="chat_icq">Chat using ICQ</string>
+<string name="chat_jabber">Chat using Jabber</string>
+<string name="chat_other">Chat</string>
 
 <string name="postal_street">Street</string>
 <string name="postal_pobox">PO box</string>
diff --git a/src/com/android/contacts/BaseContactCardActivity.java b/src/com/android/contacts/BaseContactCardActivity.java
deleted file mode 100644 (file)
index f5b207c..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * 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.contacts;
-
-import com.android.contacts.ScrollingTabWidget.OnTabSelectionChangedListener;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.internal.widget.ContactHeaderWidget;
-
-import android.app.Activity;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Entity;
-import android.content.EntityIterator;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * The base Activity class for viewing and editing a contact.
- */
-public abstract class BaseContactCardActivity extends Activity implements
-        NotifyingAsyncQueryHandler.AsyncQueryListener, OnTabSelectionChangedListener {
-
-    private static final String TAG = "BaseContactCardActivity";
-
-    private SparseArray<Long> mTabRawContactIdMap;
-    protected Uri mOriginalUri;
-    protected Uri mUri;
-    protected ScrollingTabWidget mTabWidget;
-    protected ContactHeaderWidget mContactHeaderWidget;
-    private NotifyingAsyncQueryHandler mHandler;
-
-    protected LayoutInflater mInflater;
-
-    //Projection used for the query that determines which tabs to add.
-    protected static final String[] TAB_PROJECTION = new String[] {
-        RawContacts._ID,
-        RawContacts.ACCOUNT_NAME,
-        RawContacts.ACCOUNT_TYPE
-    };
-    protected static final int TAB_CONTACT_ID_COLUMN_INDEX = 0;
-    protected static final int TAB_ACCOUNT_NAME_COLUMN_INDEX = 1;
-    protected static final int TAB_ACCOUNT_TYPE_COLUMN_INDEX = 2;
-
-    protected static final String SELECTED_RAW_CONTACT_ID_KEY = "selectedRawContact";
-    protected Long mSelectedRawContactId = null;
-
-    private static final int TOKEN_TABS = 0;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        final Intent intent = getIntent();
-        resolveContactUriFromIntent(intent);
-
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
-        setContentView(R.layout.contact_card_layout);
-
-        mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
-        mContactHeaderWidget.showStar(true);
-        mContactHeaderWidget.bindFromContactId(ContentUris.parseId(mUri));
-        mContactHeaderWidget.setExcludeMimes(new String[] {
-            Contacts.CONTENT_ITEM_TYPE
-        });
-
-        mTabWidget = (ScrollingTabWidget) findViewById(R.id.tab_widget);
-        mTabWidget.setTabSelectionListener(this);
-        mTabWidget.setVisibility(View.INVISIBLE);
-
-        mTabRawContactIdMap = new SparseArray<Long>();
-
-        mHandler = new NotifyingAsyncQueryHandler(this, this);
-
-        // TODO: turn this into async call instead of blocking ui
-        asyncSetupTabs();
-    }
-
-
-    private void resolveContactUriFromIntent(final Intent intent) {
-        mOriginalUri = intent.getData();
-        mUri = ContactsContract.Contacts.lookupContact(getContentResolver(), mOriginalUri);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
-        super.onRestoreInstanceState(savedInstanceState);
-        mSelectedRawContactId = savedInstanceState.getLong(SELECTED_RAW_CONTACT_ID_KEY);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putLong(SELECTED_RAW_CONTACT_ID_KEY, mSelectedRawContactId);
-    }
-
-    private void asyncSetupTabs() {
-        long contactId = ContentUris.parseId(mUri);
-        mHandler.startQueryEntities(TOKEN_TABS, null,
-                RawContacts.CONTENT_URI, RawContacts.CONTACT_ID + "=" + contactId, null, null);
-    }
-
-    /**
-     * Return the RawContact id associated with the tab at an index.
-     *
-     * @param index The index of the tab in question.
-     * @return The contactId associated with the tab at the specified index.
-     */
-    protected long getTabRawContactId(int index) {
-        return mTabRawContactIdMap.get(index);
-    }
-
-    /**
-     * Return the tab index associated with the RawContact id.
-     *
-     * @param index The index of the tab in question.
-     * @return The contactId associated with the tab at the specified index.
-     */
-    protected int getTabIndexForRawContactId(long rawContactId) {
-        int numTabs = mTabRawContactIdMap.size();
-        for (int i=0; i < numTabs; i++) {
-            if (mTabRawContactIdMap.get(i) == rawContactId) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /** {@inheritDoc} */
-    public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
-        try{
-            if (token == TOKEN_TABS) {
-                clearCurrentTabs();
-                bindTabs(readEntities(iterator));
-            }
-        } finally {
-            if (iterator != null) {
-                iterator.close();
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
-        // Emtpy
-    }
-
-    private ArrayList<Entity> readEntities(EntityIterator iterator) {
-        ArrayList<Entity> entities = new ArrayList<Entity>();
-        try {
-            while (iterator.hasNext()) {
-                entities.add(iterator.next());
-            }
-        } catch (RemoteException e) {
-        }
-
-        return entities;
-    }
-
-    /**
-     * Adds a tab for each {@link RawContact} associated with this contact.
-     * Override this method if you want to additional tabs and/or different
-     * tabs for your activity.
-     *
-     * @param entities An {@link ArrayList} of {@link Entity}s of all the RawContacts
-     * associated with the contact being displayed.
-     */
-    protected void bindTabs(ArrayList<Entity> entities) {
-        final Sources sources = Sources.getInstance(this);
-
-        for (Entity entity : entities) {
-            final String accountType = entity.getEntityValues().
-                    getAsString(RawContacts.ACCOUNT_TYPE);
-            final Long rawContactId = entity.getEntityValues().
-                    getAsLong(RawContacts._ID);
-
-            // TODO: ensure inflation on background task so we don't block UI thread here
-            final ContactsSource source = sources.getInflatedSource(accountType,
-                    ContactsSource.LEVEL_SUMMARY);
-            addTab(rawContactId, createTabIndicatorView(mTabWidget.getTabParent(), source));
-        }
-
-        selectInitialTab();
-        mTabWidget.setVisibility(View.VISIBLE);
-        mTabWidget.postInvalidate();
-    }
-
-    /**
-     * Add a tab to be displayed in the {@link ScrollingTabWidget}.
-     *
-     * @param contactId The contact id associated with the tab.
-     * @param view A view to use as the tab indicator.
-     */
-    protected void addTab(long rawContactId, View view) {
-        mTabRawContactIdMap.put(mTabWidget.getTabCount(), rawContactId);
-        mTabWidget.addTab(view);
-    }
-
-
-    protected void clearCurrentTabs() {
-        mTabRawContactIdMap.clear();
-        mTabWidget.removeAllTabs();
-    }
-
-    protected void selectInitialTab() {
-        int selectedTabIndex = 0;
-
-        if (mSelectedRawContactId != null) {
-            selectedTabIndex = getTabIndexForRawContactId(mSelectedRawContactId);
-            if (selectedTabIndex == -1) {
-                // If there was no matching tab, just select the first;
-                selectedTabIndex = 0;
-            }
-        }
-
-        mTabWidget.setCurrentTab(selectedTabIndex);
-        onTabSelectionChanged(selectedTabIndex, false);
-    }
-
-    @Override
-    public void onNewIntent(Intent newIntent) {
-        setIntent(newIntent);
-        resolveContactUriFromIntent(newIntent);
-        selectInitialTab();
-    }
-
-    /**
-     * Utility for creating a standard tab indicator view.
-     *
-     * @param parent The parent ViewGroup to attach the new view to.
-     * @param label The label to display in the tab indicator. If null, not label will be displayed.
-     * @param icon The icon to display. If null, no icon will be displayed.
-     * @return The tab indicator View.
-     */
-    public static View createTabIndicatorView(ViewGroup parent, CharSequence label, Drawable icon) {
-        final LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        final View tabIndicator = inflater.inflate(R.layout.tab_indicator, parent, false);
-        tabIndicator.getBackground().setDither(true);
-
-        final TextView tv = (TextView) tabIndicator.findViewById(R.id.tab_title);
-        tv.setText(label);
-
-        final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.tab_icon);
-        iconView.setImageDrawable(icon);
-
-        return tabIndicator;
-    }
-
-    /**
-     * Utility for creating a standard tab indicator view.
-     *
-     * @param context The label to display in the tab indicator. If null, not label will be displayed.
-     * @param parent The parent ViewGroup to attach the new view to.
-     * @param source The {@link ContactsSource} to build the tab view from.
-     * @return The tab indicator View.
-     */
-    public static View createTabIndicatorView(ViewGroup parent, ContactsSource source) {
-        Drawable icon = null;
-        if (source != null) {
-            final String packageName = source.resPackageName;
-            if (source.iconRes > 0) {
-                try {
-                    final Context authContext = parent.getContext().
-                            createPackageContext(packageName, 0);
-                    icon = authContext.getResources().getDrawable(source.iconRes);
-
-                } catch (PackageManager.NameNotFoundException e) {
-                    Log.d(TAG, "error getting the Package Context for " + packageName, e);
-                }
-            }
-        }
-        return createTabIndicatorView(parent, null, icon);
-    }
-}
index 5307e42..9f11165 100644 (file)
@@ -31,47 +31,6 @@ import java.util.ArrayList;
 public abstract class ContactEntryAdapter<E extends ContactEntryAdapter.Entry>
         extends BaseAdapter {
 
-    public static final String[] CONTACT_PROJECTION = new String[] {
-        Contacts.DISPLAY_NAME, // 0
-        Contacts.STARRED, //1
-        Contacts.PHOTO_ID, //2
-        Data._ID, //3
-        Data.RAW_CONTACT_ID, //4
-        Data.RES_PACKAGE, //5
-        Data.MIMETYPE, //6
-        Data.IS_PRIMARY, //7
-        Data.IS_SUPER_PRIMARY, //8
-        Data.DATA1, //9
-        Data.DATA2, //10
-        Data.DATA3, //11
-        Data.DATA4, //12
-        Data.DATA5, //13
-        Data.DATA6, //14
-        Data.DATA7, //15
-        Data.DATA8, //16
-        Data.DATA9, //17
-        Data.DATA10, //18
-    };
-    public static final int CONTACT_DISPLAY_NAME_COLUMN = 0;
-    public static final int CONTACT_STARRED_COLUMN = 1;
-    public static final int CONTACT_PHOTO_ID = 2;
-    public static final int DATA_ID_COLUMN = 3;
-    public static final int DATA_CONTACT_ID_COLUMN = 4;
-    public static final int DATA_PACKAGE_COLUMN = 5;
-    public static final int DATA_MIMETYPE_COLUMN = 6;
-    public static final int DATA_IS_PRIMARY_COLUMN = 7;
-    public static final int DATA_IS_SUPER_PRIMARY_COLUMN = 8;
-    public static final int DATA_1_COLUMN = 9;
-    public static final int DATA_2_COLUMN = 10;
-    public static final int DATA_3_COLUMN = 11;
-    public static final int DATA_4_COLUMN = 12;
-    public static final int DATA_5_COLUMN = 13;
-    public static final int DATA_6_COLUMN = 14;
-    public static final int DATA_7_COLUMN = 15;
-    public static final int DATA_8_COLUMN = 16;
-    public static final int DATA_9_COLUMN = 17;
-    public static final int DATA_10_COLUMN = 18;
-
     protected ArrayList<ArrayList<E>> mSections;
     protected LayoutInflater mInflater;
     protected Context mContext;
index 33bbf1c..7611026 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.contacts;
 
 
+import com.android.contacts.model.ContactsSource;
 import com.android.contacts.ui.FastTrackWindow;
 
 import java.io.ByteArrayInputStream;
@@ -31,9 +32,11 @@ import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
 import android.provider.Contacts;
 import android.provider.Contacts.Photos;
 import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -46,9 +49,15 @@ import android.provider.Im.ProviderNames;
 import android.database.Cursor;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 public class ContactsUtils {
 
+    private static final String TAG = "ContactsUtils";
     /**
      * Build the display title for the {@link Data#CONTENT_URI} entry in the
      * provided cursor, assuming the given mimeType.
@@ -250,4 +259,54 @@ public class ContactsUtils {
         }
         return contactId;
     }
+
+
+    /**
+     * Utility for creating a standard tab indicator view.
+     *
+     * @param parent The parent ViewGroup to attach the new view to.
+     * @param label The label to display in the tab indicator. If null, not label will be displayed.
+     * @param icon The icon to display. If null, no icon will be displayed.
+     * @return The tab indicator View.
+     */
+    public static View createTabIndicatorView(ViewGroup parent, CharSequence label, Drawable icon) {
+        final LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        final View tabIndicator = inflater.inflate(R.layout.tab_indicator, parent, false);
+        tabIndicator.getBackground().setDither(true);
+
+        final TextView tv = (TextView) tabIndicator.findViewById(R.id.tab_title);
+        tv.setText(label);
+
+        final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.tab_icon);
+        iconView.setImageDrawable(icon);
+
+        return tabIndicator;
+    }
+
+    /**
+     * Utility for creating a standard tab indicator view.
+     *
+     * @param context The label to display in the tab indicator. If null, not label will be displayed.
+     * @param parent The parent ViewGroup to attach the new view to.
+     * @param source The {@link ContactsSource} to build the tab view from.
+     * @return The tab indicator View.
+     */
+    public static View createTabIndicatorView(ViewGroup parent, ContactsSource source) {
+        Drawable icon = null;
+        if (source != null) {
+            final String packageName = source.resPackageName;
+            if (source.iconRes > 0) {
+                try {
+                    final Context authContext = parent.getContext().
+                            createPackageContext(packageName, 0);
+                    icon = authContext.getResources().getDrawable(source.iconRes);
+
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.d(TAG, "error getting the Package Context for " + packageName, e);
+                }
+            }
+        }
+        return createTabIndicatorView(parent, null, icon);
+    }
 }
index 549c8ab..b29a44c 100644 (file)
 
 package com.android.contacts;
 
-import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
-import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_CONTACT_ID_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
-import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
-
 import com.android.contacts.Collapser.Collapsible;
+import com.android.contacts.ScrollingTabWidget.OnTabSelectionChangedListener;
 import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.Sources;
+import com.android.contacts.model.ContactsSource.DataKind;
 import com.android.contacts.ui.FastTrackWindow;
+import com.android.contacts.util.NotifyingAsyncQueryHandler;
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.widget.ContactHeaderWidget;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.ActivityNotFoundException;
@@ -41,8 +37,10 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Entity;
+import android.content.EntityIterator;
 import android.content.Intent;
 import android.content.DialogInterface.OnClickListener;
+import android.content.Entity.NamedContentValues;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -58,6 +56,7 @@ import android.os.ServiceManager;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Presence;
 import android.provider.ContactsContract.RawContacts;
@@ -65,13 +64,16 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
 import android.widget.FrameLayout;
@@ -85,9 +87,10 @@ import java.util.ArrayList;
 /**
  * Displays the details of a specific contact.
  */
-public class ViewContactActivity extends BaseContactCardActivity
+public class ViewContactActivity extends Activity
         implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
-        AdapterView.OnItemClickListener {
+        AdapterView.OnItemClickListener, NotifyingAsyncQueryHandler.AsyncQueryListener,
+        OnTabSelectionChangedListener {
     private static final String TAG = "ViewContact";
     private static final String SHOW_BARCODE_INTENT = "com.google.zxing.client.android.ENCODE";
 
@@ -108,6 +111,8 @@ public class ViewContactActivity extends BaseContactCardActivity
     public static final int MENU_ITEM_JOIN_AGGREGATE = 6;
     public static final int MENU_ITEM_OPTIONS = 7;
 
+    protected Uri mOriginalUri;
+    private Uri mUri;
     private ContentResolver mResolver;
     private ViewAdapter mAdapter;
     private int mNumPhoneNumbers = 0;
@@ -132,6 +137,28 @@ public class ViewContactActivity extends BaseContactCardActivity
     private Cursor mCursor;
     private boolean mObserverRegistered;
 
+    private SparseArray<Long> mTabRawContactIdMap;
+    protected ScrollingTabWidget mTabWidget;
+    protected ContactHeaderWidget mContactHeaderWidget;
+    private NotifyingAsyncQueryHandler mHandler;
+
+    protected LayoutInflater mInflater;
+
+    //Projection used for the query that determines which tabs to add.
+    protected static final String[] TAB_PROJECTION = new String[] {
+        RawContacts._ID,
+        RawContacts.ACCOUNT_NAME,
+        RawContacts.ACCOUNT_TYPE
+    };
+    protected static final int TAB_CONTACT_ID_COLUMN_INDEX = 0;
+    protected static final int TAB_ACCOUNT_NAME_COLUMN_INDEX = 1;
+    protected static final int TAB_ACCOUNT_TYPE_COLUMN_INDEX = 2;
+
+    protected static final String SELECTED_RAW_CONTACT_ID_KEY = "selectedRawContact";
+    protected Long mSelectedRawContactId = null;
+
+    private static final int TOKEN_QUERY = 0;
+
     private ContentObserver mObserver = new ContentObserver(new Handler()) {
         @Override
         public boolean deliverSelfNotifications() {
@@ -140,8 +167,8 @@ public class ViewContactActivity extends BaseContactCardActivity
 
         @Override
         public void onChange(boolean selfChange) {
-            if (mCursor != null && !mCursor.isClosed()){
-                dataChanged();
+            if (mCursor != null && !mCursor.isClosed()) {
+                startEntityQuery();
             }
         }
     };
@@ -162,11 +189,36 @@ public class ViewContactActivity extends BaseContactCardActivity
     private FrameLayout mTabContentLayout;
     private ListView mListView;
     private boolean mShowSmsLinksForAllPhones;
+    private ArrayList<Entity> mEntities = null;
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        final Intent intent = getIntent();
+        mUri = intent.getData();
+        resolveContactUriFromIntent(intent);
+
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.contact_card_layout);
+
+        mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
+        mContactHeaderWidget.showStar(true);
+        mContactHeaderWidget.bindFromContactId(ContentUris.parseId(mUri));
+        mContactHeaderWidget.setExcludeMimes(new String[] {
+            Contacts.CONTENT_ITEM_TYPE
+        });
+
+        mTabWidget = (ScrollingTabWidget) findViewById(R.id.tab_widget);
+        mTabWidget.setTabSelectionListener(this);
+        mTabWidget.setVisibility(View.INVISIBLE);
+
+        mTabRawContactIdMap = new SparseArray<Long>();
+
+        mHandler = new NotifyingAsyncQueryHandler(this, this);
+
         mListView = new ListView(this);
         mListView.setOnCreateContextMenuListener(this);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
@@ -191,8 +243,16 @@ public class ViewContactActivity extends BaseContactCardActivity
         //TODO Read this value from a preference
         mShowSmsLinksForAllPhones = true;
 
+        //Stub query so we can get notifications.
         mCursor = mResolver.query(Uri.withAppendedPath(mUri, "data"),
-                CONTACT_PROJECTION, null, null, null);
+                new String[] {Contacts.DISPLAY_NAME}, null, null, null);
+
+        startEntityQuery();
+    }
+
+    private void resolveContactUriFromIntent(final Intent intent) {
+        mOriginalUri = intent.getData();
+        mUri = ContactsContract.Contacts.lookupContact(getContentResolver(), mOriginalUri);
     }
 
     @Override
@@ -200,7 +260,7 @@ public class ViewContactActivity extends BaseContactCardActivity
         super.onResume();
         mObserverRegistered = true;
         mCursor.registerContentObserver(mObserver);
-        dataChanged();
+        startEntityQuery();
     }
 
     @Override
@@ -229,6 +289,18 @@ public class ViewContactActivity extends BaseContactCardActivity
     }
 
     @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mSelectedRawContactId = savedInstanceState.getLong(SELECTED_RAW_CONTACT_ID_KEY);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putLong(SELECTED_RAW_CONTACT_ID_KEY, mSelectedRawContactId);
+    }
+
+    @Override
     protected Dialog onCreateDialog(int id) {
         switch (id) {
             case DIALOG_CONFIRM_DELETE:
@@ -244,12 +316,77 @@ public class ViewContactActivity extends BaseContactCardActivity
         return null;
     }
 
-    @Override
-    protected void bindTabs(ArrayList<Entity> entities) {
-        if (entities.size() > 1) {
+
+    // TAB CODE //
+    /**
+     * Adds a tab for each {@link RawContact} associated with this contact.
+     * Override this method if you want to additional tabs and/or different
+     * tabs for your activity.
+     *
+     * @param entities An {@link ArrayList} of {@link Entity}s of all the RawContacts
+     * associated with the contact being displayed.
+     */
+    protected void bindTabs() {
+        if (mEntities.size() > 1) {
             addAllTab();
         }
-        super.bindTabs(entities);
+
+        final Sources sources = Sources.getInstance(this);
+
+        for (Entity entity : mEntities) {
+            final String accountType = entity.getEntityValues().
+                    getAsString(RawContacts.ACCOUNT_TYPE);
+            final Long rawContactId = entity.getEntityValues().
+                    getAsLong(RawContacts._ID);
+
+            // TODO: ensure inflation on background task so we don't block UI thread here
+            final ContactsSource source = sources.getInflatedSource(accountType,
+                    ContactsSource.LEVEL_SUMMARY);
+            addTab(rawContactId, ContactsUtils.createTabIndicatorView(mTabWidget.getTabParent(), source));
+        }
+
+        selectInitialTab();
+        mTabWidget.setVisibility(View.VISIBLE);
+        mTabWidget.postInvalidate();
+    }
+
+    /**
+     * Add a tab to be displayed in the {@link ScrollingTabWidget}.
+     *
+     * @param contactId The contact id associated with the tab.
+     * @param view A view to use as the tab indicator.
+     */
+    protected void addTab(long rawContactId, View view) {
+        mTabRawContactIdMap.put(mTabWidget.getTabCount(), rawContactId);
+        mTabWidget.addTab(view);
+    }
+
+
+    protected void clearCurrentTabs() {
+        mTabRawContactIdMap.clear();
+        mTabWidget.removeAllTabs();
+    }
+
+    protected void selectInitialTab() {
+        int selectedTabIndex = 0;
+
+        if (mSelectedRawContactId != null) {
+            selectedTabIndex = getTabIndexForRawContactId(mSelectedRawContactId);
+            if (selectedTabIndex == -1) {
+                // If there was no matching tab, just select the first;
+                selectedTabIndex = 0;
+            }
+        }
+
+        mTabWidget.setCurrentTab(selectedTabIndex);
+        onTabSelectionChanged(selectedTabIndex, false);
+    }
+
+    @Override
+    public void onNewIntent(Intent newIntent) {
+        setIntent(newIntent);
+        resolveContactUriFromIntent(newIntent);
+        selectInitialTab();
     }
 
     private void addAllTab() {
@@ -262,33 +399,99 @@ public class ViewContactActivity extends BaseContactCardActivity
     public void onTabSelectionChanged(int tabIndex, boolean clicked) {
         long rawContactId = getTabRawContactId(tabIndex);
         mSelectedRawContactId = rawContactId;
-        dataChanged();
+        bindData();
+    }
+
+    /**
+     * Return the RawContact id associated with the tab at an index.
+     *
+     * @param index The index of the tab in question.
+     * @return The contactId associated with the tab at the specified index.
+     */
+    protected long getTabRawContactId(int index) {
+        return mTabRawContactIdMap.get(index);
+    }
+
+    /**
+     * Return the tab index associated with the RawContact id.
+     *
+     * @param index The index of the tab in question.
+     * @return The contactId associated with the tab at the specified index.
+     */
+    protected int getTabIndexForRawContactId(long rawContactId) {
+        int numTabs = mTabRawContactIdMap.size();
+        for (int i=0; i < numTabs; i++) {
+            if (mTabRawContactIdMap.get(i) == rawContactId) {
+                return i;
+            }
+        }
+        return -1;
     }
 
-    private void dataChanged() {
-        mCursor.requery();
-        if (mCursor.moveToFirst()) {
 
-            // Build up the contact entries
-            buildEntries(mCursor);
+    // QUERY CODE //
+    /** {@inheritDoc} */
+    public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
+        try{
+            if (token == TOKEN_QUERY) {
+                clearCurrentTabs();
+                mEntities = readEntities(iterator);
+                bindTabs();
+                bindData();
+            }
+        } finally {
+            if (iterator != null) {
+                iterator.close();
+            }
+        }
+    }
 
-            // Collapse similar data items in select sections.
-            Collapser.collapseList(mPhoneEntries);
-            Collapser.collapseList(mSmsEntries);
-            Collapser.collapseList(mEmailEntries);
-            Collapser.collapseList(mPostalEntries);
+    /** {@inheritDoc} */
+    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        // Emtpy
+    }
 
-            if (mAdapter == null) {
-                mAdapter = new ViewAdapter(this, mSections);
-                mListView.setAdapter(mAdapter);
-            } else {
-                mAdapter.setSections(mSections, SHOW_SEPARATORS);
+    private ArrayList<Entity> readEntities(EntityIterator iterator) {
+        ArrayList<Entity> entities = new ArrayList<Entity>();
+        try {
+            while (iterator.hasNext()) {
+                entities.add(iterator.next());
             }
+        } catch (RemoteException e) {
+        }
+
+        return entities;
+    }
+
+    private void startEntityQuery() {
+        long contactId = ContentUris.parseId(mUri);
+        mHandler.startQueryEntities(TOKEN_QUERY, null,
+                RawContacts.CONTENT_URI, RawContacts.CONTACT_ID + "=" + contactId, null, null);
+    }
+
+    private void bindData() {
+
+        // Build up the contact entries
+        buildEntries();
+
+        // Collapse similar data items in select sections.
+        Collapser.collapseList(mPhoneEntries);
+        Collapser.collapseList(mSmsEntries);
+        Collapser.collapseList(mEmailEntries);
+        Collapser.collapseList(mPostalEntries);
+
+        if (mAdapter == null) {
+            mAdapter = new ViewAdapter(this, mSections);
+            mListView.setAdapter(mAdapter);
         } else {
-            Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "invalid contact uri: " + mOriginalUri);
-            finish();
+            mAdapter.setSections(mSections, SHOW_SEPARATORS);
         }
+
+//        else {
+//            Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
+//            Log.e(TAG, "invalid contact uri: " + mOriginalUri);
+//            finish();
+//        }
     }
 
     @Override
@@ -500,7 +703,7 @@ public class ViewContactActivity extends BaseContactCardActivity
         values.put(Data.IS_SUPER_PRIMARY, 1);
         getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
                 values, null, null);
-        dataChanged();
+        startEntityQuery();
         return true;
     }
 
@@ -703,7 +906,7 @@ public class ViewContactActivity extends BaseContactCardActivity
      *
      * @param personCursor the URI for the contact being displayed
      */
-    private final void buildEntries(Cursor aggCursor) {
+    private final void buildEntries() {
         // Clear out the old entries
         final int numSections = mSections.size();
         for (int i = 0; i < numSections; i++) {
@@ -712,224 +915,215 @@ public class ViewContactActivity extends BaseContactCardActivity
 
         mRawContactIds.clear();
 
+        Sources sources = Sources.getInstance(this);
+
         // Build up method entries
         if (mUri != null) {
-            aggCursor.moveToPosition(-1);
-            while (aggCursor.moveToNext()) {
-                final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
-
-                ViewEntry entry = new ViewEntry();
-
-                final long id = aggCursor.getLong(DATA_ID_COLUMN);
-                final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
-                entry.id = id;
-                entry.uri = uri;
-                entry.mimetype = mimetype;
+            for (Entity entity: mEntities) {
+                final ContentValues entValues = entity.getEntityValues();
+                final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
                 // TODO: entry.contactId should be renamed to entry.rawContactId
-                entry.contactId = aggCursor.getLong(DATA_CONTACT_ID_COLUMN);
-                if (!mRawContactIds.contains(entry.contactId)) {
-                    mRawContactIds.add(entry.contactId);
-                }
-
-                // This performs the tab filtering
-                if (mSelectedRawContactId != null
-                        && mSelectedRawContactId != entry.contactId
-                        && mSelectedRawContactId != ALL_CONTACTS_ID) {
-                    continue;
-                }
+                long contactId = entValues.getAsLong(RawContacts._ID);
 
-                if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
-                        || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
-                        || mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
-                        || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
-                    final int type = aggCursor.getInt(DATA_1_COLUMN);
-                    final String label = aggCursor.getString(DATA_3_COLUMN);
-                    final String data = aggCursor.getString(DATA_2_COLUMN);
-                    final boolean isSuperPrimary = "1".equals(
-                            aggCursor.getString(DATA_IS_SUPER_PRIMARY_COLUMN));
+                for (NamedContentValues subValue : entity.getSubValues()) {
+                    ViewEntry entry = new ViewEntry();
 
-                    entry.type = type;
+                    ContentValues entryValues = subValue.values;
+                    final String mimetype = entryValues.getAsString(Data.MIMETYPE);
+                    if (mimetype == null || accountType == null) {
+                        continue;
+                    }
 
-                    // Don't crash if the data is bogus
-                    if (TextUtils.isEmpty(data)) {
-                        Log.w(TAG, "empty data for contact method " + id);
+                    ContactsSource contactsSource = sources.getInflatedSource(accountType,
+                            ContactsSource.LEVEL_MIMETYPES);
+                    if (contactsSource == null) {
                         continue;
                     }
 
-                    // Build phone entries
-                    if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
-                        mNumPhoneNumbers++;
-
-                        final CharSequence displayLabel = ContactsUtils.getDisplayLabel(
-                                this, mimetype, type, label);
-                        entry.label = buildActionString(R.string.actionCall, displayLabel, true);
-                        entry.data = PhoneNumberUtils.stripSeparators(data);
-                        entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
-                                Uri.fromParts("tel", data, null));
-                        entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
-                                Uri.fromParts("sms", data, null));
-                        entry.isPrimary = isSuperPrimary;
-                        entry.actionIcon = android.R.drawable.sym_action_call;
-                        mPhoneEntries.add(entry);
-
-                        if (type == CommonDataKinds.Phone.TYPE_MOBILE
-                                || mShowSmsLinksForAllPhones) {
-                            // Add an SMS entry
-                            entry.secondaryActionIcon = R.drawable.sym_action_sms;
-                        }
-                    // Build email entries
-                    } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
-                        entry.label = buildActionString(R.string.actionEmail,
-                                ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
-                        entry.data = data;
-                        entry.intent = new Intent(Intent.ACTION_SENDTO,
-                                Uri.fromParts("mailto", data, null));
-                        entry.actionIcon = android.R.drawable.sym_action_email;
-                        entry.isPrimary = isSuperPrimary;
-                        mEmailEntries.add(entry);
-                    // Build postal entries
-                    } else if (mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
-                        entry.label = buildActionString(R.string.actionMap,
-                                ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
-                        entry.data = data;
-                        entry.maxLines = 4;
-                        entry.intent = new Intent(Intent.ACTION_VIEW, uri);
-                        entry.actionIcon = R.drawable.sym_action_map;
-                        mPostalEntries.add(entry);
-                    // Build im entries
-                    } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
-                        String[] protocolStrings = getResources().getStringArray(
-                                android.R.array.imProtocols);
-                        Object protocolObj = aggCursor.getString(DATA_5_COLUMN);
-                        String host = null;
-                        // TODO: fix by moving to contactssource-based rendering rules
-//                      Object protocolObj = ContactsUtils.decodeImProtocol(
-//                      aggCursor.getString(DATA_5_COLUMN));
-//                        if (protocolObj instanceof Number) {
-//                            int protocol = ((Number) protocolObj).intValue();
-//                            entry.label = buildActionString(R.string.actionChat,
-//                                    protocolStrings[protocol], false);
-//                            host = ContactsUtils.lookupProviderNameFromId(
-//                                    protocol).toLowerCase();
-//                            if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
-//                                    || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
-//                                entry.maxLabelLines = 2;
-//                            }
-                        if (protocolObj != null) {
-                            String providerName = (String) protocolObj;
-                            entry.label = buildActionString(R.string.actionChat,
-                                    providerName, false);
-                            host = providerName.toLowerCase();
-                        }
+                    DataKind kind = contactsSource.getKindForMimetype(mimetype);
+                    if (kind == null) {
+                        continue;
+                    }
 
-                        // Only add the intent if there is a valid host
-                        if (!TextUtils.isEmpty(host)) {
-                            entry.intent = new Intent(Intent.ACTION_SENDTO,
-                                    constructImToUrl(host, data));
-                        }
-                        entry.data = data;
-                        //TODO(emillar) Add in presence info
-                        /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
-                            entry.presenceIcon = Presence.getPresenceIconResourceId(
-                                    aggCursor.getInt(METHODS_STATUS_COLUMN));
-                            entry.status = ...
-                        }*/
-                        entry.actionIcon = android.R.drawable.sym_action_chat;
-                        mImEntries.add(entry);
+                    final long id = entryValues.getAsLong(Data._ID);
+                    final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
+                    entry.contactId = contactId;
+                    entry.id = id;
+                    entry.uri = uri;
+                    entry.mimetype = mimetype;
+                    entry.label = buildActionString(kind, entryValues, true);
+                    entry.data = buildDataString(kind, entryValues);
+                    if (kind.typeColumn != null) {
+                        entry.type = entryValues.getAsInteger(kind.typeColumn);
+                    }
+                    if (kind.iconRes > 0) {
+                        entry.actionIcon = kind.iconRes;
                     }
-                // Build organization entries
-                } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
-                    final String company = aggCursor.getString(DATA_3_COLUMN);
-                    final String title = aggCursor.getString(DATA_4_COLUMN);
 
                     // Don't crash if the data is bogus
-                    if (TextUtils.isEmpty(company) && TextUtils.isEmpty(title)) {
+                    if (TextUtils.isEmpty(entry.data)) {
                         Log.w(TAG, "empty data for contact method " + id);
                         continue;
                     }
 
-                    entry.data = title;
-                    entry.actionIcon = R.drawable.sym_action_organization;
-                    entry.label = company;
-
-                    mOrganizationEntries.add(entry);
-                // Build note entries
-                } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
-                    entry.label = getString(R.string.label_notes);
-                    entry.data = aggCursor.getString(DATA_1_COLUMN);
-                    entry.id = 0;
-                    entry.uri = null;
-                    entry.intent = null;
-                    entry.maxLines = 10;
-                    entry.actionIcon = R.drawable.sym_note;
+                    if (!mRawContactIds.contains(entry.contactId)) {
+                        mRawContactIds.add(entry.contactId);
+                    }
 
-                    if (TextUtils.isEmpty(entry.data)) {
-                        Log.w(TAG, "empty data for contact method " + id);
+                    // This performs the tab filtering
+                    if (mSelectedRawContactId != null
+                            && mSelectedRawContactId != entry.contactId
+                            && mSelectedRawContactId != ALL_CONTACTS_ID) {
                         continue;
                     }
 
-                    mOtherEntries.add(entry);
-                }
+                    if (CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimetype)
+                            || CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimetype)
+                            || CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE.equals(mimetype)
+                            || CommonDataKinds.Im.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                        final boolean isSuperPrimary = entryValues.getAsInteger(
+                                Data.IS_SUPER_PRIMARY) != 0;
+
+                        if (CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                            // Build phone entries
+                            mNumPhoneNumbers++;
+
+                            entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                                    Uri.fromParts("tel", entry.data, null));
+                            entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
+                                    Uri.fromParts("sms", entry.data, null));
+                            entry.data = PhoneNumberUtils.stripSeparators(entry.data);
+                            entry.isPrimary = isSuperPrimary;
+                            mPhoneEntries.add(entry);
+
+                            if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
+                                    || mShowSmsLinksForAllPhones) {
+                                // Add an SMS entry
+                                if (kind.iconAltRes > 0) {
+                                    entry.secondaryActionIcon = kind.iconAltRes;
+                                }
+                            }
+                        } else if (CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                            // Build email entries
+                            entry.intent = new Intent(Intent.ACTION_SENDTO,
+                                    Uri.fromParts("mailto", entry.data, null));
+                            entry.isPrimary = isSuperPrimary;
+                            mEmailEntries.add(entry);
+                        } else if (CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE.
+                                equals(mimetype)) {
+                            // Build postal entries
+                            entry.maxLines = 4;
+                            entry.intent = new Intent(Intent.ACTION_VIEW, uri);
+                            mPostalEntries.add(entry);
+                        } else if (CommonDataKinds.Im.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                            // Build im entries
+                            Object protocolObj = entryValues.getAsInteger(Data.DATA5);
+                            String host = null;
+
+                            if (protocolObj instanceof Number) {
+                                int protocol = ((Number) protocolObj).intValue();
+                                host = ContactsUtils.lookupProviderNameFromId(
+                                        protocol).toLowerCase();
+                                if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
+                                        || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
+                                    entry.maxLabelLines = 2;
+                                }
+                            } else if (protocolObj != null) {
+                                String providerName = (String) protocolObj;
+                                host = providerName.toLowerCase();
+                            }
+
+                            // Only add the intent if there is a valid host
+                            if (!TextUtils.isEmpty(host)) {
+                                entry.intent = new Intent(Intent.ACTION_SENDTO,
+                                        constructImToUrl(host, entry.data));
+                            }
+                            //TODO(emillar) Add in presence info
+                            /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
+                            entry.presenceIcon = Presence.getPresenceIconResourceId(
+                                    aggCursor.getInt(METHODS_STATUS_COLUMN));
+                            entry.status = ...
+                        }*/
+                            mImEntries.add(entry);
+                        }
+                    } else if (CommonDataKinds.Organization.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                        // Build organization entries
+                        mOrganizationEntries.add(entry);
+                    } else if (CommonDataKinds.Note.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                        // Build note entries
+                        entry.id = 0;
+                        entry.uri = null;
+                        entry.intent = null;
+                        entry.maxLines = 10;
+                        mOtherEntries.add(entry);
+                    }
 
 
-                // TODO(emillar) Add group entries
-//              // Build the group entries
-//              final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
-//              Cursor groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
-//                      null, null, Groups.DEFAULT_SORT_ORDER);
-//              if (groupCursor != null) {
-//                  try {
-//                      StringBuilder sb = new StringBuilder();
-//
-//                      while (groupCursor.moveToNext()) {
-//                          String systemId = groupCursor.getString(
-//                                  ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
-//
-//                          if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
-//                              continue;
-//                          }
-//
-//                          String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
-//                          if (!TextUtils.isEmpty(name)) {
-//                              if (sb.length() == 0) {
-//                                  sb.append(name);
-//                              } else {
-//                                  sb.append(getString(R.string.group_list, name));
-//                              }
-//                          }
-//                      }
-//
-//                      if (sb.length() > 0) {
-//                          ViewEntry entry = new ViewEntry();
-//                          entry.kind = ContactEntryAdapter.Entry.KIND_GROUP;
-//                          entry.label = getString(R.string.label_groups);
-//                          entry.data = sb.toString();
-//                          entry.intent = new Intent(Intent.ACTION_EDIT, mUri);
-//
-//                          // TODO: Add an icon for the groups item.
-//
-//                          mGroupEntries.add(entry);
-//                      }
-//                  } finally {
-//                      groupCursor.close();
-//                  }
-//              }
+                    // TODO(emillar) Add group entries
+                    //              // Build the group entries
+                    //              final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
+                    //              Cursor groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
+                    //                      null, null, Groups.DEFAULT_SORT_ORDER);
+                    //              if (groupCursor != null) {
+                    //                  try {
+                    //                      StringBuilder sb = new StringBuilder();
+                    //
+                    //                      while (groupCursor.moveToNext()) {
+                    //                          String systemId = groupCursor.getString(
+                    //                                  ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
+                    //
+                    //                          if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
+                    //                              continue;
+                    //                          }
+                    //
+                    //                          String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
+                    //                          if (!TextUtils.isEmpty(name)) {
+                    //                              if (sb.length() == 0) {
+                    //                                  sb.append(name);
+                    //                              } else {
+                    //                                  sb.append(getString(R.string.group_list, name));
+                    //                              }
+                    //                          }
+                    //                      }
+                    //
+                    //                      if (sb.length() > 0) {
+                    //                          ViewEntry entry = new ViewEntry();
+                    //                          entry.kind = ContactEntryAdapter.Entry.KIND_GROUP;
+                    //                          entry.label = getString(R.string.label_groups);
+                    //                          entry.data = sb.toString();
+                    //                          entry.intent = new Intent(Intent.ACTION_EDIT, mUri);
+                    //
+                    //                          // TODO: Add an icon for the groups item.
+                    //
+                    //                          mGroupEntries.add(entry);
+                    //                      }
+                    //                  } finally {
+                    //                      groupCursor.close();
+                    //                  }
+                    //              }
+                }
 
             }
         }
     }
 
-    String buildActionString(int actionResId, CharSequence type, boolean lowerCase) {
-        // If there is no type just display an empty string
-        if (type == null) {
-            type = "";
+    String buildActionString(DataKind kind, ContentValues values, boolean lowerCase) {
+        if (kind.actionHeader == null) {
+            return null;
+        }
+        CharSequence actionHeader = kind.actionHeader.inflateUsing(this, values);
+        if (actionHeader == null) {
+            return null;
         }
+        return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
+    }
 
-        if (lowerCase) {
-            return getString(actionResId, type.toString().toLowerCase());
-        } else {
-            return getString(actionResId, type.toString());
+    String buildDataString(DataKind kind, ContentValues values) {
+        if (kind.actionBody == null) {
+            return null;
         }
+        CharSequence actionBody = kind.actionBody.inflateUsing(this, values);
+        return actionBody == null ? null : actionBody.toString();
     }
 
     /**
index 1ceed41..e623084 100644 (file)
@@ -344,6 +344,7 @@ public class ContactsSource {
      */
     public interface StringInflater {
         public CharSequence inflateUsing(Context context, Cursor cursor);
+        public CharSequence inflateUsing(Context context, ContentValues values);
     }
 
 }
index 87c8432..7a114e0 100644 (file)
@@ -199,6 +199,16 @@ public class EntityModifier {
     }
 
     /**
+     * Find the {@link EditType} that describes the given {@link ContentValues} row,
+     * assuming the given {@link DataKind} dictates the possible types.
+     */
+    public static EditType getCurrentType(ContentValues entry, DataKind kind) {
+        if (kind.typeColumn == null) return null;
+        final int rawValue = entry.getAsInteger(kind.typeColumn);
+        return getType(kind, rawValue);
+    }
+
+    /**
      * Find the {@link EditType} that describes the given {@link Cursor} row,
      * assuming the given {@link DataKind} dictates the possible types.
      */
index acff938..57b1465 100644 (file)
@@ -241,16 +241,23 @@ public class HardCodedSources {
 
             kind.typeColumn = Im.PROTOCOL;
             kind.typeList = Lists.newArrayList();
-            kind.typeList.add(new EditType(Im.PROTOCOL_AIM, R.string.type_im_aim));
-            kind.typeList.add(new EditType(Im.PROTOCOL_MSN, R.string.type_im_msn));
-            kind.typeList.add(new EditType(Im.PROTOCOL_YAHOO, R.string.type_im_yahoo));
-            kind.typeList.add(new EditType(Im.PROTOCOL_SKYPE, R.string.type_im_skype));
-            kind.typeList.add(new EditType(Im.PROTOCOL_QQ, R.string.type_im_qq));
-            kind.typeList.add(new EditType(Im.PROTOCOL_GOOGLE_TALK, R.string.type_im_google_talk));
-            kind.typeList.add(new EditType(Im.PROTOCOL_ICQ, R.string.type_im_icq));
-            kind.typeList.add(new EditType(Im.PROTOCOL_JABBER, R.string.type_im_jabber));
-            kind.typeList.add(new EditType(Im.PROTOCOL_CUSTOM, R.string.type_custom).setSecondary(
-                    true).setCustomColumn(Im.CUSTOM_PROTOCOL));
+            kind.typeList.add(new EditType(Im.PROTOCOL_AIM, R.string.type_im_aim,
+                    R.string.chat_aim));
+            kind.typeList.add(new EditType(Im.PROTOCOL_MSN, R.string.type_im_msn,
+                    R.string.chat_msn));
+            kind.typeList.add(new EditType(Im.PROTOCOL_YAHOO, R.string.type_im_yahoo,
+                    R.string.chat_yahoo));
+            kind.typeList.add(new EditType(Im.PROTOCOL_SKYPE, R.string.type_im_skype,
+                    R.string.chat_skype));
+            kind.typeList.add(new EditType(Im.PROTOCOL_QQ, R.string.type_im_qq, R.string.chat_qq));
+            kind.typeList.add(new EditType(Im.PROTOCOL_GOOGLE_TALK, R.string.type_im_google_talk,
+                    R.string.chat_gtalk));
+            kind.typeList.add(new EditType(Im.PROTOCOL_ICQ, R.string.type_im_icq,
+                    R.string.chat_icq));
+            kind.typeList.add(new EditType(Im.PROTOCOL_JABBER, R.string.type_im_jabber,
+                    R.string.chat_jabber));
+            kind.typeList.add(new EditType(Im.PROTOCOL_CUSTOM, R.string.type_custom,
+                    R.string.chat_other).setSecondary(true).setCustomColumn(Im.CUSTOM_PROTOCOL));
 
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
@@ -627,6 +634,24 @@ public class HardCodedSources {
                 return null;
             }
         }
+
+        public CharSequence inflateUsing(Context context, ContentValues values) {
+            final boolean validColumn = values.containsKey(mColumnName);
+            final boolean validString = mStringRes > 0;
+
+            final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
+            final CharSequence columnValue = validColumn ? values.getAsString(mColumnName) : null;
+
+            if (validString && validColumn) {
+                return String.format(stringValue.toString(), columnValue);
+            } else if (validString) {
+                return stringValue;
+            } else if (validColumn) {
+                return columnValue;
+            } else {
+                return null;
+            }
+        }
     }
 
     /**
@@ -648,6 +673,12 @@ public class HardCodedSources {
             final boolean validString = (type != null && type.actionRes != 0);
             return validString ? context.getText(type.actionRes) : null;
         }
+
+        public CharSequence inflateUsing(Context context, ContentValues values) {
+            final EditType type = EntityModifier.getCurrentType(values, mKind);
+            final boolean validString = (type != null && type.actionRes != 0);
+            return validString ? context.getText(type.actionRes) : null;
+        }
     }
 
     public static class ActionAltInflater implements StringInflater {
@@ -664,5 +695,11 @@ public class HardCodedSources {
             final boolean validString = (type != null && type.actionAltRes != 0);
             return validString ? context.getText(type.actionAltRes) : null;
         }
+
+        public CharSequence inflateUsing(Context context, ContentValues values) {
+            final EditType type = EntityModifier.getCurrentType(values, mKind);
+            final boolean validString = (type != null && type.actionAltRes != 0);
+            return validString ? context.getText(type.actionAltRes) : null;
+        }
     }
 }
index 60dc19b..d5bae7d 100644 (file)
@@ -16,7 +16,6 @@
 
 package com.android.contacts.ui;
 
-import com.android.contacts.BaseContactCardActivity;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
 import com.android.contacts.ScrollingTabWidget;
@@ -337,7 +336,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
             final ContactsSource source = sources.getInflatedSource(accountType,
                     ContactsSource.LEVEL_CONSTRAINTS);
 
-            final View tabView = BaseContactCardActivity.createTabIndicatorView(
+            final View tabView = ContactsUtils.createTabIndicatorView(
                     mTabWidget.getTabParent(), source);
             mTabWidget.addTab(tabView);
         }