summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYorke Lee <yorkelee@google.com>2014-02-12 14:58:25 -0800
committerYorke Lee <yorkelee@google.com>2014-02-28 12:23:44 -0800
commit6084726fbdda78bdb16e2d4cc1c3b81c84fd5da1 (patch)
treed8f528c76a3342bdafde89bc0907fff8b660d02b
parent2105200e6414a7a35596f792af08a8841333a41d (diff)
downloadandroid_packages_apps_ContactsCommon-6084726fbdda78bdb16e2d4cc1c3b81c84fd5da1.tar.gz
android_packages_apps_ContactsCommon-6084726fbdda78bdb16e2d4cc1c3b81c84fd5da1.tar.bz2
android_packages_apps_ContactsCommon-6084726fbdda78bdb16e2d4cc1c3b81c84fd5da1.zip
Add support for letter tile avatars to ContactsPhotoManager
* Add LetterTileDrawable, a custom drawable used to display letter tiles for contacts without a contact photo, instead of static bitmap resource drawables. * Add a class DefaultImageRequest to ContactPhotoManager. This is essentially a data holder object used to store and retrieve various parameters that clients can use to configure the way they want their requested letter tile avatars to be drawn. * Add LetterTileImageProvider to ContactPhotoManager. LetterTileImageProvider extends DefaultImageProvider, but returns instances of LetterTileDrawable instead of BitmapDrawables when a default contact image is needed. * Add two new APIs to ContactPhotoManager: getDefaultAvatarForContact - This returns an instance of a LetterTileDrawable, configured with the display parameters stored in the provided DefaultImageRequest. getDefaultAvatarUriForContact - This returns an uri which the ContactPhotoManager can use to recreate a DefaultImageRequest and hence a LetterTileDrawable. * Modify ContactPhotoManager.loadPhoto and loadThumbnail to accept a DefaultImageRequest as a parameter. * Modify the following classes to use the new ContactPhotoManager APIs. These affect both the Dialer and People apps. ContactEntryListADapter ContactListAdapter ContactTileView PhoneNumberListAdapter Bug: 13101785 Change-Id: Id1a87b459d6e63c42049739059a3b3ee202af837
-rw-r--r--TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java8
-rw-r--r--res/drawable-hdpi/ic_contact_picture_180_holo_dark.pngbin2326 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_contact_picture_180_holo_light.pngbin5927 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_contact_picture_holo_dark.pngbin804 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_contact_picture_holo_light.pngbin2273 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_list_item_avatar.pngbin0 -> 1845 bytes
-rw-r--r--res/drawable-hdpi/ic_list_item_businessavatar.pngbin0 -> 1671 bytes
-rw-r--r--res/drawable-hdpi/ic_voicemail_avatar.pngbin0 -> 1930 bytes
-rw-r--r--res/drawable-mdpi/ic_contact_picture_180_holo_dark.pngbin1495 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_contact_picture_180_holo_light.pngbin3967 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_contact_picture_holo_dark.pngbin589 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_contact_picture_holo_light.pngbin1623 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_list_item_avatar.pngbin0 -> 1294 bytes
-rw-r--r--res/drawable-mdpi/ic_list_item_businessavatar.pngbin0 -> 1310 bytes
-rw-r--r--res/drawable-mdpi/ic_voicemail_avatar.pngbin0 -> 1314 bytes
-rw-r--r--res/drawable-xhdpi/ic_contact_picture_180_holo_dark.pngbin3220 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_contact_picture_180_holo_light.pngbin7750 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_contact_picture_holo_dark.pngbin1091 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_contact_picture_holo_light.pngbin2969 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_list_item_avatar.pngbin0 -> 2513 bytes
-rw-r--r--res/drawable-xhdpi/ic_list_item_businessavatar.pngbin0 -> 1846 bytes
-rw-r--r--res/drawable-xhdpi/ic_voicemail_avatar.pngbin0 -> 2648 bytes
-rw-r--r--res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.pngbin6357 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_contact_picture_180_holo_light.pngbin8700 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_contact_picture_holo_dark.pngbin2841 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_contact_picture_holo_light.pngbin4240 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_list_item_avatar.pngbin0 -> 5433 bytes
-rw-r--r--res/drawable-xxhdpi/ic_list_item_businessavatar.pngbin0 -> 3457 bytes
-rw-r--r--res/drawable-xxhdpi/ic_voicemail_avatar.pngbin0 -> 3607 bytes
-rw-r--r--res/values/colors.xml15
-rw-r--r--res/values/dimens.xml6
-rw-r--r--res/values/ids.xml5
-rw-r--r--res/values/strings.xml3
-rw-r--r--src/com/android/contacts/common/ContactPhotoManager.java309
-rw-r--r--src/com/android/contacts/common/lettertiles/LetterTileDrawable.java247
-rw-r--r--src/com/android/contacts/common/list/ContactEntry.java3
-rw-r--r--src/com/android/contacts/common/list/ContactEntryListAdapter.java31
-rw-r--r--src/com/android/contacts/common/list/ContactListAdapter.java12
-rw-r--r--src/com/android/contacts/common/list/ContactTileAdapter.java3
-rw-r--r--src/com/android/contacts/common/list/ContactTileView.java22
-rw-r--r--src/com/android/contacts/common/list/DefaultContactListAdapter.java2
-rw-r--r--src/com/android/contacts/common/list/PhoneNumberListAdapter.java14
-rw-r--r--src/com/android/contacts/common/model/Contact.java8
43 files changed, 623 insertions, 65 deletions
diff --git a/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java b/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java
index 13986264..32fb8997 100644
--- a/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java
+++ b/TestCommon/src/com/android/contacts/common/test/mocks/MockContactPhotoManager.java
@@ -29,14 +29,14 @@ import com.android.contacts.common.ContactPhotoManager;
public class MockContactPhotoManager extends ContactPhotoManager {
@Override
public void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
- DefaultImageProvider defaultProvider) {
- defaultProvider.applyDefaultImage(view, -1, darkTheme);
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+ defaultProvider.applyDefaultImage(view, -1, darkTheme, null);
}
@Override
public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
- DefaultImageProvider defaultProvider) {
- defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme);
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, null);
}
@Override
diff --git a/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index a17da613..00000000
--- a/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png b/res/drawable-hdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index 7195b070..00000000
--- a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_holo_dark.png b/res/drawable-hdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index 314fa007..00000000
--- a/res/drawable-hdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_holo_light.png b/res/drawable-hdpi/ic_contact_picture_holo_light.png
deleted file mode 100644
index 87447e5f..00000000
--- a/res/drawable-hdpi/ic_contact_picture_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_list_item_avatar.png b/res/drawable-hdpi/ic_list_item_avatar.png
new file mode 100644
index 00000000..0a239d02
--- /dev/null
+++ b/res/drawable-hdpi/ic_list_item_avatar.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_list_item_businessavatar.png b/res/drawable-hdpi/ic_list_item_businessavatar.png
new file mode 100644
index 00000000..c943fc9a
--- /dev/null
+++ b/res/drawable-hdpi/ic_list_item_businessavatar.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_voicemail_avatar.png b/res/drawable-hdpi/ic_voicemail_avatar.png
new file mode 100644
index 00000000..2fb78261
--- /dev/null
+++ b/res/drawable-hdpi/ic_voicemail_avatar.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index acba3339..00000000
--- a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png b/res/drawable-mdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index 70d96977..00000000
--- a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index 68767774..00000000
--- a/res/drawable-mdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_holo_light.png b/res/drawable-mdpi/ic_contact_picture_holo_light.png
deleted file mode 100644
index c2aef96f..00000000
--- a/res/drawable-mdpi/ic_contact_picture_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_list_item_avatar.png b/res/drawable-mdpi/ic_list_item_avatar.png
new file mode 100644
index 00000000..871212b9
--- /dev/null
+++ b/res/drawable-mdpi/ic_list_item_avatar.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_list_item_businessavatar.png b/res/drawable-mdpi/ic_list_item_businessavatar.png
new file mode 100644
index 00000000..43135eaa
--- /dev/null
+++ b/res/drawable-mdpi/ic_list_item_businessavatar.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_voicemail_avatar.png b/res/drawable-mdpi/ic_voicemail_avatar.png
new file mode 100644
index 00000000..4005f24f
--- /dev/null
+++ b/res/drawable-mdpi/ic_voicemail_avatar.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index c4c001ec..00000000
--- a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index 989844eb..00000000
--- a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index ddf797fc..00000000
--- a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_holo_light.png b/res/drawable-xhdpi/ic_contact_picture_holo_light.png
deleted file mode 100644
index 914d85ce..00000000
--- a/res/drawable-xhdpi/ic_contact_picture_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_list_item_avatar.png b/res/drawable-xhdpi/ic_list_item_avatar.png
new file mode 100644
index 00000000..2a73a2e2
--- /dev/null
+++ b/res/drawable-xhdpi/ic_list_item_avatar.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_list_item_businessavatar.png b/res/drawable-xhdpi/ic_list_item_businessavatar.png
new file mode 100644
index 00000000..14742b56
--- /dev/null
+++ b/res/drawable-xhdpi/ic_list_item_businessavatar.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_voicemail_avatar.png b/res/drawable-xhdpi/ic_voicemail_avatar.png
new file mode 100644
index 00000000..f24505d3
--- /dev/null
+++ b/res/drawable-xhdpi/ic_voicemail_avatar.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index 6e057ac3..00000000
--- a/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png b/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index b04c82f1..00000000
--- a/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png b/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index 52a69c37..00000000
--- a/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_picture_holo_light.png b/res/drawable-xxhdpi/ic_contact_picture_holo_light.png
deleted file mode 100644
index 9150df2d..00000000
--- a/res/drawable-xxhdpi/ic_contact_picture_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_list_item_avatar.png b/res/drawable-xxhdpi/ic_list_item_avatar.png
new file mode 100644
index 00000000..5665631e
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_list_item_avatar.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_list_item_businessavatar.png b/res/drawable-xxhdpi/ic_list_item_businessavatar.png
new file mode 100644
index 00000000..484591c7
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_list_item_businessavatar.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_voicemail_avatar.png b/res/drawable-xxhdpi/ic_voicemail_avatar.png
new file mode 100644
index 00000000..182def8d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_voicemail_avatar.png
Binary files differ
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 65abd95c..3e5127e0 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -47,4 +47,19 @@
<color name="textColorIconOverlay">#fff</color>
<color name="textColorIconOverlayShadow">#000</color>
+
+ <!-- Make sure to also update LetterTileProvider#NUM_OF_TILE_COLORS when adding or removing colors -->
+ <array name="letter_tile_colors">
+ <item>#f16364</item>
+ <item>#f58559</item>
+ <item>#f9a43e</item>
+ <item>#e4c62e</item>
+ <item>#67bf74</item>
+ <item>#59a2be</item>
+ <item>#2093cd</item>
+ <item>#ad62a7</item>
+ </array>
+ <color name="letter_tile_default_color">#cccccc</color>
+
+ <color name="letter_tile_font_color">#ffffff</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d407da39..efe56ffb 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,4 +81,10 @@
-->
<dimen name="contact_phone_list_empty_description_size">20sp</dimen>
<dimen name="contact_phone_list_empty_description_padding">10dip</dimen>
+
+ <!-- Dimensions for contact letter tiles -->
+ <dimen name="tile_letter_font_size">40dp</dimen>
+ <dimen name="tile_letter_font_size_small">20dp</dimen>
+ <dimen name="tile_divider_width">1dp</dimen>
+ <item name="letter_to_tile_ratio" type="dimen">67%</item>
</resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 9a731952..093901ba 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -40,4 +40,9 @@
<item type="id" name="cliv_phoneticname_textview"/>
<item type="id" name="cliv_label_textview"/>
<item type="id" name="cliv_data_view"/>
+
+ <!-- For tag ids used by ContactPhotoManager to tag views with contact details -->
+ <item type="id" name="tag_display_name"/>
+ <item type="id" name="tag_identifier"/>
+ <item type="id" name="tag_contact_type"/>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d4bb1ff9..c63fb33d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -726,4 +726,7 @@ a ren't members of any other group. [CHAR LIMIT=25] -->
<!-- Attribution of a contact status update, when the time of update is known -->
<string name="contact_status_update_attribution_with_date"><xliff:g id="date" example="3 hours ago">%1$s</xliff:g> via <xliff:g id="source" example="Google Talk">%2$s</xliff:g></string>
+ <!-- Font family used when drawing letters for letter tile avatars.
+ Do not translate. -->
+ <string name="letter_tile_letter_font_family">sans-serif-thin</string>
</resources>
diff --git a/src/com/android/contacts/common/ContactPhotoManager.java b/src/com/android/contacts/common/ContactPhotoManager.java
index c0be7e89..ce02a0bd 100644
--- a/src/com/android/contacts/common/ContactPhotoManager.java
+++ b/src/com/android/contacts/common/ContactPhotoManager.java
@@ -34,6 +34,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
+import android.net.Uri.Builder;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.HandlerThread;
@@ -49,8 +50,10 @@ import android.util.LruCache;
import android.util.TypedValue;
import android.widget.ImageView;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
import com.android.contacts.common.util.BitmapUtil;
import com.android.contacts.common.util.UriUtils;
+
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -73,48 +76,231 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 {
static final boolean DEBUG = false; // Don't submit with true
static final boolean DEBUG_SIZES = false; // Don't submit with true
- /** Caches 180dip in pixel. This is used to detect whether to show the hires or lores version
- * of the default avatar */
- private static int s180DipInPixel = -1;
+ /** Contact type constants used for default letter images */
+ public static final int TYPE_PERSON = LetterTileDrawable.TYPE_PERSON;
+ public static final int TYPE_BUSINESS = LetterTileDrawable.TYPE_BUSINESS;
+ public static final int TYPE_VOICEMAIL = LetterTileDrawable.TYPE_VOICEMAIL;
+ public static final int TYPE_DEFAULT = LetterTileDrawable.TYPE_DEFAULT;
+
+ /** Scale and offset default constants used for default letter images */
+ public static final float SCALE_DEFAULT = 1.0f;
+ public static final float OFFSET_DEFAULT = 0.0f;
+
+ /** Uri-related constants used for default letter images */
+ private static final String DISPLAY_NAME_PARAM_KEY = "display_name";
+ private static final String IDENTIFIER_PARAM_KEY = "identifier";
+ private static final String CONTACT_TYPE_PARAM_KEY = "contact_type";
+ private static final String SCALE_PARAM_KEY = "scale";
+ private static final String OFFSET_PARAM_KEY = "offset";
+ private static final String DEFAULT_IMAGE_URI_SCHEME = "defaultimage";
+ private static final Uri DEFAULT_IMAGE_URI = Uri.parse(DEFAULT_IMAGE_URI_SCHEME + "://");
public static final String CONTACT_PHOTO_SERVICE = "contactPhotos";
+ // Static field used to cache the default letter avatar drawable that is created
+ // using a null {@link DefaultImageRequest}
+ private static Drawable sDefaultLetterAvatar = null;
+
/**
- * Returns the resource id of the default avatar. Tries to find a resource that is bigger
- * than the given extent (width or height). If extent=-1, a thumbnail avatar is returned
+ * Given a {@link DefaultImageRequest}, returns a {@link Drawable}, that when drawn, will
+ * draw a letter tile avatar based on the request parameters defined in the
+ * {@link DefaultImageRequest}.
+ */
+ public static Drawable getDefaultAvatarDrawableForContact(Resources resources, boolean hires,
+ DefaultImageRequest defaultImageRequest) {
+ if (defaultImageRequest == null) {
+ if (sDefaultLetterAvatar == null) {
+ // Cache and return the letter tile drawable that is created by a null request,
+ // so that it doesn't have to be recreated every time it is requested again.
+ sDefaultLetterAvatar = LetterTileDefaultImageProvider.getDefaultImageForContact(
+ resources, null);
+ }
+ return sDefaultLetterAvatar;
+ }
+ return LetterTileDefaultImageProvider.getDefaultImageForContact(resources,
+ defaultImageRequest);
+ }
+
+ /**
+ * Given a {@link DefaultImageRequest}, returns an Uri that can be used to request a
+ * letter tile avatar when passed to the {@link ContactPhotoManager}. The internal
+ * implementation of this uri is not guaranteed to remain the same across application
+ * versions, so the actual uri should never be persisted in long-term storage and reused.
+ *
+ * @param request A {@link DefaultImageRequest} object with the fields configured
+ * to return a
+ * @return A Uri that when later passed to the {@link ContactPhotoManager} via
+ * {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest)}, can be
+ * used to request a default contact image, drawn as a letter tile using the
+ * parameters as configured in the provided {@link DefaultImageRequest}
*/
- public static int getDefaultAvatarResId(Context context, int extent, boolean darkTheme) {
- // TODO: Is it worth finding a nicer way to do hires/lores here? In practice, the
- // default avatar doesn't look too different when stretched
- if (s180DipInPixel == -1) {
- Resources r = context.getResources();
- s180DipInPixel = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 180,
- r.getDisplayMetrics());
+ public static Uri getDefaultAvatarUriForContact(DefaultImageRequest request) {
+ final Builder builder = DEFAULT_IMAGE_URI.buildUpon();
+ if (request != null) {
+ if (!TextUtils.isEmpty(request.displayName)) {
+ builder.appendQueryParameter(DISPLAY_NAME_PARAM_KEY, request.displayName);
+ }
+ if (!TextUtils.isEmpty(request.identifier)) {
+ builder.appendQueryParameter(IDENTIFIER_PARAM_KEY, request.identifier);
+ }
+ if (request.contactType != TYPE_DEFAULT) {
+ builder.appendQueryParameter(CONTACT_TYPE_PARAM_KEY,
+ String.valueOf(request.contactType));
+ }
+ if (request.scale != SCALE_DEFAULT) {
+ builder.appendQueryParameter(SCALE_PARAM_KEY, String.valueOf(request.scale));
+ }
+ if (request.offset != OFFSET_DEFAULT) {
+ builder.appendQueryParameter(OFFSET_PARAM_KEY, String.valueOf(request.offset));
+ }
+
+ }
+ return builder.build();
+ }
+
+ protected static DefaultImageRequest getDefaultImageRequestFromUri(Uri uri) {
+ final DefaultImageRequest request = new DefaultImageRequest(
+ uri.getQueryParameter(DISPLAY_NAME_PARAM_KEY),
+ uri.getQueryParameter(IDENTIFIER_PARAM_KEY));
+ try {
+ String contactType = uri.getQueryParameter(CONTACT_TYPE_PARAM_KEY);
+ if (!TextUtils.isEmpty(contactType)) {
+ request.contactType = Integer.valueOf(contactType);
+ }
+
+ String scale = uri.getQueryParameter(SCALE_PARAM_KEY);
+ if (!TextUtils.isEmpty(scale)) {
+ request.scale = Float.valueOf(scale);
+ }
+
+ String offset = uri.getQueryParameter(OFFSET_PARAM_KEY);
+ if (!TextUtils.isEmpty(offset)) {
+ request.offset = Float.valueOf(offset);
+ }
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid DefaultImageRequest image parameters provided, ignoring and using "
+ + "defaults.");
}
- final boolean hires = (extent != -1) && (extent > s180DipInPixel);
- return getDefaultAvatarResId(hires, darkTheme);
+ return request;
}
- 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;
+ protected boolean isDefaultImageUri(Uri uri) {
+ return DEFAULT_IMAGE_URI_SCHEME.equals(uri.getScheme());
+ }
+
+ /**
+ * Contains fields used to contain contact details and other user-defined settings that might
+ * be used by the ContactPhotoManager to generate a default contact image. This contact image
+ * takes the form of a letter or bitmap drawn on top of a colored tile.
+ */
+ public static class DefaultImageRequest {
+ /**
+ * The contact's display name. The display name is used to
+ */
+ public String displayName;
+ /**
+ * A unique and deterministic string that can be used to identify this contact. This is
+ * usually the contact's lookup key, but other contact details can be used as well,
+ * especially for non-local or temporary contacts that might not have a lookup key. This
+ * is used to determine the color of the tile.
+ */
+ public String identifier;
+ /**
+ * The type of this contact. This contact type may be used to decide the kind of
+ * image to use in the case where a unique letter cannot be generated from the contact's
+ * display name and identifier. See:
+ * {@link #TYPE_PERSON}
+ * {@link #TYPE_BUSINESS}
+ * {@link #TYPE_PERSON}
+ * {@link #TYPE_DEFAULT}
+ */
+ public int contactType = TYPE_DEFAULT;
+ /**
+ * The amount to scale the letter or bitmap to, as a ratio of its default size (from a
+ * range of 0.0f to 2.0f). The default value is 1.0f.
+ */
+ public float scale = SCALE_DEFAULT;
+ /**
+ * The amount to vertically offset the letter or image to within the tile.
+ * The provided offset must be within the range of -0.5f to 0.5f.
+ * If set to -0.5f, the letter will be shifted upwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the top edge of the canvas.
+ * If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of the
+ * canvas it is being drawn on, which means it will be drawn with the center of the letter
+ * starting at the bottom edge of the canvas.
+ * The default is 0.0f, which means the letter is drawn in the exact vertical center of
+ * the tile.
+ */
+ public float offset = OFFSET_DEFAULT;
+
+ public DefaultImageRequest() {
+ }
+
+ public DefaultImageRequest(String displayName, String identifier) {
+ this(displayName, identifier, TYPE_DEFAULT, SCALE_DEFAULT, OFFSET_DEFAULT);
+ }
+
+ public DefaultImageRequest(String displayName, String identifier, int contactType) {
+ this(displayName, identifier, contactType, SCALE_DEFAULT, OFFSET_DEFAULT);
+ }
+
+ public DefaultImageRequest(String displayName, String identifier, int contactType,
+ float scale, float offset) {
+ this.displayName = displayName;
+ this.identifier = identifier;
+ this.contactType = contactType;
+ this.scale = scale;
+ this.offset = offset;
+ }
}
public static abstract class DefaultImageProvider {
/**
* Applies the default avatar to the ImageView. Extent is an indicator for the size (width
* or height). If darkTheme is set, the avatar is one that looks better on dark background
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a
+ * default letter tile avatar should be drawn.
*/
- public abstract void applyDefaultImage(ImageView view, int extent, boolean darkTheme);
+ public abstract void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest);
}
- private static class AvatarDefaultImageProvider extends DefaultImageProvider {
+ /**
+ * A default image provider that applies a letter tile consisting of a colored background
+ * and a letter in the foreground as the default image for a contact. The color of the
+ * background and the type of letter is decided based on the contact's details.
+ */
+ private static class LetterTileDefaultImageProvider extends DefaultImageProvider {
@Override
- public void applyDefaultImage(ImageView view, int extent, boolean darkTheme) {
- view.setImageResource(getDefaultAvatarResId(view.getContext(), extent, darkTheme));
+ public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
+ final Drawable drawable = getDefaultImageForContact(view.getResources(),
+ defaultImageRequest);
+ view.setImageDrawable(drawable);
+ }
+
+ public static Drawable getDefaultImageForContact(Resources resources,
+ DefaultImageRequest defaultImageRequest) {
+ final LetterTileDrawable drawable = new LetterTileDrawable(resources);
+ if (defaultImageRequest != null) {
+ // If the contact identifier is null or empty, fallback to the
+ // displayName. In that case, use {@code null} for the contact's
+ // display name so that a default bitmap will be used instead of a
+ // letter
+ if (TextUtils.isEmpty(defaultImageRequest.identifier)) {
+ drawable.setContactDetails(null, defaultImageRequest.displayName);
+ } else {
+ drawable.setContactDetails(defaultImageRequest.displayName,
+ defaultImageRequest.identifier);
+ }
+ drawable.setContactType(defaultImageRequest.contactType);
+ drawable.setScale(defaultImageRequest.scale);
+ drawable.setOffset(defaultImageRequest.offset);
+ }
+ return drawable;
}
}
@@ -122,7 +308,8 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 {
private static Drawable sDrawable;
@Override
- public void applyDefaultImage(ImageView view, int extent, boolean darkTheme) {
+ public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
if (sDrawable == null) {
Context context = view.getContext();
sDrawable = new ColorDrawable(context.getResources().getColor(
@@ -132,7 +319,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 {
}
}
- public static final DefaultImageProvider DEFAULT_AVATAR = new AvatarDefaultImageProvider();
+ public static DefaultImageProvider DEFAULT_AVATAR = new LetterTileDefaultImageProvider();
public static final DefaultImageProvider DEFAULT_BLANK = new BlankDefaultImageProvider();
@@ -157,20 +344,23 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 {
* from the database.
*/
public abstract void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
- DefaultImageProvider defaultProvider);
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider);
/**
- * Calls {@link #loadThumbnail(ImageView, long, boolean, DefaultImageProvider)} with
- * {@link #DEFAULT_AVATAR}.
- */
- public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme) {
- loadThumbnail(view, photoId, darkTheme, DEFAULT_AVATAR);
+ * Calls {@link #loadThumbnail(ImageView, long, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} using the {@link DefaultImageProvider} {@link #DEFAULT_AVATAR}.
+ */
+ public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
+ loadThumbnail(view, photoId, darkTheme, defaultImageRequest, DEFAULT_AVATAR);
}
+
/**
* 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.
+ *
* @param view The target view
* @param photoUri The uri of the photo to load
* @param requestedExtent Specifies an approximate Max(width, height) of the targetView.
@@ -178,27 +368,39 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 {
* is done using efficient sampling. If requestedExtent is specified, no sampling of the image
* is performed
* @param darkTheme Whether the background is dark. This is used for default avatars
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
* @param defaultProvider The provider of default avatars (this is used if photoUri doesn't
* refer to an existing image)
*/
public abstract void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
- boolean darkTheme, DefaultImageProvider defaultProvider);
+ boolean darkTheme, DefaultImageRequest defaultImageRequest,
+ DefaultImageProvider defaultProvider);
/**
- * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageProvider)} with
- * {@link #DEFAULT_AVATAR}.
+ * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and {@code null} display names and
+ * lookup keys.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
*/
public final void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
- boolean darkTheme) {
- loadPhoto(view, photoUri, requestedExtent, darkTheme, DEFAULT_AVATAR);
+ boolean darkTheme, DefaultImageRequest defaultImageRequest) {
+ loadPhoto(view, photoUri, requestedExtent, darkTheme, defaultImageRequest, DEFAULT_AVATAR);
}
/**
- * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageProvider)} with
- * {@link #DEFAULT_AVATAR} and with the assumption, that the image is a thumbnail
+ * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageRequest,
+ * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and with the assumption, that
+ * the image is a thumbnail.
+ *
+ * @param defaultImageRequest {@link DefaultImageRequest} object that specifies how a default
+ * letter tile avatar should be drawn.
*/
- public final void loadDirectoryPhoto(ImageView view, Uri photoUri, boolean darkTheme) {
- loadPhoto(view, photoUri, -1, darkTheme, DEFAULT_AVATAR);
+ public final void loadDirectoryPhoto(ImageView view, Uri photoUri, boolean darkTheme,
+ DefaultImageRequest defaultImageRequest) {
+ loadPhoto(view, photoUri, -1, darkTheme, defaultImageRequest, DEFAULT_AVATAR);
}
/**
@@ -468,10 +670,10 @@ class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
@Override
public void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
- DefaultImageProvider defaultProvider) {
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
if (photoId == 0) {
// No photo is needed
- defaultProvider.applyDefaultImage(view, -1, darkTheme);
+ defaultProvider.applyDefaultImage(view, -1, darkTheme, defaultImageRequest);
mPendingRequests.remove(view);
} else {
if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoId);
@@ -482,18 +684,31 @@ class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
@Override
public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
- DefaultImageProvider defaultProvider) {
+ DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
if (photoUri == null) {
// No photo is needed
- defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme);
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme,
+ defaultImageRequest);
mPendingRequests.remove(view);
} else {
if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoUri);
- loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, requestedExtent, darkTheme,
- defaultProvider));
+ if (isDefaultImageUri(photoUri)) {
+ createAndApplyDefaultImageForUri(view, photoUri, requestedExtent, darkTheme,
+ defaultProvider);
+ } else {
+
+ loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, requestedExtent,
+ darkTheme, defaultProvider));
+ }
}
}
+ private void createAndApplyDefaultImageForUri(ImageView view, Uri uri, int requestedExtent,
+ boolean darkTheme, DefaultImageProvider defaultProvider) {
+ DefaultImageRequest request = getDefaultImageRequestFromUri(uri);
+ defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, request);
+ }
+
private void loadPhotoByIdOrUri(ImageView view, Request request) {
boolean loaded = loadCachedPhoto(view, request, false);
if (loaded) {
@@ -1220,7 +1435,7 @@ class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
}
public void applyDefaultImage(ImageView view) {
- mDefaultProvider.applyDefaultImage(view, mRequestedExtent, mDarkTheme);
+ mDefaultProvider.applyDefaultImage(view, mRequestedExtent, mDarkTheme, null);
}
}
}
diff --git a/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java b/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java
new file mode 100644
index 00000000..9adcca69
--- /dev/null
+++ b/src/com/android/contacts/common/lettertiles/LetterTileDrawable.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2013 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.common.lettertiles;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.util.BitmapUtil;
+
+import junit.framework.Assert;
+
+/**
+ * A drawable that encapsulates all the functionality needed to display a letter tile to
+ * represent a contact image.
+ */
+public class LetterTileDrawable extends Drawable {
+
+ private final String TAG = LetterTileDrawable.class.getSimpleName();
+
+ private final Paint mPaint;
+
+ /** Letter tile */
+ private static TypedArray sColors;
+ private static int sDefaultColor;
+ private static int sTileFontColor;
+ private static float sLetterToTileRatio;
+ private static Bitmap DEFAULT_PERSON_AVATAR;
+ private static Bitmap DEFAULT_BUSINESS_AVATAR;
+ private static Bitmap DEFAULT_VOICEMAIL_AVATAR;
+
+ /** Reusable components to avoid new allocations */
+ private static final Paint sPaint = new Paint();
+ private static final Rect sRect = new Rect();
+ private static final char[] sFirstChar = new char[1];
+
+ /** Contact type constants */
+ public static final int TYPE_PERSON = 1;
+ public static final int TYPE_BUSINESS = 2;
+ public static final int TYPE_VOICEMAIL = 3;
+ public static final int TYPE_DEFAULT = TYPE_PERSON;
+
+ private String mDisplayName;
+ private String mIdentifier;
+ private int mContactType = TYPE_DEFAULT;
+ private float mScale = 1.0f;
+ private float mOffset = 0.0f;
+
+ /** This should match the total number of colors defined in colors.xml for letter_tile_color */
+ private static final int NUM_OF_TILE_COLORS = 8;
+
+ public LetterTileDrawable(final Resources res) {
+ mPaint = new Paint();
+ mPaint.setFilterBitmap(true);
+ mPaint.setDither(true);
+
+ if (sColors == null) {
+ sColors = res.obtainTypedArray(R.array.letter_tile_colors);
+ sDefaultColor = res.getColor(R.color.letter_tile_default_color);
+ sTileFontColor = res.getColor(R.color.letter_tile_font_color);
+ sLetterToTileRatio = res.getFraction(R.dimen.letter_to_tile_ratio, 1, 1);
+ DEFAULT_PERSON_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_list_item_avatar);
+ DEFAULT_BUSINESS_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_list_item_businessavatar);
+ DEFAULT_VOICEMAIL_AVATAR = BitmapFactory.decodeResource(res,
+ R.drawable.ic_voicemail_avatar);
+ sPaint.setTypeface(Typeface.create(
+ res.getString(R.string.letter_tile_letter_font_family), Typeface.NORMAL));
+ sPaint.setTextAlign(Align.CENTER);
+ sPaint.setAntiAlias(true);
+ }
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Rect bounds = getBounds();
+ if (!isVisible() || bounds.isEmpty()) {
+ return;
+ }
+ // Draw letter tile.
+ drawLetterTile(canvas);
+ }
+
+ /**
+ * Draw the bitmap onto the canvas at the current bounds taking into account the current scale.
+ */
+ private void drawBitmap(final Bitmap bitmap, final int width, final int height,
+ final Canvas canvas) {
+ // The bitmap should be drawn in the middle of the canvas without changing its width to
+ // height ratio.
+ final Rect destRect = copyBounds();
+
+ // Crop the destination bounds into a square, scaled and offset as appropriate
+ final int halfLength = (int) (mScale * Math.min(destRect.width(), destRect.height()) / 2);
+
+ destRect.set(destRect.centerX() - halfLength,
+ (int) (destRect.centerY() - halfLength + mOffset * destRect.height()),
+ destRect.centerX() + halfLength,
+ (int) (destRect.centerY() + halfLength + mOffset * destRect.height()));
+
+ // Source rectangle remains the entire bounds of the source bitmap.
+ sRect.set(0, 0, width, height);
+
+ canvas.drawBitmap(bitmap, sRect, destRect, mPaint);
+ }
+
+ private void drawLetterTile(final Canvas canvas) {
+ // Draw background color.
+ sPaint.setColor(pickColor(mIdentifier));
+
+ sPaint.setAlpha(mPaint.getAlpha());
+ canvas.drawRect(getBounds(), sPaint);
+
+ // Draw letter/digit only if the first character is an english letter
+ if (mDisplayName != null && isEnglishLetter(mDisplayName.charAt(0))) {
+ // Draw letter or digit.
+ sFirstChar[0] = Character.toUpperCase(mDisplayName.charAt(0));
+
+ // Scale text by canvas bounds and user selected scaling factor
+ final int minDimension = Math.min(getBounds().width(), getBounds().height());
+ sPaint.setTextSize(mScale * sLetterToTileRatio * minDimension);
+ //sPaint.setTextSize(sTileLetterFontSize);
+ sPaint.getTextBounds(sFirstChar, 0, 1, sRect);
+ sPaint.setColor(sTileFontColor);
+ final Rect bounds = getBounds();
+
+ // Draw the letter in the canvas, vertically shifted up or down by the user-defined
+ // offset
+ canvas.drawText(sFirstChar, 0, 1, bounds.centerX(),
+ bounds.centerY() + mOffset * bounds.height() + sRect.height() / 2,
+ sPaint);
+ } else {
+ // Draw the default image if there is no letter/digit to be drawn
+ final Bitmap bitmap = getBitmapForContactType(mContactType);
+ drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(),
+ canvas);
+ }
+ }
+
+ /**
+ * Returns a deterministic color based on the provided contact identifier string.
+ */
+ private int pickColor(final String identifier) {
+ if (TextUtils.isEmpty(identifier) || mContactType == TYPE_VOICEMAIL) {
+ return sDefaultColor;
+ }
+ // String.hashCode() implementation is not supposed to change across java versions, so
+ // this should guarantee the same email address always maps to the same color.
+ // The email should already have been normalized by the ContactRequest.
+ final int color = Math.abs(identifier.hashCode()) % NUM_OF_TILE_COLORS;
+ return sColors.getColor(color, sDefaultColor);
+ }
+
+ private static Bitmap getBitmapForContactType(int contactType) {
+ switch (contactType) {
+ case TYPE_PERSON:
+ return DEFAULT_PERSON_AVATAR;
+ case TYPE_BUSINESS:
+ return DEFAULT_BUSINESS_AVATAR;
+ case TYPE_VOICEMAIL:
+ return DEFAULT_VOICEMAIL_AVATAR;
+ default:
+ return DEFAULT_PERSON_AVATAR;
+ }
+ }
+
+ private static boolean isEnglishLetter(final char c) {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
+ }
+
+ @Override
+ public void setAlpha(final int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(final ColorFilter cf) {
+ mPaint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return android.graphics.PixelFormat.OPAQUE;
+ }
+
+ /**
+ * Scale the drawn letter tile to a ratio of its default size
+ *
+ * @param scale The ratio the letter tile should be scaled to as a percentage of its default
+ * size, from a scale of 0 to 2.0f. The default is 1.0f.
+ */
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+
+ /**
+ * Assigns the vertical offset of the position of the letter tile to the ContactDrawable
+ *
+ * @param offset The provided offset must be within the range of -0.5f to 0.5f.
+ * If set to -0.5f, the letter will be shifted upwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the top edge of the canvas.
+ * If set to 0.5f, the letter will be shifted downwards by 0.5 times the height of the canvas
+ * it is being drawn on, which means it will be drawn with the center of the letter starting
+ * at the bottom edge of the canvas.
+ * The default is 0.0f.
+ */
+ public void setOffset(float offset) {
+ Assert.assertTrue(offset >= -0.5f && offset <= 0.5f);
+ mOffset = offset;
+ }
+
+ public void setContactDetails(final String displayName, final String identifier) {
+ mDisplayName = displayName;
+ mIdentifier = identifier;
+ }
+
+ public void setContactType(int contactType) {
+ mContactType = contactType;
+ }
+}
diff --git a/src/com/android/contacts/common/list/ContactEntry.java b/src/com/android/contacts/common/list/ContactEntry.java
index 2683bef8..43fc19dd 100644
--- a/src/com/android/contacts/common/list/ContactEntry.java
+++ b/src/com/android/contacts/common/list/ContactEntry.java
@@ -29,7 +29,8 @@ public class ContactEntry {
public String phoneLabel;
public String phoneNumber;
public Uri photoUri;
- public Uri lookupKey;
+ public Uri lookupUri;
+ public String lookupKey;
public Drawable presenceIcon;
public long id;
public int pinned = PinnedPositions.UNPINNED;
diff --git a/src/com/android/contacts/common/list/ContactEntryListAdapter.java b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
index 0d13ec25..202f1219 100644
--- a/src/com/android/contacts/common/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
@@ -34,7 +34,9 @@ import android.widget.SectionIndexer;
import android.widget.TextView;
import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.R;
+import com.android.contacts.common.list.ContactListAdapter.ContactQuery;
import com.android.contacts.common.util.SearchUtil;
import java.util.HashSet;
@@ -640,10 +642,11 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
* @param photoUriColumn Index of the photo uri column. Optional: Can be -1
* @param contactIdColumn Index of the contact id column
* @param lookUpKeyColumn Index of the lookup key column
+ * @param displayNameColumn Index of the display name column
*/
protected void bindQuickContact(final ContactListItemView view, int partitionIndex,
Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn,
- int lookUpKeyColumn) {
+ int lookUpKeyColumn, int displayNameColumn) {
long photoId = 0;
if (!cursor.isNull(photoIdColumn)) {
photoId = cursor.getLong(photoIdColumn);
@@ -654,11 +657,16 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
if (photoId != 0 || photoUriColumn == -1) {
- getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme);
+ getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, null);
} else {
final String photoUriString = cursor.getString(photoUriColumn);
final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
- getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ request = getDefaultImageRequestFromCursor(cursor, displayNameColumn,
+ lookUpKeyColumn);
+ }
+ getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme, request);
}
}
@@ -693,4 +701,21 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
return directoryId != Directory.DEFAULT
&& directoryId != Directory.LOCAL_INVISIBLE;
}
+
+ /**
+ * Retrieves the lookup key and display name from a cursor, and returns a
+ * {@link DefaultImageRequest} containing these contact details
+ *
+ * @param cursor Contacts cursor positioned at the current row to retrieve contact details for
+ * @param displayNameColumn Column index of the display name
+ * @param lookupKeyColumn Column index of the lookup key
+ * @return {@link DefaultImageRequest} with the displayName and identifier fields set to the
+ * display name and lookup key of the contact.
+ */
+ public static DefaultImageRequest getDefaultImageRequestFromCursor(Cursor cursor,
+ int displayNameColumn, int lookupKeyColumn) {
+ final String displayName = cursor.getString(displayNameColumn);
+ final String lookupKey = cursor.getString(lookupKeyColumn);
+ return new DefaultImageRequest(displayName, lookupKey);
+ }
}
diff --git a/src/com/android/contacts/common/list/ContactListAdapter.java b/src/com/android/contacts/common/list/ContactListAdapter.java
index 3ad1801a..07504594 100644
--- a/src/com/android/contacts/common/list/ContactListAdapter.java
+++ b/src/com/android/contacts/common/list/ContactListAdapter.java
@@ -28,6 +28,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.R;
/**
@@ -237,11 +239,17 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter {
}
if (photoId != 0) {
- getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false);
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, null);
} else {
final String photoUriString = cursor.getString(ContactQuery.CONTACT_PHOTO_URI);
final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
- getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false);
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ String displayName = cursor.getString(ContactQuery.CONTACT_DISPLAY_NAME);
+ String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
+ request = new DefaultImageRequest(displayName, lookupKey);
+ }
+ getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false, request);
}
}
diff --git a/src/com/android/contacts/common/list/ContactTileAdapter.java b/src/com/android/contacts/common/list/ContactTileAdapter.java
index 66467a32..d9fbeac6 100644
--- a/src/com/android/contacts/common/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/common/list/ContactTileAdapter.java
@@ -256,7 +256,8 @@ public class ContactTileAdapter extends BaseAdapter {
contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
contact.status = cursor.getString(mStatusIndex);
contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
- contact.lookupKey = ContentUris.withAppendedId(
+ contact.lookupKey = lookupKey;
+ contact.lookupUri = ContentUris.withAppendedId(
Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
contact.isFavorite = cursor.getInt(mStarredIndex) > 0;
diff --git a/src/com/android/contacts/common/list/ContactTileView.java b/src/com/android/contacts/common/list/ContactTileView.java
index f26f6b45..32fd01a9 100644
--- a/src/com/android/contacts/common/list/ContactTileView.java
+++ b/src/com/android/contacts/common/list/ContactTileView.java
@@ -28,6 +28,7 @@ import android.widget.QuickContactBadge;
import android.widget.TextView;
import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.common.R;
@@ -94,7 +95,7 @@ public abstract class ContactTileView extends FrameLayout {
if (entry != null) {
mName.setText(getNameForView(entry.name));
- mLookupUri = entry.lookupKey;
+ mLookupUri = entry.lookupUri;
if (mStatus != null) {
if (entry.status == null) {
@@ -124,9 +125,11 @@ public abstract class ContactTileView extends FrameLayout {
setVisibility(View.VISIBLE);
if (mPhotoManager != null) {
+ DefaultImageRequest request = getDefaultImageRequest(entry.name, entry.lookupKey);
if (mPhoto != null) {
+
mPhotoManager.loadPhoto(mPhoto, entry.photoUri, getApproximateImageSize(),
- isDarkTheme());
+ isDarkTheme(), request);
if (mQuickContact != null) {
mQuickContact.assignContactUri(mLookupUri);
@@ -134,7 +137,7 @@ public abstract class ContactTileView extends FrameLayout {
} else if (mQuickContact != null) {
mQuickContact.assignContactUri(mLookupUri);
mPhotoManager.loadPhoto(mQuickContact, entry.photoUri,
- getApproximateImageSize(), isDarkTheme());
+ getApproximateImageSize(), isDarkTheme(), request);
}
} else {
Log.w(TAG, "contactPhotoManager not set");
@@ -182,6 +185,19 @@ public abstract class ContactTileView extends FrameLayout {
protected abstract boolean isDarkTheme();
+ /**
+ * Implemented by subclasses to allow them to return a {@link DefaultImageRequest} with the
+ * various image parameters defined to match their own layouts.
+ *
+ * @param displayName The display name of the contact
+ * @param lookupKey The lookup key of the contact
+ * @return A {@link DefaultImageRequest} object with each field configured by the subclass
+ * as desired, or {@code null}.
+ */
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(displayName, lookupKey);
+ }
+
public interface Listener {
/**
* Notification that the contact was selected; no specific action is dictated.
diff --git a/src/com/android/contacts/common/list/DefaultContactListAdapter.java b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
index fb974b4d..e6be0f21 100644
--- a/src/com/android/contacts/common/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
@@ -196,7 +196,7 @@ public class DefaultContactListAdapter extends ContactListAdapter {
if (isQuickContactEnabled()) {
bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID,
- ContactQuery.CONTACT_LOOKUP_KEY);
+ ContactQuery.CONTACT_LOOKUP_KEY, ContactQuery.CONTACT_DISPLAY_NAME);
} else {
if (getDisplayPhotos()) {
bindPhoto(view, partition, cursor);
diff --git a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
index ad370353..88cc2b87 100644
--- a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.R;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.extensions.ExtendedPhoneDirectoriesManager;
import com.android.contacts.common.extensions.ExtensionsFactory;
import com.android.contacts.common.util.Constants;
@@ -345,7 +346,7 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter {
if (isQuickContactEnabled()) {
bindQuickContact(view, partition, cursor, PhoneQuery.PHOTO_ID,
PhoneQuery.PHOTO_URI, PhoneQuery.CONTACT_ID,
- PhoneQuery.LOOKUP_KEY);
+ PhoneQuery.LOOKUP_KEY, PhoneQuery.DISPLAY_NAME);
} else {
if (getDisplayPhotos()) {
bindPhoto(view, partition, cursor);
@@ -420,11 +421,18 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter {
}
if (photoId != 0) {
- getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false);
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, null);
} else {
final String photoUriString = cursor.getString(PhoneQuery.PHOTO_URI);
final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
- getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false);
+
+ DefaultImageRequest request = null;
+ if (photoUri == null) {
+ final String displayName = cursor.getString(PhoneQuery.DISPLAY_NAME);
+ final String lookupKey = cursor.getString(PhoneQuery.LOOKUP_KEY);
+ request = new DefaultImageRequest(displayName, lookupKey);
+ }
+ getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false, request);
}
}
diff --git a/src/com/android/contacts/common/model/Contact.java b/src/com/android/contacts/common/model/Contact.java
index d5ff0a32..74b5596d 100644
--- a/src/com/android/contacts/common/model/Contact.java
+++ b/src/com/android/contacts/common/model/Contact.java
@@ -304,6 +304,14 @@ public class Contact {
return mDisplayNameSource;
}
+ /**
+ * Used by various classes to determine whether or not this contact should be displayed as
+ * a business rather than a person.
+ */
+ public boolean isDisplayNameFromOrganization() {
+ return DisplayNameSources.ORGANIZATION == mDisplayNameSource;
+ }
+
public long getPhotoId() {
return mPhotoId;
}