diff options
author | linus_lee <llee@cyngn.com> | 2014-11-26 18:20:29 -0800 |
---|---|---|
committer | linus_lee <llee@cyngn.com> | 2014-12-09 12:02:08 -0800 |
commit | f199f983c9a5e2f4434b85273d1da0d609c33228 (patch) | |
tree | 1a790aff63fe5362bac927b592507b1901030513 /src | |
parent | cb83a720c4c358eb7657298dbcf940d029e5af1c (diff) | |
download | android_packages_apps_Eleven-f199f983c9a5e2f4434b85273d1da0d609c33228.tar.gz android_packages_apps_Eleven-f199f983c9a5e2f4434b85273d1da0d609c33228.tar.bz2 android_packages_apps_Eleven-f199f983c9a5e2f4434b85273d1da0d609c33228.zip |
Eleven: Switch default artist and album to a more colorful version
Also fixed a bug where playlist worker tasks were running more than necessary
Removed the picker to fetch images from internet
because last.fm is removed for now
Change-Id: Ice438ac650d8b7be0c275f8270af13086e6568d6
Diffstat (limited to 'src')
-rw-r--r-- | src/com/cyngn/eleven/cache/ImageFetcher.java | 44 | ||||
-rw-r--r-- | src/com/cyngn/eleven/cache/ImageWorker.java | 132 | ||||
-rw-r--r-- | src/com/cyngn/eleven/cache/PlaylistWorkerTask.java | 77 | ||||
-rw-r--r-- | src/com/cyngn/eleven/menu/PhotoSelectionDialog.java | 9 | ||||
-rw-r--r-- | src/com/cyngn/eleven/ui/fragments/FadingBarFragment.java | 2 | ||||
-rw-r--r-- | src/com/cyngn/eleven/widgets/LetterTileDrawable.java | 328 |
6 files changed, 434 insertions, 158 deletions
diff --git a/src/com/cyngn/eleven/cache/ImageFetcher.java b/src/com/cyngn/eleven/cache/ImageFetcher.java index e918d7b..c0a51c8 100644 --- a/src/com/cyngn/eleven/cache/ImageFetcher.java +++ b/src/com/cyngn/eleven/cache/ImageFetcher.java @@ -22,6 +22,7 @@ import com.cyngn.eleven.MusicPlaybackService; import com.cyngn.eleven.cache.PlaylistWorkerTask.PlaylistWorkerType; import com.cyngn.eleven.utils.MusicUtils; import com.cyngn.eleven.widgets.BlurScrimImage; +import com.cyngn.eleven.widgets.LetterTileDrawable; import java.io.FileNotFoundException; import java.io.IOException; @@ -174,40 +175,6 @@ public class ImageFetcher extends ImageWorker { } /** - * @param key The key used to find the image to return - */ - public Bitmap getCachedBitmap(final String key) { - if (mImageCache != null) { - return mImageCache.getCachedBitmap(key); - } - return getDefaultArtwork(); - } - - /** - * @param keyAlbum The key (album name) used to find the album art to return - * @param keyArtist The key (artist name) used to find the album art to return - */ - public Bitmap getCachedArtwork(final String keyAlbum, final String keyArtist) { - return getCachedArtwork(keyAlbum, keyArtist, - MusicUtils.getIdForAlbum(mContext, keyAlbum, keyArtist)); - } - - /** - * @param keyAlbum The key (album name) used to find the album art to return - * @param keyArtist The key (artist name) used to find the album art to return - * @param keyId The key (album id) used to find the album art to return - */ - public Bitmap getCachedArtwork(final String keyAlbum, final String keyArtist, - final long keyId) { - if (mImageCache != null) { - return mImageCache.getCachedArtwork(mContext, - generateAlbumCacheKey(keyAlbum, keyArtist), - keyId); - } - return getDefaultArtwork(); - } - - /** * Finds cached or downloads album art. Used in {@link MusicPlaybackService} * to set the current album art in the notification and lock screen * @@ -222,10 +189,11 @@ public class ImageFetcher extends ImageWorker { boolean smallArtwork) { // Check the disk cache Bitmap artwork = null; + String key = albumName; if (artwork == null && albumName != null && mImageCache != null) { - artwork = mImageCache.getBitmapFromDiskCache( - generateAlbumCacheKey(albumName, artistName)); + key = generateAlbumCacheKey(albumName, artistName); + artwork = mImageCache.getBitmapFromDiskCache(key); } if (artwork == null && albumId >= 0 && mImageCache != null) { // Check for local artwork @@ -234,7 +202,9 @@ public class ImageFetcher extends ImageWorker { if (artwork != null) { return artwork; } - return getDefaultArtwork(smallArtwork); + + return LetterTileDrawable.createDefaultBitmap(mContext, key, ImageType.ALBUM, false, + smallArtwork); } /** diff --git a/src/com/cyngn/eleven/cache/ImageWorker.java b/src/com/cyngn/eleven/cache/ImageWorker.java index 414e481..e3cea92 100644 --- a/src/com/cyngn/eleven/cache/ImageWorker.java +++ b/src/com/cyngn/eleven/cache/ImageWorker.java @@ -20,7 +20,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.support.v8.renderscript.RenderScript; -import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -30,6 +30,7 @@ import com.cyngn.eleven.utils.ApolloUtils; import com.cyngn.eleven.utils.ImageUtils; import com.cyngn.eleven.widgets.BlurScrimImage; import com.cyngn.eleven.cache.PlaylistWorkerTask.PlaylistWorkerType; +import com.cyngn.eleven.widgets.LetterTileDrawable; import java.lang.ref.WeakReference; import java.util.Collections; @@ -77,26 +78,6 @@ public abstract class ImageWorker { private final ColorDrawable mTransparentDrawable; /** - * Default album art - */ - private final Bitmap mDefault; - - /** - * A small version of the default album art - */ - private final Bitmap mDefaultSmall; - - /** - * Default Artist art - */ - private final Bitmap mDefaultArtist; - - /** - * Default Playlist art - */ - private final Bitmap mDefaultPlaylist; - - /** * The Context to use */ protected Context mContext; @@ -119,17 +100,6 @@ public abstract class ImageWorker { } mResources = mContext.getResources(); - // Create the default artwork - mDefault = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_artwork)).getBitmap(); - // Create the small version of the default artwork - mDefaultSmall = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_artwork_sm)) - .getBitmap(); - // Create the artist artwork - mDefaultArtist = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_artist)) - .getBitmap(); - // Create the playlist artwork - mDefaultPlaylist = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_playlist)) - .getBitmap(); // Create the transparent layer for the transition drawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT); } @@ -177,47 +147,14 @@ public abstract class ImageWorker { } /** - * @return The default artwork - */ - public Bitmap getDefaultArtwork() { - return getDefaultArtwork(false); - } - - /** - * @param small returns the smaller version of the default artwork if true - * @return The default artwork + * @return A new drawable of the default artwork */ - public Bitmap getDefaultArtwork(boolean small) { - return small ? mDefaultSmall : mDefault; - } - - /** - * @return A new bitmap drawable of the default artwork - */ - public BitmapDrawable getNewDefaultBitmapDrawable(ImageType imageType) { - Bitmap targetBitmap = null; - - switch (imageType) { - case ARTIST: - targetBitmap = mDefaultArtist; - break; - - case PLAYLIST: - targetBitmap = mDefaultPlaylist; - break; - - case ALBUM: - default: - targetBitmap = mDefault; - break; - } - - BitmapDrawable bitmapDrawable = new BitmapDrawable(mResources, targetBitmap); - // No filter and no dither makes things much quicker - bitmapDrawable.setFilterBitmap(false); - bitmapDrawable.setDither(false); - - return bitmapDrawable; + public Drawable getNewDrawable(ImageType imageType, String name, + String identifier) { + LetterTileDrawable letterTileDrawable = new LetterTileDrawable(mContext); + letterTileDrawable.setTileDetails(name, identifier, imageType); + letterTileDrawable.setIsCircular(false); + return letterTileDrawable; } public static Bitmap getBitmapInBackground(final Context context, final ImageCache imageCache, @@ -447,6 +384,25 @@ public abstract class ImageWorker { } /** + * Loads the default image into the image view given the image type + * @param imageView The {@link ImageView} + * @param imageType The type of image + */ + public void loadDefaultImage(final ImageView imageView, final ImageType imageType, + final String name, final String identifier) { + if (imageView != null) { + // if an existing letter drawable exists, re-use it + Drawable existingDrawable = imageView.getDrawable(); + if (existingDrawable != null && existingDrawable instanceof LetterTileDrawable) { + ((LetterTileDrawable)existingDrawable).setTileDetails(name, identifier, imageType); + } else { + imageView.setImageDrawable(getNewDrawable(imageType, name, + identifier)); + } + } + } + + /** * Called to fetch the artist or album art. * * @param key The unique identifier for the image. @@ -493,17 +449,28 @@ public abstract class ImageWorker { imageView.setImageBitmap(lruBitmap); } } else { - // if a background drawable hasn't been set, create one so that even if - // the disk cache is paused we see something - if (imageView.getBackground() == null) { - imageView.setBackgroundDrawable(getNewDefaultBitmapDrawable(imageType)); + // load the default image + if (imageType == ImageType.ARTIST) { + loadDefaultImage(imageView, imageType, artistName, key); + } else if (imageType == ImageType.ALBUM) { + // don't show letters for albums so pass in null as the display string + // because an album could have multiple artists, use the album id as the key here + loadDefaultImage(imageView, imageType, null, String.valueOf(albumId)); + } else { + // don't show letters for playlists so pass in null as the display string + loadDefaultImage(imageView, imageType, null, key); } if (executePotentialWork(key, imageView) && imageView != null && !mImageCache.isDiskCachePaused()) { + Drawable fromDrawable = imageView.getDrawable(); + if (fromDrawable == null) { + fromDrawable = mTransparentDrawable; + } + // Otherwise run the worker task final SimpleBitmapWorkerTask bitmapWorkerTask = new SimpleBitmapWorkerTask(key, - imageView, imageType, mTransparentDrawable, mContext, scaleImgToView); + imageView, imageType, fromDrawable, mContext, scaleImgToView); final AsyncTaskContainer asyncTaskContainer = new AsyncTaskContainer(bitmapWorkerTask); imageView.setTag(asyncTaskContainer); @@ -546,15 +513,8 @@ public abstract class ImageWorker { // Bitmap found in memory cache imageView.setImageBitmap(lruBitmap); } else { - // if a background drawable hasn't been set, create one so that even if - // the disk cache is paused we see something - if (imageView.getBackground() == null) { - if (type == PlaylistWorkerType.Artist) { - imageView.setBackground(getNewDefaultBitmapDrawable(ImageType.ARTIST)); - } else if (type == PlaylistWorkerType.CoverArt) { - imageView.setBackground(getNewDefaultBitmapDrawable(ImageType.PLAYLIST)); - } - } + // load the default image + loadDefaultImage(imageView, ImageType.PLAYLIST, null, String.valueOf(playlistId)); } // even though we may have found the image in the cache, we want to check if the playlist diff --git a/src/com/cyngn/eleven/cache/PlaylistWorkerTask.java b/src/com/cyngn/eleven/cache/PlaylistWorkerTask.java index ff9e0d9..011b6f6 100644 --- a/src/com/cyngn/eleven/cache/PlaylistWorkerTask.java +++ b/src/com/cyngn/eleven/cache/PlaylistWorkerTask.java @@ -4,18 +4,15 @@ package com.cyngn.eleven.cache; import android.content.Context; -import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.provider.MediaStore; import android.widget.ImageView; -import com.cyngn.eleven.R; import com.cyngn.eleven.cache.ImageWorker.ImageType; import com.cyngn.eleven.loaders.PlaylistSongLoader; import com.cyngn.eleven.loaders.SortedCursor; @@ -48,6 +45,9 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD // if the playlist has changed protected final boolean mFoundInCache; + // because a cached image can be loaded, we use this flag to signal to remove that default image + protected boolean mFallbackToDefaultImage; + /** * Constructor of <code>PlaylistWorkerTask</code> * @param key the key of the image to store to @@ -66,6 +66,7 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD mWorkerType = type; mPlaylistStore = PlaylistArtworkStore.getInstance(mContext); mFoundInCache = foundInCache; + mFallbackToDefaultImage = false; } /** @@ -99,9 +100,15 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD bitmap = mImageCache.getCachedBitmap(mKey); } - // if we found a bitmap and we don't need an update, return it - if (bitmap != null && !needsUpdate) { - return createImageTransitionDrawable(bitmap); + // if we don't need an update, return something + if (!needsUpdate) { + if (bitmap != null) { + // if we found a bitmap, return it + return createImageTransitionDrawable(bitmap); + } else { + // otherwise return null since we don't need an update + return null; + } } // otherwise re-run the logic to get the bitmap @@ -123,19 +130,15 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD mPlaylistStore.updateArtistArt(mPlaylistId); // remove the cached image mImageCache.removeFromCache(PlaylistArtworkStore.getArtistCacheKey(mPlaylistId)); - // go back to the default image - BitmapDrawable drawable = - (BitmapDrawable) mResources.getDrawable(R.drawable.default_artist); - bitmap = drawable.getBitmap(); + // revert back to default image + mFallbackToDefaultImage = true; } else if (mWorkerType == PlaylistWorkerType.CoverArt) { // update the timestamp mPlaylistStore.updateCoverArt(mPlaylistId); // remove the cached image mImageCache.removeFromCache(PlaylistArtworkStore.getCoverCacheKey(mPlaylistId)); - // go back to the default image - BitmapDrawable drawable = - (BitmapDrawable) mResources.getDrawable(R.drawable.default_playlist); - bitmap = drawable.getBitmap(); + // revert back to default image + mFallbackToDefaultImage = true; } } else if (mWorkerType == PlaylistWorkerType.Artist) { bitmap = loadTopArtist(sortedCursor); @@ -230,16 +233,23 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD null, artistName, -1, ImageType.ARTIST); } while (sortedCursor.moveToNext() && bitmap == null); - sortedCursor.close(); + if (bitmap == null) { + // if we can't find any artist images, try loading the top songs image + bitmap = mImageCache.getCachedBitmap( + PlaylistArtworkStore.getCoverCacheKey(mPlaylistId)); + } if (bitmap != null) { // add the image to the cache mImageCache.addBitmapToCache(mKey, bitmap, true); - - // store this artist name into the db - mPlaylistStore.updateArtistArt(mPlaylistId); + } else { + mImageCache.removeFromCache(mKey); + mFallbackToDefaultImage = true; } + // store the fact that we ran this code into the db to prevent multiple re-runs + mPlaylistStore.updateArtistArt(mPlaylistId); + return bitmap; } @@ -292,8 +302,6 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD } } while (sortedCursor.moveToNext() && loadedBitmaps.size() < MAX_NUM_BITMAPS_TO_LOAD); - sortedCursor.close(); - // if we found at least 1 bitmap if (loadedBitmaps.size() > 0) { // get the first bitmap @@ -324,22 +332,24 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD combinedCanvas.drawBitmap(loadedBitmaps.get(3), null, new Rect(width / 2, height / 2, width, height), null); + combinedCanvas.release(); combinedCanvas = null; bitmap = combinedBitmap; } + } - if (bitmap != null) { - // add the image to the cache - mImageCache.addBitmapToCache(mKey, bitmap, true); + // store the fact that we ran this code into the db to prevent multiple re-runs + mPlaylistStore.updateCoverArt(mPlaylistId); - // store this artist name into the db - mPlaylistStore.updateCoverArt(mPlaylistId); - } - - return bitmap; + if (bitmap != null) { + // add the image to the cache + mImageCache.addBitmapToCache(mKey, bitmap, true); + } else { + mImageCache.removeFromCache(mKey); + mFallbackToDefaultImage = true; } - return null; + return bitmap; } /** @@ -348,8 +358,13 @@ public class PlaylistWorkerTask extends BitmapWorkerTask<Void, Void, TransitionD @Override protected void onPostExecute(TransitionDrawable transitionDrawable) { final ImageView imageView = getAttachedImageView(); - if (transitionDrawable != null && imageView != null) { - imageView.setImageDrawable(transitionDrawable); + if (imageView != null) { + if (transitionDrawable != null) { + imageView.setImageDrawable(transitionDrawable); + } else if (mFallbackToDefaultImage) { + ImageFetcher.getInstance(mContext).loadDefaultImage(imageView, + ImageType.PLAYLIST, null, String.valueOf(mPlaylistId)); + } } } } diff --git a/src/com/cyngn/eleven/menu/PhotoSelectionDialog.java b/src/com/cyngn/eleven/menu/PhotoSelectionDialog.java index 7adc921..7d27da8 100644 --- a/src/com/cyngn/eleven/menu/PhotoSelectionDialog.java +++ b/src/com/cyngn/eleven/menu/PhotoSelectionDialog.java @@ -118,10 +118,11 @@ public class PhotoSelectionDialog extends DialogFragment { private void setArtistChoices() { // Select a photo from the gallery mChoices.add(NEW_PHOTO, getString(R.string.new_photo)); + /* Disable fetching image until we find a last.fm replacement if (ApolloUtils.isOnline(getActivity())) { // Option to fetch the old artist image mChoices.add(OLD_PHOTO, getString(R.string.context_menu_fetch_artist_image)); - } + }*/ } /** @@ -130,11 +131,12 @@ public class PhotoSelectionDialog extends DialogFragment { private void setAlbumChoices() { // Select a photo from the gallery mChoices.add(NEW_PHOTO, getString(R.string.new_photo)); + /* Disable fetching image until we find a last.fm replacement // Option to fetch the old album image if (ApolloUtils.isOnline(getActivity())) { // Option to fetch the old artist image mChoices.add(OLD_PHOTO, getString(R.string.context_menu_fetch_album_art)); - } + }*/ } /** @@ -143,8 +145,9 @@ public class PhotoSelectionDialog extends DialogFragment { private void setOtherChoices() { // Select a photo from the gallery mChoices.add(NEW_PHOTO, getString(R.string.new_photo)); + // Disable fetching image until we find a last.fm replacement // Option to use the default image - mChoices.add(OLD_PHOTO, getString(R.string.use_default)); + // mChoices.add(OLD_PHOTO, getString(R.string.use_default)); } /** diff --git a/src/com/cyngn/eleven/ui/fragments/FadingBarFragment.java b/src/com/cyngn/eleven/ui/fragments/FadingBarFragment.java index 7fe3a59..5c3404e 100644 --- a/src/com/cyngn/eleven/ui/fragments/FadingBarFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/FadingBarFragment.java @@ -7,7 +7,7 @@ import android.widget.AbsListView.OnScrollListener; import com.cyngn.eleven.ui.activities.HomeActivity; public abstract class FadingBarFragment extends DetailFragment implements OnScrollListener { - protected static final int ACTION_BAR_DEFAULT_OPACITY = 65; + protected static final int ACTION_BAR_DEFAULT_OPACITY = 100; @Override public void setupActionBar() { diff --git a/src/com/cyngn/eleven/widgets/LetterTileDrawable.java b/src/com/cyngn/eleven/widgets/LetterTileDrawable.java new file mode 100644 index 0000000..139d5e7 --- /dev/null +++ b/src/com/cyngn/eleven/widgets/LetterTileDrawable.java @@ -0,0 +1,328 @@ +/* + * 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.cyngn.eleven.widgets; + +import android.content.Context; +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 junit.framework.Assert; + +import com.cyngn.eleven.R; +import com.cyngn.eleven.cache.ImageWorker.ImageType; +import com.cyngn.eleven.utils.MusicUtils; + +/** + * A drawable that encapsulates all the functionality needed to display a letter tile to + * represent a artist/album/playlist 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_ARTIST; + private static Bitmap DEFAULT_ARTIST_LARGE; + private static Bitmap DEFAULT_ALBUM; + private static Bitmap DEFAULT_ALBUM_LARGE; + private static Bitmap DEFAULT_PLAYLIST; + private static Bitmap DEFAULT_PLAYLIST_LARGE; + + /** Reusable components to avoid new allocations */ + private static final Paint sPaint = new Paint(); + private static final Rect sRect = new Rect(); + private static final char[] sChars = new char[2]; + + private String mDisplayName; + private String mIdentifier; + private float mScale = 1.0f; + private float mOffset = 0.0f; + private Resources res; + private boolean mIsCircle = false; + + private ImageType mImageType; + + private static synchronized void initializeStaticVariables(final Resources res) { + 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_ARTIST = BitmapFactory.decodeResource(res, R.drawable.ic_artist); + DEFAULT_ARTIST_LARGE = BitmapFactory.decodeResource(res, R.drawable.ic_artist_lg); + DEFAULT_ALBUM = BitmapFactory.decodeResource(res, R.drawable.ic_album); + DEFAULT_ALBUM_LARGE = BitmapFactory.decodeResource(res, R.drawable.ic_album_lg); + DEFAULT_PLAYLIST = BitmapFactory.decodeResource(res, R.drawable.ic_playlist); + DEFAULT_PLAYLIST_LARGE = BitmapFactory.decodeResource(res, R.drawable.ic_playlist_lg); + + sPaint.setTypeface(Typeface.create( + res.getString(R.string.letter_tile_letter_font_family), Typeface.NORMAL)); + sPaint.setTextAlign(Align.CENTER); + sPaint.setAntiAlias(true); + } + } + + public LetterTileDrawable(final Context context) { + mPaint = new Paint(); + mPaint.setFilterBitmap(true); + mPaint.setDither(true); + res = context.getResources(); + + initializeStaticVariables(res); + } + + @Override + public void draw(final Canvas canvas) { + //setBounds(0, 0, 120, 120); + final Rect bounds = getBounds(); + if (!isVisible() || bounds.isEmpty()) { + return; + } + // Draw letter tile. + drawLetterTile(canvas); + } + + @Override + public void setBounds(Rect bounds) { + super.setBounds(bounds); + } + + private void drawLetterTile(final Canvas canvas) { + // Draw background color. + sPaint.setColor(pickColor(mIdentifier)); + + sPaint.setAlpha(mPaint.getAlpha()); + final Rect bounds = getBounds(); + final int minDimension = Math.min(bounds.width(), bounds.height()); + + if (mIsCircle) { + canvas.drawCircle(bounds.centerX(), bounds.centerY(), minDimension / 2, sPaint); + } else { + canvas.drawRect(bounds, sPaint); + } + + // Draw letter/digit only if the first character is an english letter + if (mDisplayName != null + && isEnglishLetter(mDisplayName.charAt(0))) { + int numChars = 1; + + // Draw letter or digit. + sChars[0] = Character.toUpperCase(mDisplayName.charAt(0)); + + if (mDisplayName.length() > 1 && isEnglishLetter(mDisplayName.charAt(1))) { + sChars[1] = Character.toLowerCase(mDisplayName.charAt(1)); + numChars = 2; + } + + // Scale text by canvas bounds and user selected scaling factor + sPaint.setTextSize(mScale * sLetterToTileRatio * minDimension); + //sPaint.setTextSize(sTileLetterFontSize); + sPaint.getTextBounds(sChars, 0, numChars, sRect); + sPaint.setColor(sTileFontColor); + + // Draw the letter in the canvas, vertically shifted up or down by the user-defined + // offset + canvas.drawText(sChars, 0, numChars, 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 = getDefaultBitmapForImageType(mImageType, bounds); + + // The bitmap should be drawn in the middle of the canvas without changing its width to + // height ratio. + final Rect destRect = copyBounds(); + + drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), canvas, destRect, mScale, + mOffset, mPaint); + } + } + + public int getColor() { + return pickColor(mIdentifier); + } + + /** + * Returns a deterministic color based on the provided contact identifier string. + */ + private static int pickColor(final String identifier) { + if (TextUtils.isEmpty(identifier)) { + 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()) % sColors.length(); + return sColors.getColor(color, sDefaultColor); + } + + /** + * Gets the default image to show for the image type. If the bounds are large, + * it will use the large default bitmap + */ + private static Bitmap getDefaultBitmapForImageType(ImageType type, Rect bounds) { + Bitmap ret = getDefaultBitmap(type, true); + if (Math.max(bounds.width(), bounds.height()) > Math.max(ret.getWidth(), ret.getHeight())) { + ret = getDefaultBitmap(type, false); + } + + return ret; + } + + private static Bitmap getDefaultBitmap(ImageType type, boolean small) { + switch (type) { + case ARTIST: + return small ? DEFAULT_ARTIST : DEFAULT_ARTIST_LARGE; + case ALBUM: + return small ? DEFAULT_ALBUM : DEFAULT_ALBUM_LARGE; + case PLAYLIST: + return small ? DEFAULT_PLAYLIST : DEFAULT_PLAYLIST_LARGE; + default: + throw new IllegalArgumentException("Unrecognized image type"); + } + } + + private static boolean isEnglishLetter(final char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9'); + } + + @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 setTileDetails(final String displayName, final String identifier, + final ImageType type) { + mDisplayName = MusicUtils.getTrimmedName(displayName); + mIdentifier = MusicUtils.getTrimmedName(identifier); + mImageType = type; + invalidateSelf(); + } + + public void setIsCircular(boolean isCircle) { + mIsCircle = isCircle; + } + + /** + * Draw the bitmap onto the canvas at the current bounds taking into account the current scale. + */ + private static void drawBitmap(final Bitmap bitmap, final int width, final int height, + final Canvas canvas, final Rect destRect, final float scale, + final float offset, final Paint paint) { + // Crop the destination bounds into a square, scaled and offset as appropriate + final int halfLength = (int) (scale * Math.min(destRect.width(), destRect.height()) / 2); + + destRect.set(destRect.centerX() - halfLength, + (int) (destRect.centerY() - halfLength + offset * destRect.height()), + destRect.centerX() + halfLength, + (int) (destRect.centerY() + halfLength + offset * destRect.height())); + + // Source rectangle remains the entire bounds of the source bitmap. + sRect.set(0, 0, width, height); + + canvas.drawBitmap(bitmap, sRect, destRect, paint); + } + + /** + * Draws the default letter tile drawable for the image type to a bitmap + */ + public static Bitmap createDefaultBitmap(Context context, String identifier, ImageType type, + boolean isCircle, boolean smallArtwork) { + initializeStaticVariables(context.getResources()); + + identifier = MusicUtils.getTrimmedName(identifier); + + // get the default bitmap to determine what to draw to + Bitmap defaultBitmap = getDefaultBitmap(type, smallArtwork); + final Rect bounds = new Rect(0, 0, defaultBitmap.getWidth(), defaultBitmap.getHeight()); + + // create a bitmap and canvas for drawing + Bitmap createdBitmap = Bitmap.createBitmap(defaultBitmap.getWidth(), + defaultBitmap.getHeight(), defaultBitmap.getConfig()); + Canvas canvas = new Canvas(createdBitmap); + + Paint paint = new Paint(); + paint.setColor(pickColor(identifier)); + + final int minDimension = Math.min(bounds.width(), bounds.height()); + + if (isCircle) { + canvas.drawCircle(bounds.centerX(), bounds.centerY(), minDimension / 2, paint); + } else { + canvas.drawRect(bounds, paint); + } + + // draw to the bitmap + drawBitmap(defaultBitmap, defaultBitmap.getWidth(), defaultBitmap.getHeight(), canvas, + bounds, 1, 0, paint); + + return createdBitmap; + } +} |