am 1a576923: Merge "Check whether the contact exists when saving the contact"

* commit '1a576923d9eaa74f68076227949fdcfade5baa9c':
  Check whether the contact exists when saving the contact
diff --git a/res/layout/confirm_add_detail_activity.xml b/res/layout/confirm_add_detail_activity.xml
index 8826b62..e26c917 100644
--- a/res/layout/confirm_add_detail_activity.xml
+++ b/res/layout/confirm_add_detail_activity.xml
@@ -33,8 +33,7 @@
             android:id="@+id/photo"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:scaleType="centerCrop"
-            android:src="@drawable/ic_contact_picture_180_holo_light" />
+            android:scaleType="centerCrop"/>
 
         <View
             android:id="@+id/photo_text_bar"
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 0a7f557..48af96c 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -29,7 +29,6 @@
             android:id="@+id/photo"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:src="@drawable/ic_contact_picture_holo_light"
             android:cropToPadding="true"
             android:scaleType="centerCrop"
             android:gravity="start"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0d25bb2..7cd8227 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -330,6 +330,11 @@
          was found that could perform the selected action. [CHAR LIMIT=NONE] -->
     <string name="quickcontact_missing_app">No app was found to handle this action.</string>
 
+    <!-- Shown as a toast when the user attempts an action (add contact, edit
+         contact, etc) and no application was found that could perform that
+         action. [CHAR LIMIT=NONE] -->
+    <string name="missing_app">No app was found to handle this action.</string>
+
     <!-- The menu item to share the currently viewed contact [CHAR LIMIT=30] -->
     <string name="menu_share">Share</string>
 
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 7a8f9f3..8688a47 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -410,6 +410,12 @@
                 Log.e(TAG, "Problem persisting user edits", e);
                 break;
 
+            } catch (IllegalArgumentException e) {
+                // This is thrown by applyBatch on malformed requests
+                Log.e(TAG, "Problem persisting user edits", e);
+                showToast(R.string.contactSavedErrorToast);
+                break;
+
             } catch (OperationApplicationException e) {
                 // Version consistency failed, re-parent change and try again
                 Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
@@ -429,7 +435,8 @@
                 sb.append(")");
 
                 if (first) {
-                    throw new IllegalStateException("Version consistency failed for a new contact");
+                    throw new IllegalStateException(
+                            "Version consistency failed for a new contact", e);
                 }
 
                 final RawContactDeltaList newState = RawContactDeltaList.fromQuery(
diff --git a/src/com/android/contacts/SplitAggregateView.java b/src/com/android/contacts/SplitAggregateView.java
index 6e38549..2281ec6 100644
--- a/src/com/android/contacts/SplitAggregateView.java
+++ b/src/com/android/contacts/SplitAggregateView.java
@@ -157,6 +157,9 @@
         Uri dataUri = Uri.withAppendedPath(mAggregateUri, Data.CONTENT_DIRECTORY);
         Cursor cursor = getContext().getContentResolver().query(dataUri,
                 SplitQuery.COLUMNS, null, null, null);
+        if (cursor == null) {
+            return Collections.emptyList();
+        }
         try {
             while (cursor.moveToNext()) {
                 long rawContactId = cursor.getLong(SplitQuery.RAW_CONTACT_ID);
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index c707b62..f184d12 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -239,6 +239,10 @@
             // Just set to the field here.  The listener will be notified by update().
             mCurrentTab = savedState.getInt(EXTRA_KEY_SELECTED_TAB);
         }
+        if (mCurrentTab >= TabState.COUNT || mCurrentTab < 0) {
+            // Invalid tab index was saved (b/12938207). Restore the default.
+            mCurrentTab = TabState.DEFAULT;
+        }
         // Show tabs or the expanded {@link SearchView}, depending on whether or not we are in
         // search mode.
         update();
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index bf79bfa..25c23e0 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -69,6 +69,8 @@
 
     // Height and width (in pixels) to request for the photo - queried from the provider.
     private static int mPhotoDim;
+    // Default photo dimension to use if unable to query the provider.
+    private static final int mDefaultPhotoDim = 720;
 
     private Uri mContactUri;
 
@@ -91,14 +93,21 @@
 
         mContentResolver = getContentResolver();
 
-        // Load the photo dimension to request.
-        Cursor c = mContentResolver.query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
-                new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null);
-        try {
-            c.moveToFirst();
-            mPhotoDim = c.getInt(0);
-        } finally {
-            c.close();
+        // Load the photo dimension to request. mPhotoDim is a static class
+        // member varible so only need to load this if this is the first time
+        // through.
+        if (mPhotoDim == 0) {
+            Cursor c = mContentResolver.query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
+                    new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null);
+            if (c != null) {
+                try {
+                    if (c.moveToFirst()) {
+                        mPhotoDim = c.getInt(0);
+                    }
+                } finally {
+                    c.close();
+                }
+            }
         }
     }
 
@@ -128,28 +137,20 @@
             final Intent myIntent = getIntent();
             final Uri inputUri = myIntent.getData();
 
-            final int perm = checkUriPermission(inputUri, android.os.Process.myPid(),
-                    android.os.Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION |
-                            Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
             final Uri toCrop;
-
-            if (perm == PackageManager.PERMISSION_DENIED) {
-                // Work around to save a read-only URI into a temporary file provider URI so that
-                // we can add the FLAG_GRANT_WRITE_URI_PERMISSION flag to the eventual
-                // crop intent b/10837468
-                ContactPhotoUtils.savePhotoFromUriToUri(this, inputUri, mTempPhotoUri, false);
-                toCrop = mTempPhotoUri;
-            } else {
-                toCrop = inputUri;
-            }
+            // Save the URI into a temporary file provider URI so that
+            // we can add the FLAG_GRANT_WRITE_URI_PERMISSION flag to the eventual
+            // crop intent for read-only URI's.
+            // TODO: With b/10837468 fixed should be able to avoid this copy.
+            ContactPhotoUtils.savePhotoFromUriToUri(this, inputUri, mTempPhotoUri, false);
+            toCrop = mTempPhotoUri;
 
             final Intent intent = new Intent("com.android.camera.action.CROP", toCrop);
             if (myIntent.getStringExtra("mimeType") != null) {
                 intent.setDataAndType(toCrop, myIntent.getStringExtra("mimeType"));
             }
             ContactPhotoUtils.addPhotoPickerExtras(intent, mCroppedPhotoUri);
-            ContactPhotoUtils.addCropExtras(intent, mPhotoDim);
+            ContactPhotoUtils.addCropExtras(intent, mPhotoDim != 0 ? mPhotoDim : mDefaultPhotoDim);
 
             startActivityForResult(intent, REQUEST_CROP_PHOTO);
 
@@ -229,6 +230,10 @@
             Log.w(TAG, "Could not find bitmap");
             return;
         }
+        if (bitmap == null) {
+            Log.w(TAG, "Could not decode bitmap");
+            return;
+        }
 
         final Bitmap scaled = Bitmap.createScaledBitmap(bitmap, size, size, false);
         final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index d60cc73..e613ec1 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -62,6 +62,8 @@
 import com.android.contacts.editor.Editor;
 import com.android.contacts.editor.EditorUiUtils;
 import com.android.contacts.editor.ViewIdGenerator;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.RawContact;
 import com.android.contacts.common.model.RawContactDelta;
@@ -121,6 +123,7 @@
     private Uri mContactUri;
     private long mContactId;
     private String mDisplayName;
+    private String mLookupKey;
     private boolean mIsReadOnly;
 
     private QueryHandler mQueryHandler;
@@ -268,6 +271,9 @@
         // Retrieve references to all the Views in the dialog activity.
         mDisplayNameView = (TextView) findViewById(R.id.name);
         mPhotoView = (ImageView) findViewById(R.id.photo);
+        mPhotoView.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
+                getResources(), false, null));
+
         mEditorContainerView = (ViewGroup) findViewById(R.id.editor_container);
 
         resetAsyncQueryHandler();
