New avatars

Bug:5074147

Change-Id: I583d22c63a7b617567c523efeed292ae1e2be7e4
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 7d9b758..b7ccffc 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -611,7 +611,7 @@
 
     /** Load the contact photos and places them in the corresponding views. */
     private void loadContactPhotos(Uri photoUri) {
-        mContactPhotoManager.loadPhoto(mContactBackgroundView, photoUri);
+        mContactPhotoManager.loadPhoto(mContactBackgroundView, photoUri, true, true);
     }
 
     private String getVoicemailNumber() {
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index eb9531a..b2ceffa 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -17,6 +17,7 @@
 package com.android.contacts;
 
 import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.UriUtils;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
@@ -59,11 +60,12 @@
 
     public static final String CONTACT_PHOTO_SERVICE = "contactPhotos";
 
-    /**
-     * The resource ID of the image to be used when the photo is unavailable or being
-     * loaded.
-     */
-    protected final int mDefaultResourceId = R.drawable.ic_contact_picture;
+    public static int getDefaultAvatarResId(boolean hires, boolean darkTheme) {
+        if (hires && darkTheme) return R.drawable.ic_contact_picture_180_holo_dark;
+        if (hires) return R.drawable.ic_contact_picture_180_holo_light;
+        if (darkTheme) return R.drawable.ic_contact_picture_holo_dark;
+        return R.drawable.ic_contact_picture_holo_light;
+    }
 
     /**
      * Requests the singleton instance of {@link AccountTypeManager} with data bound from
@@ -89,14 +91,14 @@
      * it is displayed immediately.  Otherwise a request is sent to load the photo
      * from the database.
      */
-    public abstract void loadPhoto(ImageView view, long photoId);
+    public abstract void loadPhoto(ImageView view, long photoId, boolean hires, boolean darkTheme);
 
     /**
      * Load photo into the supplied image view.  If the photo is already cached,
      * it is displayed immediately.  Otherwise a request is sent to load the photo
      * from the location specified by the URI.
      */
-    public abstract void loadPhoto(ImageView view, Uri photoUri);
+    public abstract void loadPhoto(ImageView view, Uri photoUri, boolean hires, boolean darkTheme);
 
     /**
      * Remove photo from the supplied image view. This also cancels current pending load request
@@ -185,11 +187,11 @@
     private final LruCache<Object, Bitmap> mBitmapCache;
 
     /**
-     * A map from ImageView to the corresponding photo ID. Please note that this
-     * photo ID may change before the photo loading request is started.
+     * A map from ImageView to the corresponding photo ID or uri, encapsulated in a request.
+     * The request may swapped out before the photo loading request is started.
      */
-    private final ConcurrentHashMap<ImageView, Object> mPendingRequests =
-            new ConcurrentHashMap<ImageView, Object>();
+    private final ConcurrentHashMap<ImageView, Request> mPendingRequests =
+            new ConcurrentHashMap<ImageView, Request>();
 
     /**
      * Handler for messages sent to the UI thread.
@@ -234,33 +236,33 @@
     }
 
     @Override
-    public void loadPhoto(ImageView view, long photoId) {
+    public void loadPhoto(ImageView view, long photoId, boolean hires, boolean darkTheme) {
         if (photoId == 0) {
             // No photo is needed
-            view.setImageResource(mDefaultResourceId);
+            view.setImageResource(getDefaultAvatarResId(hires, darkTheme));
             mPendingRequests.remove(view);
         } else {
-            loadPhotoByIdOrUri(view, photoId);
+            loadPhotoByIdOrUri(view, Request.createFromId(photoId, hires, darkTheme));
         }
     }
 
     @Override
-    public void loadPhoto(ImageView view, Uri photoUri) {
+    public void loadPhoto(ImageView view, Uri photoUri, boolean hires, boolean darkTheme) {
         if (photoUri == null) {
             // No photo is needed
-            view.setImageResource(mDefaultResourceId);
+            view.setImageResource(getDefaultAvatarResId(hires, darkTheme));
             mPendingRequests.remove(view);
         } else {
-            loadPhotoByIdOrUri(view, photoUri);
+            loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, hires, darkTheme));
         }
     }
 
-    private void loadPhotoByIdOrUri(ImageView view, Object key) {
-        boolean loaded = loadCachedPhoto(view, key);
+    private void loadPhotoByIdOrUri(ImageView view, Request request) {
+        boolean loaded = loadCachedPhoto(view, request);
         if (loaded) {
             mPendingRequests.remove(view);
         } else {
-            mPendingRequests.put(view, key);
+            mPendingRequests.put(view, request);
             if (!mPaused) {
                 // Send a request to start loading photos
                 requestLoading();
@@ -286,16 +288,16 @@
      *
      * @return false if the photo needs to be (re)loaded from the provider.
      */
-    private boolean loadCachedPhoto(ImageView view, Object key) {
-        BitmapHolder holder = mBitmapHolderCache.get(key);
+    private boolean loadCachedPhoto(ImageView view, Request request) {
+        BitmapHolder holder = mBitmapHolderCache.get(request.getKey());
         if (holder == null) {
             // The bitmap has not been loaded - should display the placeholder image.
-            view.setImageResource(mDefaultResourceId);
+            view.setImageResource(getDefaultAvatarResId(request.isHires(), request.isDarkTheme()));
             return false;
         }
 
         if (holder.bytes == null) {
-            view.setImageResource(mDefaultResourceId);
+            view.setImageResource(getDefaultAvatarResId(request.isHires(), request.isDarkTheme()));
             return holder.fresh;
         }
 
@@ -305,7 +307,7 @@
         view.setImageBitmap(holder.bitmap);
 
         // Put the bitmap in the LRU cache
-        mBitmapCache.put(key, holder.bitmap);
+        mBitmapCache.put(request, holder.bitmap);
 
         // Soften the reference
         holder.bitmap = null;
@@ -376,6 +378,7 @@
     /**
      * Processes requests on the main thread.
      */
+    @Override
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
             case MESSAGE_REQUEST_LOADING: {
@@ -412,7 +415,7 @@
         Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
         while (iterator.hasNext()) {
             ImageView view = iterator.next();
-            Object key = mPendingRequests.get(view);
+            Request key = mPendingRequests.get(view);
             boolean loaded = loadCachedPhoto(view, key);
             if (loaded) {
                 iterator.remove();
@@ -469,16 +472,16 @@
          * concurrent change, we will need to check the map again once loading
          * is complete.
          */
-        Iterator<Object> iterator = mPendingRequests.values().iterator();
+        Iterator<Request> iterator = mPendingRequests.values().iterator();
         while (iterator.hasNext()) {
-            Object key = iterator.next();
-            BitmapHolder holder = mBitmapHolderCache.get(key);
+            Request request = iterator.next();
+            BitmapHolder holder = mBitmapHolderCache.get(request);
             if (holder == null || !holder.fresh) {
-                if (key instanceof Long) {
-                    photoIds.add((Long)key);
-                    photoIdsAsStrings.add(key.toString());
+                if (request.isUriRequest()) {
+                    uris.add(request.mUri);
                 } else {
-                    uris.add((Uri)key);
+                    photoIds.add(request.mId);
+                    photoIdsAsStrings.add(String.valueOf(request.mId));
                 }
             }
         }
@@ -571,6 +574,7 @@
          * Receives the above message, loads photos and then sends a message
          * to the main thread to process them.
          */
+        @Override
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MESSAGE_PRELOAD_PHOTOS:
@@ -777,4 +781,65 @@
             }
         }
     }
+
+    /**
+     * A holder for either a Uri or an id and a flag whether this was requested for the dark or
+     * light theme
+     */
+    private static final class Request {
+        private final long mId;
+        private final Uri mUri;
+        private final boolean mDarkTheme;
+        private final boolean mHires;
+
+        private Request(long id, Uri uri, boolean hires, boolean darkTheme) {
+            mId = id;
+            mUri = uri;
+            mDarkTheme = darkTheme;
+            mHires = hires;
+        }
+
+        public static Request createFromId(long id, boolean hires, boolean darkTheme) {
+            return new Request(id, null /* no URI */, hires, darkTheme);
+        }
+
+        public static Request createFromUri(Uri uri, boolean hires, boolean darkTheme) {
+            return new Request(0 /* no ID */, uri, hires, darkTheme);
+        }
+
+        public boolean isDarkTheme() {
+            return mDarkTheme;
+        }
+
+        public boolean isHires() {
+            return mHires;
+        }
+
+        public boolean isUriRequest() {
+            return mUri != null;
+        }
+
+        @Override
+        public int hashCode() {
+            if (mUri != null) return mUri.hashCode();
+
+            // copied over from Long.hashCode()
+            return (int) (mId ^ (mId >>> 32));
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Request)) return false;
+            final Request that = (Request) o;
+            // Don't compare equality of mHires and mDarkTheme fields because these are only used
+            // in the default contact photo case. When the contact does have a photo, the contact
+            // photo is the same regardless of mHires and mDarkTheme, so we shouldn't need to put
+            // the photo request on the queue twice.
+            return mId == that.mId && UriUtils.areEqual(mUri, that.mUri);
+        }
+
+        public Object getKey() {
+            return mUri == null ? mId : mUri;
+        }
+    }
 }
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 25f6fc2..e13eecb 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -482,6 +482,7 @@
             mSearchFragment = (PhoneNumberPickerFragment) fragment;
             mSearchFragment.setOnPhoneNumberPickerActionListener(mPhoneNumberPickerActionListener);
             mSearchFragment.setQuickContactEnabled(true);
+            mSearchFragment.setDarkTheme(true);
             final FragmentTransaction transaction = getFragmentManager().beginTransaction();
             if (mInSearchUi) {
                 transaction.show(mSearchFragment);
diff --git a/src/com/android/contacts/calllog/CallLogAdapter.java b/src/com/android/contacts/calllog/CallLogAdapter.java
index 4223146..0bbf53c 100644
--- a/src/com/android/contacts/calllog/CallLogAdapter.java
+++ b/src/com/android/contacts/calllog/CallLogAdapter.java
@@ -776,7 +776,7 @@
 
     private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri) {
         views.quickContactView.assignContactUri(contactUri);
-        mContactPhotoManager.loadPhoto(views.quickContactView, photoId);
+        mContactPhotoManager.loadPhoto(views.quickContactView, photoId, false, true);
     }
 
     /**
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index f6690d6..bdcd6b0 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -200,7 +200,7 @@
         }
         byte[] photo = contactData.getPhotoBinaryData();
         Bitmap bitmap = photo != null ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
-                : ContactBadgeUtil.loadPlaceholderPhoto(context);
+                : ContactBadgeUtil.loadDefaultAvatarPhoto(context, true, false);
         boolean fadeIn = contactData.isDirectoryEntry();
         if (photoView.getDrawable() == null && fadeIn) {
             AlphaAnimation animation = new AlphaAnimation(0, 1);
@@ -249,7 +249,7 @@
         setDataOrHideIfNone(snippet, statusView);
         if (photoUri != null) {
             ContactPhotoManager.getInstance(context).loadPhoto(
-                    statusPhotoView, Uri.parse(photoUri));
+                    statusPhotoView, Uri.parse(photoUri), true, false);
             statusPhotoView.setVisibility(View.VISIBLE);
         } else {
             statusPhotoView.setVisibility(View.GONE);
@@ -338,7 +338,8 @@
             pushLayerView.setClickable(false);
             pushLayerView.setEnabled(false);
         }
-        contactPhotoManager.loadPhoto(imageView, Uri.parse(streamItemPhoto.getPhotoUri()));
+        contactPhotoManager.loadPhoto(imageView, Uri.parse(streamItemPhoto.getPhotoUri()), true,
+                false);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index df90cff..996dbc4 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -86,7 +86,7 @@
             photo.setImageBitmap(BitmapFactory.decodeByteArray(
                     suggestion.photo, 0, suggestion.photo.length));
         } else {
-            photo.setImageResource(R.drawable.ic_contact_picture);
+            photo.setImageResource(R.drawable.ic_contact_picture_holo_light);
         }
 
         TextView name = (TextView) findViewById(R.id.aggregation_suggestion_name);
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index b9b8c2c..086b07f 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -162,7 +162,7 @@
 
     protected void resetDefault() {
         // Invalid photo, show default "add photo" place-holder
-        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture_holo_light);
         mFrameView.setEnabled(!mReadOnly && isEnabled());
         mHasSetPhoto = false;
         mEntry.setFromTemplate(true);
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 96e0c8a..99e6b48 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -971,7 +971,7 @@
                 });
             }
 
-            mPhotoManager.loadPhoto(badge, member.getPhotoUri());
+            mPhotoManager.loadPhoto(badge, member.getPhotoUri(), false, false);
             return result;
         }
 
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 623b5a2..08401bf 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -143,7 +143,7 @@
         }
         byte[] byteArray = member.getPhotoByteArray();
         if (byteArray == null) {
-            icon.setImageResource(R.drawable.ic_contact_picture);
+            icon.setImageResource(R.drawable.ic_contact_picture_holo_light);
         } else {
             Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
             icon.setImageBitmap(bitmap);
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index 9b968f7..9c36f05 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -90,6 +90,7 @@
 
     private ContactListFilter mFilter;
     private String mContactsCount = "";
+    private boolean mDarkTheme = false;
 
     public ContactEntryListAdapter(Context context) {
         super(context);
@@ -306,6 +307,10 @@
         return mProfileExists;
     }
 
+    public void setDarkTheme(boolean value) {
+        mDarkTheme = value;
+    }
+
     public void configureDirectoryLoader(DirectoryListLoader loader) {
         loader.setDirectorySearchMode(mDirectorySearchMode);
         loader.setLocalInvisibleDirectoryEnabled(LOCAL_INVISIBLE_DIRECTORY_ENABLED);
@@ -607,7 +612,7 @@
         QuickContactBadge quickContact = view.getQuickContact();
         quickContact.assignContactUri(
                 getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
-        getPhotoLoader().loadPhoto(quickContact, photoId);
+        getPhotoLoader().loadPhoto(quickContact, photoId, false, mDarkTheme);
     }
 
     protected Uri getContactUri(int partitionIndex, Cursor cursor,
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index e361a19..91c582c 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -19,7 +19,6 @@
 import com.android.common.widget.CompositeCursorAdapter.Partition;
 import com.android.contacts.ContactListEmptyView;
 import com.android.contacts.ContactPhotoManager;
-import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.R;
 import com.android.contacts.preference.ContactsPreferences;
 import com.android.contacts.widget.ContextMenuAdapter;
@@ -86,6 +85,7 @@
     private static final String KEY_DIRECTORY_SEARCH_MODE = "directorySearchMode";
     private static final String KEY_SELECTION_VISIBLE = "selectionVisible";
     private static final String KEY_REQUEST = "request";
+    private static final String KEY_DARK_THEME = "darkTheme";
     private static final String KEY_LEGACY_COMPATIBILITY = "legacyCompatibility";
     private static final String KEY_DIRECTORY_RESULT_LIMIT = "directoryResultLimit";
 
@@ -134,6 +134,8 @@
 
     private boolean mForceLoad;
 
+    private boolean mDarkTheme;
+
     protected boolean mUserProfileExists;
 
     private static final int STATUS_NOT_LOADED = 0;
@@ -161,7 +163,6 @@
         }
     };
 
-
     protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
     protected abstract T createListAdapter();
 
@@ -249,6 +250,7 @@
         outState.putString(KEY_QUERY_STRING, mQueryString);
         outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
         outState.putParcelable(KEY_REQUEST, mRequest);
+        outState.putBoolean(KEY_DARK_THEME, mDarkTheme);
 
         if (mListView != null) {
             mListState = mListView.onSaveInstanceState();
@@ -281,6 +283,7 @@
         mQueryString = savedState.getString(KEY_QUERY_STRING);
         mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
         mRequest = savedState.getParcelable(KEY_REQUEST);
+        mDarkTheme = savedState.getBoolean(KEY_DARK_THEME);
 
         // Retrieve list state. This will be applied in onLoadFinished
         mListState = savedState.getParcelable(KEY_LIST_STATE);
@@ -812,6 +815,7 @@
         mAdapter.setSectionHeaderDisplayEnabled(mSectionHeaderDisplayEnabled);
         mAdapter.setSelectionVisible(mSelectionVisible);
         mAdapter.setDirectoryResultLimit(mDirectoryResultLimit);
+        mAdapter.setDarkTheme(mDarkTheme);
     }
 
     @Override
@@ -920,6 +924,11 @@
         return telephonyManager.hasIccCard();
     }
 
+    public void setDarkTheme(boolean value) {
+        mDarkTheme = value;
+        if (mAdapter != null) mAdapter.setDarkTheme(value);
+    }
+
     /**
      * Processes a result returned by the contact picker.
      */
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 0553909..6eb9423 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -255,11 +255,11 @@
         }
 
         if (photoId != 0) {
-            getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+            getPhotoLoader().loadPhoto(view.getPhotoView(), photoId, false, false);
         } else {
             final String photoUriString = cursor.getString(CONTACT_PHOTO_URI_COLUMN_INDEX);
             final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
-            getPhotoLoader().loadPhoto(view.getPhotoView(), photoUri);
+            getPhotoLoader().loadPhoto(view.getPhotoView(), photoUri, false, false);
         }
     }
 
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index a6e241f..da062f3 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -48,11 +48,6 @@
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * A custom view for an item in the contact list.
  * The view contains the contact's photo, a set of text views (for name, status, etc...) and
