diff options
author | Jay Shrauner <shrauner@google.com> | 2014-03-05 00:11:49 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-03-05 00:11:49 +0000 |
commit | ee8df7615a48a8fe9372ff4c013774d661413b73 (patch) | |
tree | d69dfc7e388fb6e7b69afcf1b2b609e303b08d8c | |
parent | cab654369292e17eb338aca05605c26c6606186b (diff) | |
parent | 5ff42dbbd58b50b622f5979908ceb1f1193c8a42 (diff) | |
download | android_packages_apps_ContactsCommon-ee8df7615a48a8fe9372ff4c013774d661413b73.tar.gz android_packages_apps_ContactsCommon-ee8df7615a48a8fe9372ff4c013774d661413b73.tar.bz2 android_packages_apps_ContactsCommon-ee8df7615a48a8fe9372ff4c013774d661413b73.zip |
am 5ff42dbb: am 0144b772: am 2c5e1207: am 86f4cd9f: Merge "Add support for letter tile avatars to ContactsPhotoManager" into klp-dev
* commit '5ff42dbbd58b50b622f5979908ceb1f1193c8a42':
Add support for letter tile avatars to ContactsPhotoManager
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 Binary files differdeleted file mode 100644 index a17da613..00000000 --- a/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png b/res/drawable-hdpi/ic_contact_picture_180_holo_light.png Binary files differdeleted file mode 100644 index 7195b070..00000000 --- a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_contact_picture_holo_dark.png b/res/drawable-hdpi/ic_contact_picture_holo_dark.png Binary files differdeleted file mode 100644 index 314fa007..00000000 --- a/res/drawable-hdpi/ic_contact_picture_holo_dark.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_contact_picture_holo_light.png b/res/drawable-hdpi/ic_contact_picture_holo_light.png Binary files differdeleted file mode 100644 index 87447e5f..00000000 --- a/res/drawable-hdpi/ic_contact_picture_holo_light.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_list_item_avatar.png b/res/drawable-hdpi/ic_list_item_avatar.png Binary files differnew file mode 100644 index 00000000..0a239d02 --- /dev/null +++ b/res/drawable-hdpi/ic_list_item_avatar.png diff --git a/res/drawable-hdpi/ic_list_item_businessavatar.png b/res/drawable-hdpi/ic_list_item_businessavatar.png Binary files differnew file mode 100644 index 00000000..c943fc9a --- /dev/null +++ b/res/drawable-hdpi/ic_list_item_businessavatar.png diff --git a/res/drawable-hdpi/ic_voicemail_avatar.png b/res/drawable-hdpi/ic_voicemail_avatar.png Binary files differnew file mode 100644 index 00000000..2fb78261 --- /dev/null +++ b/res/drawable-hdpi/ic_voicemail_avatar.png diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png Binary files differdeleted file mode 100644 index acba3339..00000000 --- a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png b/res/drawable-mdpi/ic_contact_picture_180_holo_light.png Binary files differdeleted file mode 100644 index 70d96977..00000000 --- a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_contact_picture_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_holo_dark.png Binary files differdeleted file mode 100644 index 68767774..00000000 --- a/res/drawable-mdpi/ic_contact_picture_holo_dark.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_contact_picture_holo_light.png b/res/drawable-mdpi/ic_contact_picture_holo_light.png Binary files differdeleted file mode 100644 index c2aef96f..00000000 --- a/res/drawable-mdpi/ic_contact_picture_holo_light.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_list_item_avatar.png b/res/drawable-mdpi/ic_list_item_avatar.png Binary files differnew file mode 100644 index 00000000..871212b9 --- /dev/null +++ b/res/drawable-mdpi/ic_list_item_avatar.png diff --git a/res/drawable-mdpi/ic_list_item_businessavatar.png b/res/drawable-mdpi/ic_list_item_businessavatar.png Binary files differnew file mode 100644 index 00000000..43135eaa --- /dev/null +++ b/res/drawable-mdpi/ic_list_item_businessavatar.png diff --git a/res/drawable-mdpi/ic_voicemail_avatar.png b/res/drawable-mdpi/ic_voicemail_avatar.png Binary files differnew file mode 100644 index 00000000..4005f24f --- /dev/null +++ b/res/drawable-mdpi/ic_voicemail_avatar.png diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png Binary files differdeleted file mode 100644 index c4c001ec..00000000 --- a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png Binary files differdeleted file mode 100644 index 989844eb..00000000 --- a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_holo_dark.png Binary files differdeleted file mode 100644 index ddf797fc..00000000 --- a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_contact_picture_holo_light.png b/res/drawable-xhdpi/ic_contact_picture_holo_light.png Binary files differdeleted file mode 100644 index 914d85ce..00000000 --- a/res/drawable-xhdpi/ic_contact_picture_holo_light.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_list_item_avatar.png b/res/drawable-xhdpi/ic_list_item_avatar.png Binary files differnew file mode 100644 index 00000000..2a73a2e2 --- /dev/null +++ b/res/drawable-xhdpi/ic_list_item_avatar.png diff --git a/res/drawable-xhdpi/ic_list_item_businessavatar.png b/res/drawable-xhdpi/ic_list_item_businessavatar.png Binary files differnew file mode 100644 index 00000000..14742b56 --- /dev/null +++ b/res/drawable-xhdpi/ic_list_item_businessavatar.png diff --git a/res/drawable-xhdpi/ic_voicemail_avatar.png b/res/drawable-xhdpi/ic_voicemail_avatar.png Binary files differnew file mode 100644 index 00000000..f24505d3 --- /dev/null +++ b/res/drawable-xhdpi/ic_voicemail_avatar.png diff --git a/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png Binary files differdeleted file mode 100644 index 6e057ac3..00000000 --- a/res/drawable-xxhdpi/ic_contact_picture_180_holo_dark.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png b/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png Binary files differdeleted file mode 100644 index b04c82f1..00000000 --- a/res/drawable-xxhdpi/ic_contact_picture_180_holo_light.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png b/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png Binary files differdeleted file mode 100644 index 52a69c37..00000000 --- a/res/drawable-xxhdpi/ic_contact_picture_holo_dark.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_contact_picture_holo_light.png b/res/drawable-xxhdpi/ic_contact_picture_holo_light.png Binary files differdeleted file mode 100644 index 9150df2d..00000000 --- a/res/drawable-xxhdpi/ic_contact_picture_holo_light.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_list_item_avatar.png b/res/drawable-xxhdpi/ic_list_item_avatar.png Binary files differnew file mode 100644 index 00000000..5665631e --- /dev/null +++ b/res/drawable-xxhdpi/ic_list_item_avatar.png diff --git a/res/drawable-xxhdpi/ic_list_item_businessavatar.png b/res/drawable-xxhdpi/ic_list_item_businessavatar.png Binary files differnew file mode 100644 index 00000000..484591c7 --- /dev/null +++ b/res/drawable-xxhdpi/ic_list_item_businessavatar.png diff --git a/res/drawable-xxhdpi/ic_voicemail_avatar.png b/res/drawable-xxhdpi/ic_voicemail_avatar.png Binary files differnew file mode 100644 index 00000000..182def8d --- /dev/null +++ b/res/drawable-xxhdpi/ic_voicemail_avatar.png 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 995201d6..916c9b53 100644 --- a/src/com/android/contacts/common/ContactPhotoManager.java +++ b/src/com/android/contacts/common/ContactPhotoManager.java @@ -33,6 +33,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; @@ -48,9 +49,11 @@ 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.MemoryUtils; 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); } /** @@ -467,10 +669,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); @@ -481,18 +683,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) { @@ -1219,7 +1434,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 c81a81c5..740f75af 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; } |