@@ -479,6 +485,8 @@
                         if (cursor != null && cursor.moveToFirst()) {
                             // Get the cursor values
                             mDisplayName = cursor.getString(ContactQuery.DISPLAY_NAME);
+                            mLookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
+                            setDefaultContactImage(mDisplayName, mLookupKey);
                             final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
 
                             // If there is no photo ID, then do a disambiguation
@@ -489,8 +497,7 @@
                                 startDisambiguationQuery(mDisplayName);
                             } else {
                                 // Otherwise do the photo query.
-                                Uri lookupUri = Contacts.getLookupUri(mContactId,
-                                        cursor.getString(ContactQuery.LOOKUP_KEY));
+                                Uri lookupUri = Contacts.getLookupUri(mContactId, mLookupKey);
                                 startPhotoQuery(photoId, lookupUri);
                                 // Display the name because there is no
                                 // disambiguation query.
@@ -683,12 +690,15 @@
             // Skip kind that are not editable
             if (!kind.editable) continue;
             if (mMimetype.equals(kind.mimeType)) {
-                for (ValuesDelta valuesDelta : mRawContactDelta.getMimeEntries(mMimetype)) {
-                    // Skip entries that aren't visible
-                    if (!valuesDelta.isVisible()) continue;
-                    if (valuesDelta.isInsert()) {
-                        inflateEditorView(kind, valuesDelta, mRawContactDelta);
-                        return;
+                final ArrayList<ValuesDelta> deltas = mRawContactDelta.getMimeEntries(mMimetype);
+                if (deltas != null) {
+                    for (ValuesDelta valuesDelta : deltas) {
+                        // Skip entries that aren't visible
+                        if (!valuesDelta.isVisible()) continue;
+                        if (valuesDelta.isInsert()) {
+                            inflateEditorView(kind, valuesDelta, mRawContactDelta);
+                            return;
+                        }
                     }
                 }
             }
@@ -734,6 +744,11 @@
         extraTextView.setText(value);
     }
 
+    private void setDefaultContactImage(String displayName, String lookupKey) {
+        mPhotoView.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
+                getResources(), false, new DefaultImageRequest(displayName, lookupKey)));
+    }
+
     /**
      * Shows all the contents of the dialog to the user at one time. This should only be called
      * once all the queries have completed, otherwise the screen will flash as additional data
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 03821a7..a4e0470 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -105,8 +105,6 @@
                     | ActionBar.DISPLAY_SHOW_HOME);
             actionBar.setTitle("");
         }
-
-        Log.i(TAG, getIntent().getData().toString());
     }
 
     @Override
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 1eb610a..9bb7395 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -20,6 +20,7 @@
 import android.app.ActionBar.LayoutParams;
 import android.app.Activity;
 import android.app.Fragment;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -39,6 +40,7 @@
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
+import android.widget.Toast;
 
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
@@ -317,6 +319,7 @@
                 break;
             }
 
+            case ContactsRequest.ACTION_DEFAULT:
             case ContactsRequest.ACTION_PICK_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
                 fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
@@ -538,7 +541,13 @@
         if (extras != null) {
             intent.putExtras(extras);
         }
-        startActivity(intent);
+        try {
+            startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "startActivity() failed: " + e);
+            Toast.makeText(ContactSelectionActivity.this, R.string.missing_app,
+                    Toast.LENGTH_SHORT).show();
+        }
         finish();
     }
 
diff --git a/src/com/android/contacts/activities/PhotoSelectionActivity.java b/src/com/android/contacts/activities/PhotoSelectionActivity.java
index 20892d6..c3b2eeb 100644
--- a/src/com/android/contacts/activities/PhotoSelectionActivity.java
+++ b/src/com/android/contacts/activities/PhotoSelectionActivity.java
@@ -28,7 +28,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.support.v4.content.FileProvider;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.FrameLayout.LayoutParams;
@@ -40,13 +39,8 @@
 import com.android.contacts.detail.PhotoSelectionHandler;
 import com.android.contacts.editor.PhotoActionPopup;
 import com.android.contacts.common.model.RawContactDeltaList;
-import com.android.contacts.util.ContactPhotoUtils;
 import com.android.contacts.util.SchedulingUtils;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-
-
 /**
  * Popup activity for choosing a contact photo within the Contacts app.
  */