diff --git a/src/com/android/contacts/list/ContactTileDarkFrequentView.java b/src/com/android/contacts/list/ContactTileDarkFrequentView.java
new file mode 100644
index 0000000..78f45bd
--- /dev/null
+++ b/src/com/android/contacts/list/ContactTileDarkFrequentView.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A dark version of the {@link ContactTileView} (This class is needed to load the proper avatar)
+ */
+public class ContactTileDarkFrequentView extends ContactTileView {
+    public ContactTileDarkFrequentView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected boolean isDarkTheme() {
+        return true;
+    }
+}
diff --git a/src/com/android/contacts/list/ContactTileSecondaryTargetView.java b/src/com/android/contacts/list/ContactTileSecondaryTargetView.java
index 073ac29..c4ea212 100644
--- a/src/com/android/contacts/list/ContactTileSecondaryTargetView.java
+++ b/src/com/android/contacts/list/ContactTileSecondaryTargetView.java
@@ -50,4 +50,9 @@
             }
         });
     }
+
+    @Override
+    protected boolean isDarkTheme() {
+        return true;
+    }
 }
diff --git a/src/com/android/contacts/list/ContactTileStarredView.java b/src/com/android/contacts/list/ContactTileStarredView.java
index c017731..3be6bf2 100644
--- a/src/com/android/contacts/list/ContactTileStarredView.java
+++ b/src/com/android/contacts/list/ContactTileStarredView.java
@@ -22,9 +22,6 @@
  * A {@link ContactTileStarredView} displays the contact's picture overlayed with their name
  * in a square.  The actual dimensions are set by
  * {@link com.android.contacts.list.ContactTileAdapter.ContactTileRow}.
