diff options
author | linus_lee <llee@cyngn.com> | 2014-09-02 18:57:39 -0700 |
---|---|---|
committer | linus_lee <llee@cyngn.com> | 2014-11-20 11:59:11 -0800 |
commit | 32e9492d3d5d0fae297af88865cfc3cfc22e27c1 (patch) | |
tree | a17d7d9233935af839885e3fffdfe0f9a5dc5eb1 /src/com/cyngn/eleven | |
parent | 08276a046ece9c2d25ee5db858bce822bfb0a292 (diff) | |
download | android_packages_apps_Eleven-32e9492d3d5d0fae297af88865cfc3cfc22e27c1.tar.gz android_packages_apps_Eleven-32e9492d3d5d0fae297af88865cfc3cfc22e27c1.tar.bz2 android_packages_apps_Eleven-32e9492d3d5d0fae297af88865cfc3cfc22e27c1.zip |
Eleven: Add blurring behind the now playing and the queue fragments and fixes a few bugs
In addition to blurring, hide non-english headers, and fix the gripper colors
Change-Id: I6899a5d1d3bb8c6c7711652a618b1bdc47abcb1f
Diffstat (limited to 'src/com/cyngn/eleven')
-rw-r--r-- | src/com/cyngn/eleven/adapters/SongAdapter.java | 21 | ||||
-rw-r--r-- | src/com/cyngn/eleven/cache/ImageFetcher.java | 10 | ||||
-rw-r--r-- | src/com/cyngn/eleven/cache/ImageWorker.java | 422 | ||||
-rw-r--r-- | src/com/cyngn/eleven/slidinguppanel/SlidingUpPanelLayout.java | 28 | ||||
-rw-r--r-- | src/com/cyngn/eleven/ui/activities/HomeActivity.java | 18 | ||||
-rw-r--r-- | src/com/cyngn/eleven/ui/fragments/QueueFragment.java | 2 | ||||
-rw-r--r-- | src/com/cyngn/eleven/ui/fragments/SongFragment.java | 2 | ||||
-rw-r--r-- | src/com/cyngn/eleven/utils/MusicUtils.java | 20 | ||||
-rw-r--r-- | src/com/cyngn/eleven/utils/SectionCreatorUtils.java | 9 | ||||
-rw-r--r-- | src/com/cyngn/eleven/widgets/BlurScrimImage.java | 73 |
10 files changed, 545 insertions, 60 deletions
diff --git a/src/com/cyngn/eleven/adapters/SongAdapter.java b/src/com/cyngn/eleven/adapters/SongAdapter.java index 54b0f23..1cefb05 100644 --- a/src/com/cyngn/eleven/adapters/SongAdapter.java +++ b/src/com/cyngn/eleven/adapters/SongAdapter.java @@ -97,15 +97,18 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba // padding doesn't apply to included layouts, so we need // to wrap it in a container and show/hide with the container PlayPauseProgressButton playPauseProgressButton = holder.mPlayPauseProgressButton.get(); - View playPauseContainer = holder.mPlayPauseProgressContainer.get(); - if (mCurrentlyPlayingSongId == dataHolder.mItemId) { - // make it visible - playPauseProgressButton.enableAndShow(); - playPauseContainer.setVisibility(View.VISIBLE); - } else { - // hide it - playPauseProgressButton.disableAndHide(); - playPauseContainer.setVisibility(View.GONE); + if (playPauseProgressButton != null) { + View playPauseContainer = holder.mPlayPauseProgressContainer.get(); + + if (mCurrentlyPlayingSongId == dataHolder.mItemId) { + // make it visible + playPauseProgressButton.enableAndShow(); + playPauseContainer.setVisibility(View.VISIBLE); + } else { + // hide it + playPauseProgressButton.disableAndHide(); + playPauseContainer.setVisibility(View.GONE); + } } return convertView; diff --git a/src/com/cyngn/eleven/cache/ImageFetcher.java b/src/com/cyngn/eleven/cache/ImageFetcher.java index 0d7c7dc..e647693 100644 --- a/src/com/cyngn/eleven/cache/ImageFetcher.java +++ b/src/com/cyngn/eleven/cache/ImageFetcher.java @@ -25,6 +25,7 @@ import com.cyngn.eleven.lastfm.MusicEntry; import com.cyngn.eleven.lastfm.ImageSize; import com.cyngn.eleven.utils.MusicUtils; import com.cyngn.eleven.utils.PreferenceUtils; +import com.cyngn.eleven.widgets.BlurScrimImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -160,6 +161,15 @@ public class ImageFetcher extends ImageWorker { } /** + * Used to fetch the current artwork blurred. + */ + public void loadCurrentBlurredArtwork(final BlurScrimImage image) { + loadBlurImage(generateAlbumCacheKey(MusicUtils.getAlbumName(), MusicUtils.getArtistName()), + MusicUtils.getArtistName(), MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(), + image, ImageType.ALBUM); + } + + /** * Used to fetch artist images. */ public void loadArtistImage(final String key, final ImageView imageView) { diff --git a/src/com/cyngn/eleven/cache/ImageWorker.java b/src/com/cyngn/eleven/cache/ImageWorker.java index 68553f4..2e63e4c 100644 --- a/src/com/cyngn/eleven/cache/ImageWorker.java +++ b/src/com/cyngn/eleven/cache/ImageWorker.java @@ -20,10 +20,17 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.AsyncTask; +import android.support.v7.graphics.Palette; +import android.support.v7.graphics.PaletteItem; +import android.support.v8.renderscript.Allocation; +import android.support.v8.renderscript.Element; +import android.support.v8.renderscript.RenderScript; +import android.support.v8.renderscript.ScriptIntrinsicBlur; import android.widget.ImageView; import com.cyngn.eleven.R; import com.cyngn.eleven.utils.ApolloUtils; +import com.cyngn.eleven.widgets.BlurScrimImage; import java.lang.ref.WeakReference; import java.util.concurrent.RejectedExecutionException; @@ -37,9 +44,19 @@ import java.util.concurrent.RejectedExecutionException; public abstract class ImageWorker { /** + * Render script + */ + private static RenderScript sRenderScript = null; + + /** * Default transition drawable fade time */ - private static final int FADE_IN_TIME = 200; + public static final int FADE_IN_TIME = 200; + + /** + * Default transition drawable fade time slow + */ + public static final int FADE_IN_TIME_SLOW = 1000; /** * The resources to use @@ -49,17 +66,17 @@ public abstract class ImageWorker { /** * First layer of the transition drawable */ - private final ColorDrawable mCurrentDrawable; + private final ColorDrawable mTransparentDrawable; /** - * Layer drawable used to cross fade the result from the worker + * Default album art */ - private final Drawable[] mArrayDrawable; + private final Bitmap mDefault; /** - * Default album art + * Default album art blurred */ - private final Bitmap mDefault; + private final Bitmap mDefaultBlur; /** * The Context to use @@ -78,15 +95,18 @@ public abstract class ImageWorker { */ protected ImageWorker(final Context context) { mContext = context.getApplicationContext(); + + if (sRenderScript == null) { + sRenderScript = RenderScript.create(mContext); + } + mResources = mContext.getResources(); // Create the default artwork mDefault = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_artwork)).getBitmap(); + // Create the default blurred artwork + mDefaultBlur = ((BitmapDrawable) mResources.getDrawable(R.drawable.default_artwork_blur)).getBitmap(); // Create the transparent layer for the transition drawable - mCurrentDrawable = new ColorDrawable(mResources.getColor(R.color.transparent)); - // A transparent image (layer 0) and the new result (layer 1) - mArrayDrawable = new Drawable[2]; - mArrayDrawable[0] = mCurrentDrawable; - // XXX The second layer is set in the worker task. + mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT); } /** @@ -153,7 +173,7 @@ public abstract class ImageWorker { /** * The actual {@link AsyncTask} that will process the image. */ - private final class BitmapWorkerTask extends AsyncTask<String, Void, TransitionDrawable> { + private class BitmapWorkerTask extends AsyncTask<String, Void, Object> { /** * The {@link ImageView} used to set the result @@ -191,6 +211,11 @@ public abstract class ImageWorker { private String mUrl; /** + * Layer drawable used to cross fade the result from the worker + */ + protected Drawable mFromDrawable; + + /** * Constructor of <code>BitmapWorkerTask</code> * * @param imageView The {@link ImageView} to use. @@ -200,13 +225,12 @@ public abstract class ImageWorker { public BitmapWorkerTask(final ImageView imageView, final ImageType imageType) { mImageReference = new WeakReference<ImageView>(imageView); mImageType = imageType; + + // A transparent image (layer 0) and the new result (layer 1) + mFromDrawable = mTransparentDrawable; } - /** - * {@inheritDoc} - */ - @Override - protected TransitionDrawable doInBackground(final String... params) { + protected Bitmap getBitmapInBackground(final String... params) { // Define the key mKey = params[0]; @@ -246,33 +270,33 @@ public abstract class ImageWorker { addBitmapToCache(mKey, bitmap); } - // Add the second layer to the transiation drawable - if (bitmap != null) { - final BitmapDrawable layerTwo = new BitmapDrawable(mResources, bitmap); - layerTwo.setFilterBitmap(false); - layerTwo.setDither(false); - mArrayDrawable[1] = layerTwo; - - // Finally, return the image - final TransitionDrawable result = new TransitionDrawable(mArrayDrawable); - result.setCrossFadeEnabled(true); - result.startTransition(FADE_IN_TIME); - return result; - } - return null; + return bitmap; } /** * {@inheritDoc} */ @Override - protected void onPostExecute(TransitionDrawable result) { + protected Object doInBackground(final String... params) { + final Bitmap bitmap = getBitmapInBackground(params); + return createImageTransitionDrawable(mResources, mFromDrawable, bitmap, + FADE_IN_TIME, false, false); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(Object result) { if (isCancelled()) { result = null; } + + TransitionDrawable transitionDrawable = (TransitionDrawable)result; + final ImageView imageView = getAttachedImageView(); - if (result != null && imageView != null) { - imageView.setImageDrawable(result); + if (transitionDrawable != null && imageView != null) { + imageView.setImageDrawable(transitionDrawable); } } @@ -281,7 +305,7 @@ public abstract class ImageWorker { * the ImageView's task still points to this task as well. * Returns null otherwise. */ - private final ImageView getAttachedImageView() { + protected ImageView getAttachedImageView() { final ImageView imageView = mImageReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask) { @@ -292,6 +316,258 @@ public abstract class ImageWorker { } /** + * This will download the image (if needed) and create a blur and set the scrim as well on the + * BlurScrimImage + */ + private class BlurBitmapWorkerTask extends BitmapWorkerTask { + // if the image is too small, the blur will look bad post scale up so we use the min size + // to scale up before bluring + private static final int MIN_BITMAP_SIZE = 500; + // number of times to run the blur + private static final int NUM_BLUR_RUNS = 8; + // 25f is the max blur radius possible + private static final float BLUR_RADIUS = 25f; + + // container for the result + private class ResultContainer { + public TransitionDrawable mImageViewBitmapDrawable; + public int mPaletteColor; + } + + /** + * The {@link BlurScrimImage} used to set the result + */ + private final WeakReference<BlurScrimImage> mBlurScrimImage; + + /** + * Constructor of <code>BitmapWorkerTask</code> + * + * @param blurScrimImage The {@link BlurScrimImage} to use. + * @param imageType The type of image URL to fetch for. + */ + @SuppressWarnings("deprecation") + public BlurBitmapWorkerTask(final BlurScrimImage blurScrimImage, final ImageType imageType) { + super(blurScrimImage.getImageView(), imageType); + mBlurScrimImage = new WeakReference<BlurScrimImage>(blurScrimImage); + + // use the existing image as the drawable and if it doesn't exist fallback to transparent + mFromDrawable = blurScrimImage.getImageView().getDrawable(); + if (mFromDrawable == null) { + mFromDrawable = mTransparentDrawable; + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Object doInBackground(final String... params) { + Bitmap bitmap = getBitmapInBackground(params); + + ResultContainer result = new ResultContainer(); + + Bitmap output = null; + + if (bitmap != null) { + // now create the blur bitmap + Bitmap input = bitmap; + + // if the image is too small, scale it up before running through the blur + if (input.getWidth() < MIN_BITMAP_SIZE || input.getHeight() < MIN_BITMAP_SIZE) { + float multiplier = Math.max(MIN_BITMAP_SIZE / (float)input.getWidth(), + MIN_BITMAP_SIZE / (float)input.getHeight()); + input = input.createScaledBitmap(bitmap, (int)(input.getWidth() * multiplier), + (int)(input.getHeight() * multiplier), true); + // since we created a new bitmap, we can re-use the bitmap for our output + output = input; + } else { + // if we aren't creating a new bitmap, create a new output bitmap + output = Bitmap.createBitmap(input.getWidth(), input.getHeight(), input.getConfig()); + } + + // run the blur multiple times + for (int i = 0; i < NUM_BLUR_RUNS; i++) { + final Allocation inputAlloc = Allocation.createFromBitmap(sRenderScript, input); + final Allocation outputAlloc = Allocation.createTyped(sRenderScript, + inputAlloc.getType()); + final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(sRenderScript, + Element.U8_4(sRenderScript)); + + script.setRadius(BLUR_RADIUS); + script.setInput(inputAlloc); + script.forEach(outputAlloc); + outputAlloc.copyTo(output); + + // if we run more than 1 blur, the new input should be the old output + input = output; + } + + // calculate the palette color + result.mPaletteColor = getPaletteColorInBackground(output); + } else { + // else transition to the default blur bitmap + output = mDefaultBlur; + } + + // create the bitmap transition drawable + result.mImageViewBitmapDrawable = createImageTransitionDrawable(mResources, mFromDrawable, + output, FADE_IN_TIME_SLOW, true, true); + + return result; + } + + /** + * This will get the most vibrant palette color for a bitmap + * @param input to process + * @return the most vibrant color or transparent if none found + */ + private int getPaletteColorInBackground(Bitmap input) { + int color = Color.TRANSPARENT; + + if (input != null) { + Palette palette = Palette.generate(input); + PaletteItem paletteItem = palette.getVibrantColor(); + + // keep walking through the palette items to find a color if we don't have any + if (paletteItem == null) { + paletteItem = palette.getVibrantColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getLightVibrantColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getLightMutedColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getLightMutedColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getDarkVibrantColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getMutedColor(); + } + + if (paletteItem == null) { + paletteItem = palette.getDarkMutedColor(); + } + + if (paletteItem != null) { + // grab the rgb values + color = paletteItem.getRgb() | 0xFFFFFF; + + // make it 20% opacity + color &= 0x33000000; + } + } + + return color; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(Object result) { + if (isCancelled()) { + result = null; + } + + ResultContainer resultContainer = (ResultContainer)result; + + BlurScrimImage blurScrimImage = mBlurScrimImage.get(); + if (blurScrimImage != null) { + TransitionDrawable paletteTransition = createPaletteTransition(blurScrimImage, + resultContainer.mPaletteColor); + + blurScrimImage.setTransitionDrawable(resultContainer.mImageViewBitmapDrawable, + paletteTransition); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected final ImageView getAttachedImageView() { + final BlurScrimImage blurImage = mBlurScrimImage.get(); + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(blurImage); + if (this == bitmapWorkerTask) { + return blurImage.getImageView(); + } + return null; + } + } + + /** + * Creates a transition drawable to Bitmap with params + * @param resources Android Resources! + * @param fromDrawable the drawable to transition from + * @param bitmap the bitmap to transition to + * @param fadeTime the fade time in MS to fade in + * @param dither setting + * @param force force create a transition even if bitmap == null (fade to transparent) + * @return the drawable if created, null otherwise + */ + public static TransitionDrawable createImageTransitionDrawable(final Resources resources, + final Drawable fromDrawable, final Bitmap bitmap, final int fadeTime, + final boolean dither, final boolean force) { + if (bitmap != null || force) { + final Drawable[] arrayDrawable = new Drawable[2]; + arrayDrawable[0] = fromDrawable; + + // Add the transition to drawable + Drawable layerTwo; + if (bitmap != null) { + layerTwo = new BitmapDrawable(resources, bitmap); + layerTwo.setFilterBitmap(false); + layerTwo.setDither(dither); + } else { + // if no bitmap (forced) then transition to transparent + layerTwo = new ColorDrawable(Color.TRANSPARENT); + } + + arrayDrawable[1] = layerTwo; + + // Finally, return the image + final TransitionDrawable result = new TransitionDrawable(arrayDrawable); + result.setCrossFadeEnabled(true); + result.startTransition(fadeTime); + return result; + } + + return null; + } + + /** + * This will create the palette transition from the original color to the new one + * @param scrimImage the container to change the color for + * @param color the color to transition to + * @return the transition to run + */ + public static TransitionDrawable createPaletteTransition(BlurScrimImage scrimImage, int color) { + final Drawable[] arrayDrawable = new Drawable[2]; + arrayDrawable[0] = scrimImage.getBackground(); + + if (arrayDrawable[0] == null) { + arrayDrawable[0] = new ColorDrawable(Color.TRANSPARENT); + } + + arrayDrawable[1] = new ColorDrawable(color); + + // create the transition + final TransitionDrawable result = new TransitionDrawable(arrayDrawable); + result.setCrossFadeEnabled(true); + result.startTransition(FADE_IN_TIME_SLOW); + return result; + } + + /** * Calls {@code cancel()} in the worker task * * @param imageView the {@link ImageView} to use @@ -310,6 +586,26 @@ public abstract class ImageWorker { */ public static final boolean executePotentialWork(final Object data, final ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + return executePotentialWork(data, bitmapWorkerTask); + } + + /** + * Returns true if the current work has been canceled or if there was no + * work in progress on this image view. Returns false if the work in + * progress deals with the same data. The work is not stopped in that case. + */ + public static final boolean executePotentialWork(final Object data, final BlurScrimImage image) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(image); + return executePotentialWork(data, bitmapWorkerTask); + } + + /** + * Returns true if the current work has been canceled or if there was no + * work in progress on this image view. Returns false if the work in + * progress deals with the same data. The work is not stopped in that case. + */ + public static final boolean executePotentialWork(final Object data, + final BitmapWorkerTask bitmapWorkerTask) { if (bitmapWorkerTask != null) { final Object bitmapData = bitmapWorkerTask.mKey; if (bitmapData == null || !bitmapData.equals(data)) { @@ -342,6 +638,24 @@ public abstract class ImageWorker { } /** + * Used to determine if the current image drawable has an instance of + * {@link BitmapWorkerTask} + * + * @param image Any {@link BlurScrimImage}. + * @return Retrieve the currently active work task (if any) associated with + * this {@link BlurScrimImage}. null if there is no such task. + */ + private static final BitmapWorkerTask getBitmapWorkerTask(final BlurScrimImage image) { + if (image != null) { + final AsyncDrawable asyncDrawable = (AsyncDrawable)image.getTag(); + if (asyncDrawable != null) { + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + /** * A custom {@link BitmapDrawable} that will be attached to the * {@link ImageView} while the work is in progress. Contains a reference to * the actual worker task, so that it can be stopped if a new binding is @@ -417,6 +731,44 @@ public abstract class ImageWorker { } /** + * Called to fetch the blurred artist or album art. + * + * @param key The unique identifier for the image. + * @param artistName The artist name for the Last.fm API. + * @param albumName The album name for the Last.fm API. + * @param albumId The album art index, to check for missing artwork. + * @param blurScrimImage The {@link BlurScrimImage} used to set the cached + * {@link Bitmap}. + * @param imageType The type of image URL to fetch for. + */ + protected void loadBlurImage(final String key, final String artistName, final String albumName, + final long albumId, final BlurScrimImage blurScrimImage, final ImageType imageType) { + if (key == null || mImageCache == null || blurScrimImage == null) { + return; + } + + // Create the blur key + final String blurKey = key + "_blur"; + + if (executePotentialWork(blurKey, blurScrimImage) + && blurScrimImage != null && !mImageCache.isDiskCachePaused()) { + // Otherwise run the worker task + final BlurBitmapWorkerTask blurWorkerTask = new BlurBitmapWorkerTask(blurScrimImage, imageType); + final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mDefault, + blurWorkerTask); + blurScrimImage.setTag(asyncDrawable); + + try { + ApolloUtils.execute(false, blurWorkerTask, key, + artistName, albumName, String.valueOf(albumId)); + } catch (RejectedExecutionException e) { + // Executor has exhausted queue space, show default artwork + blurScrimImage.transitionToDefaultState(); + } + } + } + + /** * Subclasses should override this to define any processing or work that * must happen to produce the final {@link Bitmap}. This will be executed in * a background thread and be long running. diff --git a/src/com/cyngn/eleven/slidinguppanel/SlidingUpPanelLayout.java b/src/com/cyngn/eleven/slidinguppanel/SlidingUpPanelLayout.java index 49f7f0b..a1b823d 100644 --- a/src/com/cyngn/eleven/slidinguppanel/SlidingUpPanelLayout.java +++ b/src/com/cyngn/eleven/slidinguppanel/SlidingUpPanelLayout.java @@ -160,6 +160,11 @@ public class SlidingUpPanelLayout extends ViewGroup { private View mMainView; /** + * The background view + */ + private View mBackgroundView; + + /** * Current state of the slideable view. */ private enum SlideState { @@ -572,7 +577,7 @@ public class SlidingUpPanelLayout extends ViewGroup { } else { left = right = top = bottom = 0; } - View child = getChildAt(0); + View child = mMainView; final int clampedChildLeft = Math.max(leftBound, child.getLeft()); final int clampedChildTop = Math.max(topBound, child.getTop()); final int clampedChildRight = Math.min(rightBound, child.getRight()); @@ -628,12 +633,19 @@ public class SlidingUpPanelLayout extends ViewGroup { final int childCount = getChildCount(); - if (childCount != 2) { - throw new IllegalStateException("Sliding up panel layout must have exactly 2 children!"); + if (childCount != 2 && childCount != 3) { + throw new IllegalStateException("Sliding up panel layout must have exactly 2 or 3 children!"); + } + + if (childCount == 2) { + mMainView = getChildAt(0); + mSlideableView = getChildAt(1); + } else { + mBackgroundView = getChildAt(0); + mMainView = getChildAt(1); + mSlideableView = getChildAt(2); } - mMainView = getChildAt(0); - mSlideableView = getChildAt(1); if (mDragView == null) { setDragView(mSlideableView); } @@ -651,7 +663,7 @@ public class SlidingUpPanelLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); // We always measure the sliding panel in order to know it's height (needed for show panel) - if (child.getVisibility() == GONE && i == 0) { + if (child.getVisibility() == GONE && child == mMainView) { continue; } @@ -718,7 +730,7 @@ public class SlidingUpPanelLayout extends ViewGroup { final View child = getChildAt(i); // Always layout the sliding view on the first layout - if (child.getVisibility() == GONE && (i == 0 || mFirstLayout)) { + if (child.getVisibility() == GONE && (child == mMainView || mFirstLayout)) { continue; } @@ -1027,7 +1039,7 @@ public class SlidingUpPanelLayout extends ViewGroup { boolean result; final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); - if (isSlidingEnabled() && mSlideableView != child) { + if (isSlidingEnabled() && mMainView == child) { // Clip against the slider; no sense drawing what will immediately be covered, // Unless the panel is set to overlay content if (!mOverlayContent) { diff --git a/src/com/cyngn/eleven/ui/activities/HomeActivity.java b/src/com/cyngn/eleven/ui/activities/HomeActivity.java index 5744c6c..98dc16e 100644 --- a/src/com/cyngn/eleven/ui/activities/HomeActivity.java +++ b/src/com/cyngn/eleven/ui/activities/HomeActivity.java @@ -12,26 +12,27 @@ package com.cyngn.eleven.ui.activities; import android.content.Intent; +import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.view.View; -import android.widget.TextView; +import android.widget.ImageView; import com.cyngn.eleven.R; import com.cyngn.eleven.slidinguppanel.SlidingUpPanelLayout; import com.cyngn.eleven.slidinguppanel.SlidingUpPanelLayout.SimplePanelSlideListener; import com.cyngn.eleven.ui.HeaderBar; import com.cyngn.eleven.ui.fragments.AudioPlayerFragment; -import com.cyngn.eleven.ui.fragments.QueueFragment; import com.cyngn.eleven.ui.fragments.phone.MusicBrowserPhoneFragment; import com.cyngn.eleven.utils.ApolloUtils; import com.cyngn.eleven.utils.MusicUtils; +import com.cyngn.eleven.widgets.BlurScrimImage; /** * This class is used to display the {@link ViewPager} used to swipe between the * main {@link Fragment}s used to browse the user's music. - * + * * @author Andrew Neal (andrewdneal@gmail.com) */ public class HomeActivity extends BaseActivity { @@ -50,6 +51,9 @@ public class HomeActivity extends BaseActivity { private SlidingUpPanelLayout mSecondPanel; private HeaderBar mSecondHeaderBar; + // this is the blurred image that goes behind the now playing and queue fragments + private BlurScrimImage mBlurScrimImage; + /** * {@inheritDoc} */ @@ -65,6 +69,10 @@ public class HomeActivity extends BaseActivity { setupFirstPanel(); setupSecondPanel(); + // get the blur scrim image + findViewById(R.id.bottom_action_bar_parent).setBackgroundColor(Color.TRANSPARENT); + mBlurScrimImage = (BlurScrimImage)findViewById(R.id.blurScrimImage); + // if we've been launched by an intent, parse it Intent launchIntent = getIntent(); if (launchIntent != null) { @@ -215,6 +223,9 @@ public class HomeActivity extends BaseActivity { protected void updateMetaInfo() { super.updateMetaInfo(); + // load the blurred image + mBlurScrimImage.loadBlurImage(ApolloUtils.getImageFetcher(this)); + // Set the artist name mFirstHeaderBar.setTitleText(MusicUtils.getArtistName()); } @@ -228,6 +239,7 @@ public class HomeActivity extends BaseActivity { final HeaderBar headerBar = (HeaderBar) findViewById(containerId); headerBar.setTitleText(textId); headerBar.setupCustomButton(customIconId, listener); + headerBar.setBackgroundColor(Color.TRANSPARENT); return headerBar; } diff --git a/src/com/cyngn/eleven/ui/fragments/QueueFragment.java b/src/com/cyngn/eleven/ui/fragments/QueueFragment.java index 2f06b62..1aafce8 100644 --- a/src/com/cyngn/eleven/ui/fragments/QueueFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/QueueFragment.java @@ -130,7 +130,7 @@ public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the adpater - mAdapter = new SongAdapter(getActivity(), R.layout.edit_track_list_item); + mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item); } /** diff --git a/src/com/cyngn/eleven/ui/fragments/SongFragment.java b/src/com/cyngn/eleven/ui/fragments/SongFragment.java index 444d81f..cc35a3b 100644 --- a/src/com/cyngn/eleven/ui/fragments/SongFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/SongFragment.java @@ -134,7 +134,7 @@ public class SongFragment extends Fragment implements LoaderCallbacks<SectionLis public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the adpater - mAdapter = new SectionAdapter<Song, SongAdapter>(getActivity(), new SongAdapter(getActivity(), R.layout.list_item_song)); + mAdapter = new SectionAdapter<Song, SongAdapter>(getActivity(), new SongAdapter(getActivity(), R.layout.list_item_simple)); } /** diff --git a/src/com/cyngn/eleven/utils/MusicUtils.java b/src/com/cyngn/eleven/utils/MusicUtils.java index d8b5e98..698a9b4 100644 --- a/src/com/cyngn/eleven/utils/MusicUtils.java +++ b/src/com/cyngn/eleven/utils/MusicUtils.java @@ -1367,6 +1367,10 @@ public final class MusicUtils { * @return the localized label of the bucket that the name falls into */ public static String getLocalizedBucketLetter(String name, boolean trimName) { + if (name == null || name.length() == 0) { + return null; + } + if (trimName) { name = name.trim().toLowerCase(); if (name.startsWith("the ")) { @@ -1387,7 +1391,21 @@ public final class MusicUtils { } if (name.length() > 0) { - return LocaleUtils.getInstance().getLabel(name); + String lbl = LocaleUtils.getInstance().getLabel(name); + // For now let's cap it to latin alphabet and the # sign + // since chinese characters are resulting in " " and other random + // characters but the sort doesn't match the sql sort so it is + // not quite sorted + if (lbl != null && lbl.length() > 0) { + char ch = lbl.charAt(0); + if (ch < 'A' && ch > 'Z' && ch != '#') { + return null; + } + } + + if (lbl != null && lbl.length() > 0) { + return lbl; + } } return null; diff --git a/src/com/cyngn/eleven/utils/SectionCreatorUtils.java b/src/com/cyngn/eleven/utils/SectionCreatorUtils.java index 8247cb9..9d563d2 100644 --- a/src/com/cyngn/eleven/utils/SectionCreatorUtils.java +++ b/src/com/cyngn/eleven/utils/SectionCreatorUtils.java @@ -50,8 +50,13 @@ public class SectionCreatorUtils { @Override public String createSectionSeparator(T first, T second) { String secondLabel = createLabel(second); - if (first == null || !createLabel(first).equals(secondLabel)) { - return createLabel(second); + // if we can't determine a good label then don't bother creating a section + if (secondLabel == null) { + return null; + } + + if (first == null || !secondLabel.equals(createLabel(first))) { + return secondLabel; } return null; diff --git a/src/com/cyngn/eleven/widgets/BlurScrimImage.java b/src/com/cyngn/eleven/widgets/BlurScrimImage.java new file mode 100644 index 0000000..e9be8f9 --- /dev/null +++ b/src/com/cyngn/eleven/widgets/BlurScrimImage.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Cyanogen, Inc. + */ +package com.cyngn.eleven.widgets; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.TransitionDrawable; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.cyngn.eleven.R; +import com.cyngn.eleven.cache.ImageFetcher; +import com.cyngn.eleven.cache.ImageWorker; + +public class BlurScrimImage extends FrameLayout { + private ImageView mImageView; + + public BlurScrimImage(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mImageView = (ImageView)findViewById(R.id.blurImage); + } + + public ImageView getImageView() { + return mImageView; + } + + /** + * Transitions the image to the default state (default blur artwork) + */ + public void transitionToDefaultState() { + Bitmap blurredBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.default_artwork_blur)).getBitmap(); + + TransitionDrawable imageTransition = ImageWorker.createImageTransitionDrawable(getResources(), + mImageView.getDrawable(), blurredBitmap, ImageWorker.FADE_IN_TIME_SLOW, true, true); + + TransitionDrawable paletteTransition = ImageWorker.createPaletteTransition(this, + Color.TRANSPARENT); + + + setTransitionDrawable(imageTransition, paletteTransition); + } + + /** + * Sets the transition drawable + * @param imageTransition the transition for the imageview + * @param paletteTransition the transition for the scrim overlay + */ + public void setTransitionDrawable(TransitionDrawable imageTransition, + TransitionDrawable paletteTransition) { + setBackground(paletteTransition); + mImageView.setImageDrawable(imageTransition); + } + + /** + * Loads the current artwork into this BlurScrimImage + * @param imageFetcher an ImageFetcher instance + */ + public void loadBlurImage(ImageFetcher imageFetcher) { + imageFetcher.loadCurrentBlurredArtwork(this); + } +} |