@@ -184,6 +178,7 @@
 
         mBackdrop = findViewById(R.id.backdrop);
         mPhotoView = (ImageView) findViewById(R.id.photo);
+
         mSourceBounds = intent.getSourceBounds();
 
         // Fade in the background.
@@ -324,11 +319,11 @@
         if (mPhotoUri != null) {
             // If we have a URI, the bitmap should be cached directly.
             ContactPhotoManager.getInstance(this).loadPhoto(mPhotoView, mPhotoUri, photoWidth,
-                    false);
+                    false, null);
         } else {
-            // Fall back to avatar image.
-            mPhotoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(this, photoWidth,
-                    false));
+            // If we don't have a URI, just display an empty ImageView. The default image from the
+            // ContactDetailFragment will show up in the background instead.
+            mPhotoView.setImageDrawable(null);
         }
 
         mPhotoView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index e1aa217..5adc57a 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -236,8 +236,7 @@
         setDataOrHideIfNone(snippet, statusView);
         if (photoUri != null) {
             ContactPhotoManager.getInstance(context).loadPhoto(
-                    statusPhotoView, Uri.parse(photoUri), -1, false,
-                    ContactPhotoManager.DEFAULT_BLANK);
+                    statusPhotoView, Uri.parse(photoUri), -1, false, null);
             statusPhotoView.setVisibility(View.VISIBLE);
         } else {
             statusPhotoView.setVisibility(View.GONE);
@@ -342,7 +341,7 @@
             pushLayerView.setEnabled(false);
         }
         contactPhotoManager.loadPhoto(imageView, Uri.parse(streamItemPhoto.getPhotoUri()), -1,
-                false, ContactPhotoManager.DEFAULT_BLANK);
+                false, null);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index abaa8eb..629e36b 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -2066,6 +2066,7 @@
                     GroupMembership.CONTENT_ITEM_TYPE);
             final ValuesDelta entry = RawContactModifier.insertChild(rawContactEntityDelta,
                     groupMembershipKind);
+            if (entry == null) return;
             entry.setGroupRowId(defaultGroupId);
 
             // and fire off the intent. we don't need a callback, as the database listener