- *
- * TODO Just remove this class.  We probably don't need {@link ContactTileSecondaryTargetView}
- * either.  (We can probably put the functionality to {@link ContactTileView})
  */
 public class ContactTileStarredView extends ContactTileView {
     private final static String TAG = ContactTileStarredView.class.getSimpleName();
@@ -32,4 +29,9 @@
     public ContactTileStarredView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
+
+    @Override
+    protected boolean isDefaultIconHires() {
+        return true;
+    }
 }
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 137f6e7..ddfb241 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -118,14 +118,16 @@
 
             if (mPhotoManager != null) {
                 if (mPhoto != null) {
-                    mPhotoManager.loadPhoto(mPhoto, entry.photoUri);
+                    mPhotoManager.loadPhoto(mPhoto, entry.photoUri, isDefaultIconHires(),
+                            isDarkTheme());
 
                     if (mQuickContact != null) {
                         mQuickContact.assignContactUri(mLookupUri);
                     }
                 } else if (mQuickContact != null) {
                     mQuickContact.assignContactUri(mLookupUri);
-                    mPhotoManager.loadPhoto(mQuickContact, entry.photoUri);
+                    mPhotoManager.loadPhoto(mQuickContact, entry.photoUri, isDefaultIconHires(),
+                            isDarkTheme());
                 }
 
             } else {
@@ -148,6 +150,14 @@
         return mLookupUri;
     }
 