diff --git a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
index 112621a..437de45 100644
--- a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
+++ b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
@@ -38,8 +38,7 @@
 public class ContactDetailPhotoSetter extends ImageViewDrawableSetter {
     public OnClickListener setupContactPhotoForClick(Context context, Contact contactData,
             ImageView photoView, boolean expandPhotoOnClick) {
-        setTarget(photoView);
-        Bitmap bitmap = setCompressedImage(contactData.getPhotoBinaryData());
+        Bitmap bitmap = setupContactPhoto(contactData, photoView);
         return setupClickListener(context, contactData, bitmap, expandPhotoOnClick);
     }
 
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index 52a292b..d2363de 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -59,6 +59,11 @@
     private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002;
     private static final int REQUEST_CROP_PHOTO = 1003;
 
+    // Height and width (in pixels) to request for the photo - queried from the provider.
+    private static int mPhotoDim;
+    // Default photo dimension to use if unable to query the provider.
+    private static final int mDefaultPhotoDim = 720;
+
     protected final Context mContext;
     private final View mPhotoView;
     private final int mPhotoMode;
@@ -263,15 +268,23 @@
     }
 
     private int getPhotoPickSize() {
+        if (mPhotoDim != 0) {
+            return mPhotoDim;
+        }
+
         // Note that this URI is safe to call on the UI thread.
         Cursor c = mContext.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
                 new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null);
-        try {
-            c.moveToFirst();
-            return c.getInt(0);
-        } finally {
-            c.close();
+        if (c != null) {
+            try {
+                if (c.moveToFirst()) {
+                    mPhotoDim = c.getInt(0);
+                }
+            } finally {
+                c.close();
+            }
         }
+        return mPhotoDim != 0 ? mPhotoDim : mDefaultPhotoDim;
     }
 
     /**
diff --git a/src/com/android/contacts/detail/TransformableImageView.java b/src/com/android/contacts/detail/TransformableImageView.java
index 241df41..1eb35aa 100644
--- a/src/com/android/contacts/detail/TransformableImageView.java
+++ b/src/com/android/contacts/detail/TransformableImageView.java
@@ -40,6 +40,9 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
+        if (getDrawable() == null) {
+            return;
+        }
         int saveCount = canvas.getSaveCount();
         canvas.save();
         canvas.translate(mPaddingLeft, mPaddingTop);
diff --git a/src/com/android/contacts/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
index 2f77858..f121605 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionEngine.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
@@ -300,6 +300,9 @@
     private void loadAggregationSuggestions(Uri uri) {
         ContentResolver contentResolver = mContext.getContentResolver();
         Cursor cursor = contentResolver.query(uri, new String[]{Contacts._ID}, null, null, null);
+        if (cursor == null) {
+            return;
+        }
         try {
             // If a new request is pending, chuck the result of the previous request
             if (getHandler().hasMessages(MESSAGE_NAME_CHANGE)) {
@@ -324,7 +327,9 @@
 
             Cursor dataCursor = contentResolver.query(Data.CONTENT_URI,
                     DataQuery.COLUMNS, sb.toString(), null, Data.CONTACT_ID);
-            mMainHandler.sendMessage(mMainHandler.obtainMessage(MESSAGE_DATA_CURSOR, dataCursor));
+            if (dataCursor != null) {
+                mMainHandler.sendMessage(mMainHandler.obtainMessage(MESSAGE_DATA_CURSOR, dataCursor));
+            }
         } finally {
             cursor.close();
         }
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index 439b1df..a3d04e4 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -28,8 +28,10 @@
 import com.android.contacts.R;
 import com.android.contacts.editor.AggregationSuggestionEngine.RawContact;
 import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
+
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
@@ -86,7 +88,8 @@
             photo.setImageBitmap(BitmapFactory.decodeByteArray(
                     suggestion.photo, 0, suggestion.photo.length));
         } else {
-            photo.setImageResource(R.drawable.ic_contact_picture_holo_light);
+            photo.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
+                    getResources(), false, null));
         }
 
         TextView name = (TextView) findViewById(R.id.aggregation_suggestion_name);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 9243625..54c9d3b 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -1392,14 +1392,14 @@
             String dataSet2 = two.getValues().getAsString(RawContacts.DATA_SET);
             final AccountType type2 = accountTypes.getAccountType(accountType2, dataSet2);
 
-            // Check read-only
+            // Check read-only. Sort read/write before read-only.
             if (!type1.areContactsWritable() && type2.areContactsWritable()) {
                 return 1;
             } else if (type1.areContactsWritable() && !type2.areContactsWritable()) {
                 return -1;
             }
 
-            // Check account type
+            // Check account type. Sort Google before non-Google.
             boolean skipAccountTypeCheck = false;
             boolean isGoogleAccount1 = type1 instanceof GoogleAccountType;
             boolean isGoogleAccount2 = type2 instanceof GoogleAccountType;
@@ -1413,21 +1413,32 @@
 
             int value;
             if (!skipAccountTypeCheck) {
-                if (type1.accountType == null) {
+                // Sort accounts with type before accounts without types.
+                if (type1.accountType != null && type2.accountType == null) {
+                    return -1;
+                } else if (type1.accountType == null && type2.accountType != null) {
                     return 1;
                 }
-                value = type1.accountType.compareTo(type2.accountType);
-                if (value != 0) {
-                    return value;
-                } else {
-                    // Fall back to data set.
-                    if (type1.dataSet != null) {
-                        value = type1.dataSet.compareTo(type2.dataSet);
-                        if (value != 0) {
-                            return value;
-                        }
-                    } else if (type2.dataSet != null) {
-                        return 1;
+
+                if (type1.accountType != null && type2.accountType != null) {
+                    value = type1.accountType.compareTo(type2.accountType);
+                    if (value != 0) {
+                        return value;
+                    }
+                }
+
+                // Fall back to data set. Sort accounts with data sets before
+                // those without.
+                if (type1.dataSet != null && type2.dataSet == null) {
+                    return -1;
+                } else if (type1.dataSet == null && type2.dataSet != null) {
+                    return 1;
+                }
+
+                if (type1.dataSet != null && type2.dataSet != null) {
+                    value = type1.dataSet.compareTo(type2.dataSet);
+                    if (value != 0) {
+                        return value;
                     }
                 }
             }
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index db389fb..bcea53d 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -188,7 +188,7 @@
                 // Ensure that the newly created group is checked.
                 int position = mAdapter.getCount() - 2;
                 ListView listView = mPopup.getListView();
-                if (!listView.isItemChecked(position)) {
+                if (listView != null && !listView.isItemChecked(position)) {
                     // Newly created group is not checked, so check it.
                     listView.setItemChecked(position, true);
                     onItemClick(listView, null, position, listView.getItemIdAtPosition(position));
@@ -282,6 +282,7 @@
     @Override
     public void onClick(View v) {
         if (UiClosables.closeQuietly(mPopup)) {
+            mPopup = null;
             return;
         }
 
@@ -369,7 +370,9 @@
             long groupId = item.getGroupId();
             if (item.isChecked() && !hasMembership(groupId)) {
                 ValuesDelta entry = RawContactModifier.insertChild(mState, mKind);
-                entry.setGroupRowId(groupId);
+                if (entry != null) {
+                    entry.setGroupRowId(groupId);
+                }
             }
         }
 
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index bee61f4..a74e159 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -27,6 +27,7 @@
 
 import com.android.contacts.R;
 import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.ContactsUtils;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
@@ -166,7 +167,8 @@
 
     protected void resetDefault() {
         // Invalid photo, show default "add photo" place-holder
-        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture_holo_light);
+        mPhotoImageView.setImageDrawable(
+                ContactPhotoManager.getDefaultAvatarDrawableForContact(getResources(), false, null));
         mFrameView.setEnabled(!mReadOnly && isEnabled());
         mHasSetPhoto = false;
         mEntry.setFromTemplate(true);
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index c36725c..39fe95a 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -391,7 +391,9 @@
             long defaultGroupId = getDefaultGroupId();
             if (defaultGroupId != -1) {
                 ValuesDelta entry = RawContactModifier.insertChild(mState, mGroupMembershipKind);
-                entry.setGroupRowId(defaultGroupId);
+                if (entry != null) {
+                    entry.setGroupRowId(defaultGroupId);
+                }
             }
         }
     }
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
index d1fa282..36e96a2 100644
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
@@ -199,39 +199,43 @@
         // Phones
         ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
         if (phones != null) {
-            for (int i = 0; i < phones.size(); i++) {
-                ValuesDelta phone = phones.get(i);
-                final String phoneNumber = PhoneNumberUtils.formatNumber(
-                        phone.getPhoneNumber(),
-                        phone.getPhoneNormalizedNumber(),
+            boolean isFirstPhoneBound = true;
+            for (ValuesDelta phone : phones) {
+                final String phoneNumber = phone.getPhoneNumber();
+                if (TextUtils.isEmpty(phoneNumber)) {
+                    continue;
+                }
+                final String formattedNumber = PhoneNumberUtils.formatNumber(
+                        phoneNumber, phone.getPhoneNormalizedNumber(),
                         GeoUtil.getCurrentCountryIso(getContext()));
-                final CharSequence phoneType;
+                CharSequence phoneType = null;
                 if (phone.phoneHasType()) {
                     phoneType = Phone.getTypeLabel(
                             res, phone.getPhoneType(), phone.getPhoneLabel());
-                } else {
-                    phoneType = null;
                 }
-                bindData(mContext.getText(R.string.phoneLabelsGroup), phoneNumber, phoneType,
-                        i == 0, true);
+                bindData(mContext.getText(R.string.phoneLabelsGroup), formattedNumber,
+                        phoneType, isFirstPhoneBound, true);
+                isFirstPhoneBound = false;
             }
         }
 
         // Emails
         ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
         if (emails != null) {
-            for (int i = 0; i < emails.size(); i++) {
-                ValuesDelta email = emails.get(i);
+            boolean isFirstEmailBound = true;
+            for (ValuesDelta email : emails) {
                 final String emailAddress = email.getEmailData();
-                final CharSequence emailType;
+                if (TextUtils.isEmpty(emailAddress)) {
+                    continue;
+                }
+                CharSequence emailType = null;
                 if (email.emailHasType()) {
                     emailType = Email.getTypeLabel(
                             res, email.getEmailType(), email.getEmailLabel());
-                } else {
-                    emailType = null;
                 }
                 bindData(mContext.getText(R.string.emailLabelsGroup), emailAddress, emailType,
-                        i == 0);
+                        isFirstEmailBound);
+                isFirstEmailBound = false;
             }
         }
 
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index f48e0a7..48751e7 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -197,7 +197,7 @@
     private void bindHeaderView(GroupListItem entry, GroupListItemViewCache viewCache) {
         AccountType accountType = mAccountTypeManager.getAccountType(
                 entry.getAccountType(), entry.getDataSet());
-        viewCache.accountType.setText(accountType.getDisplayLabel(mContext).toString());
+        viewCache.accountType.setText(accountType.getDisplayLabel(mContext));
         viewCache.accountName.setText(entry.getAccountName());
     }
 
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 834e2c3..9d46583 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -20,6 +20,7 @@
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.CursorLoader;
@@ -44,6 +45,7 @@
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.ListView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.contacts.GroupMemberLoader;
 import com.android.contacts.GroupMetaDataLoader;
@@ -319,12 +321,18 @@
         if (size == -1) {
             groupSizeString = null;
         } else {
-            String groupSizeTemplateString = getResources().getQuantityString(
-                    R.plurals.num_contacts_in_group, size);
             AccountType accountType = mAccountTypeManager.getAccountType(mAccountTypeString,
                     mDataSet);
-            groupSizeString = String.format(groupSizeTemplateString, size,
-                    accountType.getDisplayLabel(mContext));
+            final CharSequence dispLabel = accountType.getDisplayLabel(mContext);
+            if (!TextUtils.isEmpty(dispLabel)) {
+                String groupSizeTemplateString = getResources().getQuantityString(
+                        R.plurals.num_contacts_in_group, size);
+                groupSizeString = String.format(groupSizeTemplateString, size, dispLabel);
+            } else {
+                String groupSizeTemplateString = getResources().getQuantityString(
+                        R.plurals.group_list_num_contacts_in_group, size);
+                groupSizeString = String.format(groupSizeTemplateString, size);
+            }
         }
 
         if (mGroupSize != null) {
@@ -381,7 +389,13 @@
                     final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                     intent.setClassName(accountType.syncAdapterPackageName,
                             accountType.getViewGroupActivity());
-                    startActivity(intent);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.e(TAG, "startActivity() failed: " + e);
+                        Toast.makeText(getActivity(), R.string.missing_app,
+                                Toast.LENGTH_SHORT).show();
+                    }
                 }
             });
         } else if (mGroupSourceView != null) {
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index acc99f4..91800f8 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -64,6 +64,7 @@
 import com.android.contacts.R;
 import com.android.contacts.activities.GroupEditorActivity;
 import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.editor.SelectAccountDialogFragment;
@@ -71,6 +72,7 @@
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
 import com.android.contacts.common.util.ViewUtil;
+
 import com.google.common.base.Objects;
 
 import java.util.ArrayList;
@@ -849,11 +851,13 @@
         private final Uri mLookupUri;
         private final String mDisplayName;
         private final Uri mPhotoUri;
+        private final String mLookupKey;
 
         public Member(long rawContactId, String lookupKey, long contactId, String displayName,
                 String photoUri) {
             mRawContactId = rawContactId;
             mContactId = contactId;
+            mLookupKey = lookupKey;
             mLookupUri = Contacts.getLookupUri(contactId, lookupKey);
             mDisplayName = displayName;
             mPhotoUri = (photoUri != null) ? Uri.parse(photoUri) : null;
@@ -871,6 +875,10 @@
             return mLookupUri;
         }
 
+        public String getLookupKey() {
+            return mLookupKey;
+        }
+
         public String getDisplayName() {
             return mDisplayName;
         }
@@ -904,6 +912,7 @@
             dest.writeLong(mRawContactId);
             dest.writeLong(mContactId);
             dest.writeParcelable(mLookupUri, flags);
+            dest.writeString(mLookupKey);
             dest.writeString(mDisplayName);
             dest.writeParcelable(mPhotoUri, flags);
         }
@@ -912,6 +921,7 @@
             mRawContactId = in.readLong();
             mContactId = in.readLong();
             mLookupUri = in.readParcelable(getClass().getClassLoader());
+            mLookupKey = in.readString();
             mDisplayName = in.readString();
             mPhotoUri = in.readParcelable(getClass().getClassLoader());
         }
@@ -963,9 +973,10 @@
                     }
                 });
             }
-
+            DefaultImageRequest request = new DefaultImageRequest(member.getDisplayName(),
+                    member.getLookupKey());
             mPhotoManager.loadPhoto(badge, member.getPhotoUri(),
-                    ViewUtil.getConstantPreLayoutWidth(badge), false);
+                    ViewUtil.getConstantPreLayoutWidth(badge), false, request);
             return result;
         }
 
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 067c052..19ff611 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -37,6 +37,7 @@
 import android.widget.TextView;
 
 import com.android.contacts.R;
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
 
 import java.util.ArrayList;
@@ -143,7 +144,8 @@
         }
         byte[] byteArray = member.getPhotoByteArray();
         if (byteArray == null) {
-            icon.setImageResource(R.drawable.ic_contact_picture_holo_light);
+            icon.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
+                    icon.getResources(), false, null));
         } else {
             Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
             icon.setImageBitmap(bitmap);
@@ -263,31 +265,33 @@
                     "=?) AND " + rawContactIdSelectionBuilder.toString(),
                     selectionArgs.toArray(new String[0]), null);
 
-            try {
-                memberDataCursor.moveToPosition(-1);
-                while (memberDataCursor.moveToNext()) {
-                    long rawContactId = memberDataCursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
-                    SuggestedMember member = suggestionsMap.get(rawContactId);
-                    if (member == null) {
-                        continue;
-                    }
-                    String mimetype = memberDataCursor.getString(MIMETYPE_COLUMN_INDEX);
-                    if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                        // Set photo
-                        byte[] bitmapArray = memberDataCursor.getBlob(PHOTO_COLUMN_INDEX);
-                        member.setPhotoByteArray(bitmapArray);
-                    } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype) ||
-                            Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                        // Set at most 1 extra piece of contact info that can be a phone number or
-                        // email
-                        if (!member.hasExtraInfo()) {
-                            String info = memberDataCursor.getString(DATA_COLUMN_INDEX);
-                            member.setExtraInfo(info);
+            if (memberDataCursor != null) {
+                try {
+                    memberDataCursor.moveToPosition(-1);
+                    while (memberDataCursor.moveToNext()) {
+                        long rawContactId = memberDataCursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
+                        SuggestedMember member = suggestionsMap.get(rawContactId);
+                        if (member == null) {
+                            continue;
+                        }
+                        String mimetype = memberDataCursor.getString(MIMETYPE_COLUMN_INDEX);
+                        if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                            // Set photo
+                            byte[] bitmapArray = memberDataCursor.getBlob(PHOTO_COLUMN_INDEX);
+                            member.setPhotoByteArray(bitmapArray);
+                        } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype) ||
+                                Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
+                            // Set at most 1 extra piece of contact info that can be a phone number or
+                            // email
+                            if (!member.hasExtraInfo()) {
+                                String info = memberDataCursor.getString(DATA_COLUMN_INDEX);
+                                member.setExtraInfo(info);
+                            }
                         }
                     }
+                } finally {
+                    memberDataCursor.close();
                 }
-            } finally {
-                memberDataCursor.close();
             }
             results.values = suggestionsList;
             return results;
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
index 4a32ae3..960f29a 100644
--- a/src/com/android/contacts/list/EmailAddressListAdapter.java
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.list.ContactEntryListAdapter;
 import com.android.contacts.common.list.ContactListItemView;
 
@@ -44,7 +45,8 @@
             Email.LABEL,                     // 2
             Email.DATA,                      // 3
             Email.PHOTO_ID,                  // 4
-            Email.DISPLAY_NAME_PRIMARY,      // 5
+            Email.LOOKUP_KEY,                // 5
+            Email.DISPLAY_NAME_PRIMARY,      // 6
         };
 
         private static final String[] PROJECTION_ALTERNATIVE = new String[] {
@@ -53,7 +55,8 @@
             Email.LABEL,                     // 2
             Email.DATA,                      // 3
             Email.PHOTO_ID,                  // 4
-            Email.DISPLAY_NAME_ALTERNATIVE,  // 5
+            Email.LOOKUP_KEY,                // 5
+            Email.DISPLAY_NAME_ALTERNATIVE,  // 6
         };
 
         public static final int EMAIL_ID           = 0;
@@ -61,7 +64,8 @@
         public static final int EMAIL_LABEL        = 2;
         public static final int EMAIL_ADDRESS      = 3;
         public static final int EMAIL_PHOTO_ID     = 4;
-        public static final int EMAIL_DISPLAY_NAME = 5;
+        public static final int EMAIL_LOOKUP_KEY   = 5;
+        public static final int EMAIL_DISPLAY_NAME = 6;
     }
 
     private final CharSequence mUnknownNameText;
@@ -175,8 +179,12 @@
         if (!cursor.isNull(EmailQuery.EMAIL_PHOTO_ID)) {
             photoId = cursor.getLong(EmailQuery.EMAIL_PHOTO_ID);
         }
-
-        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false);
+        DefaultImageRequest request = null;
+        if (photoId == 0) {
+             request = getDefaultImageRequestFromCursor(cursor, EmailQuery.EMAIL_DISPLAY_NAME,
+                    EmailQuery.EMAIL_LOOKUP_KEY);
+        }
+        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, request);
     }
 //
 //    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index a800c00..42c73bc 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -28,6 +28,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.common.list.ContactEntryListAdapter;
 import com.android.contacts.common.list.ContactListItemView;
 
@@ -43,7 +44,8 @@
             StructuredPostal.LABEL,                     // 2
             StructuredPostal.DATA,                      // 3
             StructuredPostal.PHOTO_ID,                  // 4
-            StructuredPostal.DISPLAY_NAME_PRIMARY,      // 5
+            StructuredPostal.LOOKUP_KEY,                // 5
+            StructuredPostal.DISPLAY_NAME_PRIMARY,      // 6
         };
 
         private static final String[] PROJECTION_ALTERNATIVE = new String[] {
@@ -52,7 +54,8 @@
             StructuredPostal.LABEL,                     // 2
             StructuredPostal.DATA,                      // 3
             StructuredPostal.PHOTO_ID,                  // 4
-            StructuredPostal.DISPLAY_NAME_ALTERNATIVE,  // 5
+            StructuredPostal.LOOKUP_KEY,                // 5
+            StructuredPostal.DISPLAY_NAME_ALTERNATIVE,  // 6
         };
 
         public static final int POSTAL_ID           = 0;
@@ -60,7 +63,8 @@
         public static final int POSTAL_LABEL        = 2;
         public static final int POSTAL_ADDRESS      = 3;
         public static final int POSTAL_PHOTO_ID     = 4;
-        public static final int POSTAL_DISPLAY_NAME = 5;
+        public static final int POSTAL_LOOKUP_KEY   = 5;
+        public static final int POSTAL_DISPLAY_NAME = 6;
     }
 
     private final CharSequence mUnknownNameText;
@@ -166,7 +170,13 @@
             photoId = cursor.getLong(PostalQuery.POSTAL_PHOTO_ID);
         }
 
-        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false);
+        DefaultImageRequest request = null;
+        if (photoId == 0) {
+            request = getDefaultImageRequestFromCursor(cursor, PostalQuery.POSTAL_DISPLAY_NAME,
+                    PostalQuery.POSTAL_LOOKUP_KEY);
+        }
+
+        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, request);
     }
 //
 //    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 0ff7a8d..e950fcb 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -312,6 +312,7 @@
                                 @Override
                                 public void run() {
                                     finish();
+                                    overridePendingTransition(0, 0);
                                 }
                             }, POST_DRAW_WAIT_DURATION);
                         }
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
index 82a6820..fe792d7 100644
--- a/src/com/android/contacts/util/ContactBadgeUtil.java
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -110,6 +110,6 @@
 
     public static Bitmap loadDefaultAvatarPhoto(Context context, boolean hires, boolean darkTheme) {
         return BitmapFactory.decodeResource(context.getResources(),
-                ContactPhotoManager.getDefaultAvatarResId(hires, darkTheme));
+                R.drawable.ic_contacts_holo_dark);
     }
 }
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index a189f58..4dfa66c 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -23,10 +23,14 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.text.TextUtils;
 import android.util.Log;
 import android.widget.ImageView;
 
 import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
 import com.android.contacts.common.model.Contact;
 
 import java.util.Arrays;
@@ -40,6 +44,7 @@
     private byte[] mCompressed;
     private Drawable mPreviousDrawable;
     private int mDurationInMillis = 0;
+    private Contact mContact;
     private static final String TAG = "ImageViewDrawableSetter";
 
     public ImageViewDrawableSetter() {
@@ -49,9 +54,10 @@
         mTarget = target;
     }
 
-    public void setupContactPhoto(Contact contactData, ImageView photoView) {
+    public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
+        mContact = contactData;
         setTarget(photoView);
-        setCompressedImage(contactData.getPhotoBinaryData());
+        return setCompressedImage(contactData.getPhotoBinaryData());
     }
 
     public void setTransitionDuration(int durationInMillis) {
@@ -122,23 +128,33 @@
     }
 
     private Bitmap previousBitmap() {
-        return (mPreviousDrawable == null)
-                ? null
+        return (mPreviousDrawable == null) ? null
+                : mPreviousDrawable instanceof LetterTileDrawable ? null
                 : ((BitmapDrawable) mPreviousDrawable).getBitmap();
     }
 
     /**
-     * Obtain the default drawable for a contact when no photo is available.
+     * Obtain the default drawable for a contact when no photo is available. If this is a local
+     * contact, then use the contact's display name and lookup key (as a unique identifier) to
+     * retrieve a default drawable for this contact. If not, then use the name as the contact
+     * identifier instead.
      */
     private Drawable defaultDrawable() {
         Resources resources = mTarget.getResources();
-        final int resId = ContactPhotoManager.getDefaultAvatarResId(true, false);
-        try {
-            return resources.getDrawable(resId);
-        } catch (NotFoundException e) {
-            Log.wtf(TAG, "Cannot load default avatar resource.");
-            return null;
+        DefaultImageRequest request;
+        int contactType = ContactPhotoManager.TYPE_DEFAULT;
+
+        if (mContact.isDisplayNameFromOrganization()) {
+            contactType = ContactPhotoManager.TYPE_BUSINESS;
         }
+
+        if (TextUtils.isEmpty(mContact.getLookupKey())) {
+            request = new DefaultImageRequest(null, mContact.getDisplayName(), contactType);
+        } else {
+            request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
+                    contactType);
+        }
+        return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
     }
 
     private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
@@ -146,5 +162,4 @@
         Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
         return new BitmapDrawable(rsrc, bitmap);
     }
-
 }