+    protected boolean isDefaultIconHires() {
+        return false;
+    }
+
+    protected boolean isDarkTheme() {
+        return false;
+    }
+
     public interface Listener {
         void onClick(ContactTileView contactTileView);
     }
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
index 5f96297..52daaa0 100644
--- a/src/com/android/contacts/list/EmailAddressListAdapter.java
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -175,7 +175,7 @@
             photoId = cursor.getLong(EMAIL_PHOTO_ID_COLUMN_INDEX);
         }
 
-        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId, false, false);
     }
 //
 //    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index a86e082..5c12dd6 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -313,6 +313,6 @@
             photoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
         }
 
-        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId, false, false);
     }
 }
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index 88e5ecf..938d43a 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -15,7 +15,6 @@
  */
 package com.android.contacts.list;
 
-import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.R;
 import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index c9da281..7e58a8e 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -164,7 +164,7 @@
             photoId = cursor.getLong(POSTAL_PHOTO_ID_COLUMN_INDEX);
         }
 
-        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId, false, false);
     }
 //
 //    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/ShortcutIntentBuilder.java b/src/com/android/contacts/list/ShortcutIntentBuilder.java
index 3aa0b55..886732c 100644
--- a/src/com/android/contacts/list/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/list/ShortcutIntentBuilder.java
@@ -224,7 +224,7 @@
             bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
         } else {
             bitmap = ((BitmapDrawable) mContext.getResources().getDrawableForDensity(
-                    R.drawable.ic_contact_picture, mIconDensity)).getBitmap();
+                    R.drawable.ic_contact_picture_holo_light, mIconDensity)).getBitmap();
         }
 
         Intent shortcutIntent;
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 472f701..fbca129 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -17,11 +17,11 @@
 package com.android.contacts.quickcontact;
 
 import com.android.contacts.Collapser;
+import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.R;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.DataKind;
-import com.android.contacts.util.ContactBadgeUtil;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.util.NotifyingAsyncQueryHandler;
 import com.android.contacts.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
@@ -491,8 +491,11 @@
 
         if (photoView != null) {
             // Place photo when discovered in data, otherwise show generic avatar
-            photoView.setImageBitmap(photoBitmap != null ? photoBitmap
-                    : ContactBadgeUtil.loadPlaceholderPhoto(context));
+            if (photoBitmap != null) {
+                photoView.setImageBitmap(photoBitmap);
+            } else {
+                photoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(true, false));
+            }
         }
 
         // TODO: Bring this back once we have a design
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index 9d064a5..9f7e18f 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -136,12 +136,12 @@
         if (contactData.isError() || contactData == ContactLoader.Result.NOT_FOUND) {
             setDisplayNameAndSnippet(context, views,
                     context.getString(R.string.invalidContactMessage), null, null, null);
-            setPhoto(views, ContactBadgeUtil.loadPlaceholderPhoto(context));
+            setPhoto(views, ContactBadgeUtil.loadDefaultAvatarPhoto(context, false, false));
         } else {
             byte[] photo = contactData.getPhotoBinaryData();
             setPhoto(views, photo != null
                     ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
-                            : ContactBadgeUtil.loadPlaceholderPhoto(context));
+                            : ContactBadgeUtil.loadDefaultAvatarPhoto(context, false, false));
 
             // TODO: Rotate between all the stream items?
 
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
index a89177a..de90c40 100644
--- a/src/com/android/contacts/util/ContactBadgeUtil.java
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.util;
 
+import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.R;
 
 import android.content.Context;
@@ -106,7 +107,8 @@
         return attribution;
     }
 
-    public static Bitmap loadPlaceholderPhoto(Context context) {
-        return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture);
+    public static Bitmap loadDefaultAvatarPhoto(Context context, boolean hires, boolean darkTheme) {
+        return BitmapFactory.decodeResource(context.getResources(),
+                ContactPhotoManager.getDefaultAvatarResId(hires, darkTheme));
     }
 }