From 95abbb330ce9bbaf23594245f0f8a795c8118038 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 4 Aug 2014 10:53:22 -0700 Subject: [PATCH] Updating the virtual preloader UX. > No click feedback when in preloader mode > No preloader UI when drawn in drag layer > The preloader consists of a background 9 patch image and a circular progress is drawn in the content region of the background. > The preloader is drawn in a slightly larget area than the actual bounds to make the circular progress more prominent compared to the icon. issue: 15835307 Change-Id: Ifec3d93ecf1fac994d1128b517da3797247e7ed6 --- res/drawable-xxhdpi/virtual_preload.9.png | Bin 0 -> 16628 bytes res/drawable-xxhdpi/virtual_preload_folder.9.png | Bin 0 -> 7697 bytes res/values/attrs.xml | 6 + res/values/styles.xml | 12 ++ src/com/android/launcher3/BubbleTextView.java | 36 ++++- src/com/android/launcher3/DragLayer.java | 4 + src/com/android/launcher3/FastBitmapDrawable.java | 88 +++++++---- src/com/android/launcher3/Folder.java | 24 +-- src/com/android/launcher3/FolderIcon.java | 4 +- src/com/android/launcher3/PreloadIconDrawable.java | 173 +++++++++++++++------ src/com/android/launcher3/Utilities.java | 81 ++++++++++ src/com/android/launcher3/Workspace.java | 54 ++++--- 12 files changed, 363 insertions(+), 119 deletions(-) create mode 100644 res/drawable-xxhdpi/virtual_preload.9.png create mode 100644 res/drawable-xxhdpi/virtual_preload_folder.9.png diff --git a/res/drawable-xxhdpi/virtual_preload.9.png b/res/drawable-xxhdpi/virtual_preload.9.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec1740bfb8f6878deb3f12397e81d0b447de50a GIT binary patch literal 16628 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�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 + + + + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index 462c29239..6079eee3b 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -161,6 +161,18 @@ 2.0 + + + + diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index ab94814c3..5c2bb9946 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -28,6 +29,7 @@ import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; +import android.util.SparseArray; import android.util.TypedValue; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -39,6 +41,9 @@ import android.widget.TextView; * too aggressive. */ public class BubbleTextView extends TextView { + + private static SparseArray sPreloaderThemes = new SparseArray<>(2); + static final float SHADOW_LARGE_RADIUS = 4.0f; static final float SHADOW_SMALL_RADIUS = 1.75f; static final float SHADOW_Y_OFFSET = 2.0f; @@ -128,10 +133,7 @@ public class BubbleTextView extends TextView { LauncherAppState app = LauncherAppState.getInstance(); FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); - if (info.isDisabled) { - iconDrawable.setSaturation(0); - iconDrawable.setBrightness(20); - } + iconDrawable.setGhostModeEnabled(info.isDisabled); setCompoundDrawables(null, iconDrawable, null, null); if (setDefaultPadding) { @@ -315,7 +317,9 @@ public class BubbleTextView extends TextView { } void setCellLayoutPressedOrFocusedIcon() { - if (getParent() instanceof ShortcutAndWidgetContainer) { + // Disable pressed state when the icon is in preloader state. + if ((getParent() instanceof ShortcutAndWidgetContainer) && + !(getCompoundDrawables()[1] instanceof PreloadIconDrawable)){ ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent(); if (parent != null) { CellLayout layout = (CellLayout) parent.getParent(); @@ -385,7 +389,13 @@ public class BubbleTextView extends TextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (mBackground != null) mBackground.setCallback(this); + Drawable top = getCompoundDrawables()[1]; + + if (top instanceof PreloadIconDrawable) { + ((PreloadIconDrawable) top).applyTheme(getPreloaderTheme()); + } mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -466,7 +476,7 @@ public class BubbleTextView extends TextView { if (top instanceof PreloadIconDrawable) { preloadDrawable = (PreloadIconDrawable) top; } else { - preloadDrawable = new PreloadIconDrawable(top, getResources()); + preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme()); setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]); } @@ -478,4 +488,18 @@ public class BubbleTextView extends TextView { } } } + + private Theme getPreloaderTheme() { + Object tag = getTag(); + int style = ((tag != null) && (tag instanceof ShortcutInfo) && + (((ShortcutInfo) tag).container >= 0)) ? R.style.PreloadIcon_Folder + : R.style.PreloadIcon; + Theme theme = sPreloaderThemes.get(style); + if (theme == null) { + theme = getResources().newTheme(); + theme.applyStyle(style, true); + sPreloaderThemes.put(style, theme); + } + return theme; + } } diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index 8bcc407d6..80f8dfce8 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -566,6 +566,10 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang // the drag view about the scaled child view. toY += Math.round(toScale * tv.getPaddingTop()); toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2; + if (dragView.getDragVisualizeOffset() != null) { + toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y); + } + toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; } else if (child instanceof FolderIcon) { // Account for holographic blur padding on the drag view diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index ef8d0973d..cf7c22eef 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -28,15 +28,17 @@ import android.graphics.drawable.Drawable; class FastBitmapDrawable extends Drawable { - private static final ColorMatrix sTempSaturationMatrix = new ColorMatrix(); - private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix(); + private static ColorMatrix sGhostModeMatrix; + private static final ColorMatrix sTempMatrix = new ColorMatrix(); + + private static final int GHOST_MODE_MIN_COLOR_RANGE = 130; private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private Bitmap mBitmap; private int mAlpha; - private float mSatutation = 1; private int mBrightness = 0; + private boolean mGhostModeEnabled = false; FastBitmapDrawable(Bitmap b) { mAlpha = 255; @@ -101,13 +103,19 @@ class FastBitmapDrawable extends Drawable { return mBitmap; } - public float getSaturation() { - return mSatutation; + /** + * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost' + * appearance. + */ + public void setGhostModeEnabled(boolean enabled) { + if (mGhostModeEnabled != enabled) { + mGhostModeEnabled = enabled; + updateFilter(); + } } - public void setSaturation(float saturation) { - mSatutation = saturation; - updateFilter(); + public boolean isGhostModeEnabled() { + return mGhostModeEnabled; } public int getBrightness() { @@ -115,36 +123,58 @@ class FastBitmapDrawable extends Drawable { } public void addBrightness(int amount) { - mBrightness += amount; - updateFilter(); + setBrightness(mBrightness + amount); } public void setBrightness(int brightness) { - mBrightness = brightness; - updateFilter(); + if (mBrightness != brightness) { + mBrightness = brightness; + updateFilter(); + } } private void updateFilter() { - if (mSatutation != 1 || mBrightness != 0) { - sTempSaturationMatrix.setSaturation(mSatutation); - - if (mBrightness != 0) { - // Brightness: C-new = C-old*(1-amount) + amount - float scale = 1 - mBrightness / 255.0f; - sTempBrightnessMatrix.setScale(scale, scale, scale, 1); - float[] array = sTempBrightnessMatrix.getArray(); - - // Add the amount to RGB components of the matrix, as per the above formula. - // Fifth elements in the array correspond to the constant being added to - // red, blue, green, and alpha channel respectively. - array[4] = mBrightness; - array[9] = mBrightness; - array[14] = mBrightness; - sTempSaturationMatrix.preConcat(sTempBrightnessMatrix); + if (mGhostModeEnabled) { + if (sGhostModeMatrix == null) { + sGhostModeMatrix = new ColorMatrix(); + sGhostModeMatrix.setSaturation(0); + + // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255] + float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f; + sTempMatrix.set(new float[] { + range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, 0, 0, 1, 0 }); + sGhostModeMatrix.preConcat(sTempMatrix); + } + + if (mBrightness == 0) { + mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix)); + } else { + setBrightnessMatrix(sTempMatrix, mBrightness); + sTempMatrix.postConcat(sGhostModeMatrix); + mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix)); } - mPaint.setColorFilter(new ColorMatrixColorFilter(sTempSaturationMatrix)); + } else if (mBrightness != 0) { + setBrightnessMatrix(sTempMatrix, mBrightness); + mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix)); } else { mPaint.setColorFilter(null); } } + + private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) { + // Brightness: C-new = C-old*(1-amount) + amount + float scale = 1 - brightness / 255.0f; + matrix.setScale(scale, scale, scale, 1); + float[] array = matrix.getArray(); + + // Add the amount to RGB components of the matrix, as per the above formula. + // Fifth elements in the array correspond to the constant being added to + // red, blue, green, and alpha channel respectively. + array[4] = brightness; + array[9] = brightness; + array[14] = brightness; + } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 655e5c368..fcedaead3 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -1182,6 +1182,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (mIsExternalDrag) { si.cellX = mEmptyCell[0]; si.cellY = mEmptyCell[1]; + + // Actually move the item in the database if it was an external drag. Call this + // before creating the view, so that ShortcutInfo is updated appropriately. + LauncherModel.addOrMoveItemInDatabase( + mLauncher, si, mInfo.id, 0, si.cellX, si.cellY); + + // We only need to update the locations if it doesn't get handled in #onDropCompleted. + if (d.dragSource != this) { + updateItemLocationsInDatabaseBatch(); + } + mIsExternalDrag = false; + currentDragView = createAndAddShortcut(si); } else { currentDragView = mCurrentDragView; @@ -1209,18 +1221,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mItemsInvalidated = true; setupContentDimensions(getItemCount()); - // Actually move the item in the database if it was an external drag. - if (mIsExternalDrag) { - LauncherModel.addOrMoveItemInDatabase( - mLauncher, si, mInfo.id, 0, si.cellX, si.cellY); - - // We only need to update the locations if it doesn't get handled in #onDropCompleted. - if (d.dragSource != this) { - updateItemLocationsInDatabaseBatch(); - } - mIsExternalDrag = false; - } - // Temporarily suppress the listener, as we did all the work already here. mSuppressOnAdd = true; mInfo.add(si); diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 5b49fb87a..c0b9da7d4 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -380,7 +380,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { // These correspond two the drawable and view that the icon was dropped _onto_ - Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1]; + Drawable animateDrawable = getTopDrawable((TextView) destView); computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth()); @@ -394,7 +394,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) { - Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1]; + Drawable animateDrawable = getTopDrawable((TextView) finalView); computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), finalView.getMeasuredWidth()); diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java index d9365cc1f..2972c4f9b 100644 --- a/src/com/android/launcher3/PreloadIconDrawable.java +++ b/src/com/android/launcher3/PreloadIconDrawable.java @@ -1,96 +1,143 @@ package com.android.launcher3; import android.animation.ObjectAnimator; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; class PreloadIconDrawable extends Drawable { + private static final float ANIMATION_PROGRESS_STOPPED = -1.0f; private static final float ANIMATION_PROGRESS_STARTED = 0f; private static final float ANIMATION_PROGRESS_COMPLETED = 1.0f; - private static final float ICON_SCALE_FACTOR = 0.6f; + private static final float MIN_SATUNATION = 0.2f; + private static final float MIN_LIGHTNESS = 0.6f; + + private static final float ICON_SCALE_FACTOR = 0.5f; + private static final int DEFAULT_COLOR = 0xFF009688; - private static Bitmap sProgressBg, sProgressFill; + private static final Rect sTempRect = new Rect(); - private final Rect mCanvasClipRect = new Rect(); - private final RectF mRect = new RectF(); - private final Path mProgressPath = new Path(); - private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + private final RectF mIndicatorRect = new RectF(); + private boolean mIndicatorRectDirty; + private final Paint mPaint; final Drawable mIcon; + private Drawable mBgDrawable; + private int mRingOutset; + + private int mIndicatorColor = 0; + /** * Indicates the progress of the preloader [0-100]. If it goes above 100, only the icon * is shown with no progress bar. */ private int mProgress = 0; - private boolean mPathChanged; private float mAnimationProgress = ANIMATION_PROGRESS_STOPPED; private ObjectAnimator mAnimator; - public PreloadIconDrawable(Drawable icon, Resources res) { + public PreloadIconDrawable(Drawable icon, Theme theme) { mIcon = icon; + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeCap(Paint.Cap.ROUND); + setBounds(icon.getBounds()); - mPathChanged = false; + applyTheme(theme); + onLevelChange(0); + } - if (sProgressBg == null) { - sProgressBg = BitmapFactory.decodeResource(res, R.drawable.bg_preloader); - } - if (sProgressFill == null) { - sProgressFill = BitmapFactory.decodeResource(res, R.drawable.bg_preloader_progress); + @Override + public void applyTheme(Theme t) { + TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable); + mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background); + mBgDrawable.setFilterBitmap(true); + mPaint.setStrokeWidth(ta.getDimension(R.styleable.PreloadIconDrawable_indicatorSize, 0)); + mRingOutset = ta.getDimensionPixelSize(R.styleable.PreloadIconDrawable_ringOutset, 0); + ta.recycle(); + onBoundsChange(getBounds()); + invalidateSelf(); + } + + @Override + protected void onBoundsChange(Rect bounds) { + mIcon.setBounds(bounds); + if (mBgDrawable != null) { + sTempRect.set(bounds); + sTempRect.inset(-mRingOutset, -mRingOutset); + mBgDrawable.setBounds(sTempRect); } + mIndicatorRectDirty = true; + } + + public int getOutset() { + return mRingOutset; + } + + /** + * The size of the indicator is same as the content region of the {@link #mBgDrawable} minus + * half the stroke size to accommodate the indicator. + */ + private void initIndicatorRect() { + Drawable d = mBgDrawable; + Rect bounds = d.getBounds(); + + d.getPadding(sTempRect); + // Amount by which padding has to be scaled + float paddingScaleX = ((float) bounds.width()) / d.getIntrinsicWidth(); + float paddingScaleY = ((float) bounds.height()) / d.getIntrinsicHeight(); + mIndicatorRect.set( + bounds.left + sTempRect.left * paddingScaleX, + bounds.top + sTempRect.top * paddingScaleY, + bounds.right - sTempRect.right * paddingScaleX, + bounds.bottom - sTempRect.bottom * paddingScaleY); + + float inset = mPaint.getStrokeWidth() / 2; + mIndicatorRect.inset(inset, inset); + mIndicatorRectDirty = false; } @Override public void draw(Canvas canvas) { - final Rect r = getBounds(); - if (canvas.getClipBounds(mCanvasClipRect) && !Rect.intersects(mCanvasClipRect, r)) { + final Rect r = new Rect(getBounds()); + if (canvas.getClipBounds(sTempRect) && !Rect.intersects(sTempRect, r)) { // The draw region has been clipped. return; } + if (mIndicatorRectDirty) { + initIndicatorRect(); + } final float iconScale; if ((mAnimationProgress >= ANIMATION_PROGRESS_STARTED) && (mAnimationProgress < ANIMATION_PROGRESS_COMPLETED)) { mPaint.setAlpha((int) ((1 - mAnimationProgress) * 255)); - canvas.drawBitmap(sProgressBg, null, r, mPaint); - canvas.drawBitmap(sProgressFill, null, r, mPaint); - iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress; + mBgDrawable.setAlpha(mPaint.getAlpha()); + mBgDrawable.draw(canvas); + canvas.drawOval(mIndicatorRect, mPaint); + iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress; } else if (mAnimationProgress == ANIMATION_PROGRESS_STOPPED) { mPaint.setAlpha(255); iconScale = ICON_SCALE_FACTOR; - canvas.drawBitmap(sProgressBg, null, r, mPaint); + mBgDrawable.setAlpha(255); + mBgDrawable.draw(canvas); if (mProgress >= 100) { - canvas.drawBitmap(sProgressFill, null, r, mPaint); + canvas.drawOval(mIndicatorRect, mPaint); } else if (mProgress > 0) { - if (mPathChanged) { - mProgressPath.reset(); - mProgressPath.moveTo(r.exactCenterX(), r.centerY()); - - mRect.set(r); - mProgressPath.arcTo(mRect, -90, mProgress * 3.6f); - mProgressPath.close(); - mPathChanged = false; - } - - canvas.save(); - canvas.clipPath(mProgressPath); - canvas.drawBitmap(sProgressFill, null, r, mPaint); - canvas.restore(); + canvas.drawArc(mIndicatorRect, -90, mProgress * 3.6f, false, mPaint); } } else { iconScale = 1; @@ -103,12 +150,6 @@ class PreloadIconDrawable extends Drawable { } @Override - protected void onBoundsChange(Rect bounds) { - mIcon.setBounds(bounds); - mPathChanged = true; - } - - @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @@ -126,7 +167,6 @@ class PreloadIconDrawable extends Drawable { @Override protected boolean onLevelChange(int level) { mProgress = level; - mPathChanged = true; // Stop Animation if (mAnimator != null) { @@ -134,6 +174,14 @@ class PreloadIconDrawable extends Drawable { mAnimator = null; } mAnimationProgress = ANIMATION_PROGRESS_STOPPED; + if (level > 0) { + // Set the paint color only when the level changes, so that the dominant color + // is only calculated when needed. + mPaint.setColor(getIndicatorColor()); + } + if (mIcon instanceof FastBitmapDrawable) { + ((FastBitmapDrawable) mIcon).setGhostModeEnabled(level <= 0); + } invalidateSelf(); return true; @@ -165,4 +213,37 @@ class PreloadIconDrawable extends Drawable { public float getAnimationProgress() { return mAnimationProgress; } + + @Override + public int getIntrinsicHeight() { + return mIcon.getIntrinsicHeight(); + } + + @Override + public int getIntrinsicWidth() { + return mIcon.getIntrinsicWidth(); + } + + private int getIndicatorColor() { + if (mIndicatorColor != 0) { + return mIndicatorColor; + } + if (!(mIcon instanceof FastBitmapDrawable)) { + mIndicatorColor = DEFAULT_COLOR; + return mIndicatorColor; + } + mIndicatorColor = Utilities.findDominantColorByHue( + ((FastBitmapDrawable) mIcon).getBitmap(), 20); + + // Make sure that the dominant color has enough saturation to be visible properly. + float[] hsv = new float[3]; + Color.colorToHSV(mIndicatorColor, hsv); + if (hsv[1] < MIN_SATUNATION) { + mIndicatorColor = DEFAULT_COLOR; + return mIndicatorColor; + } + hsv[2] = Math.max(MIN_LIGHTNESS, hsv[2]); + mIndicatorColor = Color.HSVToColor(hsv); + return mIndicatorColor; + } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 0a711c5dd..87c2d7515 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; @@ -39,6 +40,7 @@ import android.graphics.drawable.PaintDrawable; import android.os.Build; import android.util.DisplayMetrics; import android.util.Log; +import android.util.SparseArray; import android.view.View; import android.widget.Toast; @@ -393,4 +395,83 @@ public final class Utilities { return false; } } + + /** + * This picks a dominant color, looking for high-saturation, high-value, repeated hues. + * @param bitmap The bitmap to scan + * @param samples The approximate max number of samples to use. + */ + static int findDominantColorByHue(Bitmap bitmap, int samples) { + final int height = bitmap.getHeight(); + final int width = bitmap.getWidth(); + int sampleStride = (int) Math.sqrt((height * width) / samples); + if (sampleStride < 1) { + sampleStride = 1; + } + + // This is an out-param, for getting the hsv values for an rgb + float[] hsv = new float[3]; + + // First get the best hue, by creating a histogram over 360 hue buckets, + // where each pixel contributes a score weighted by saturation, value, and alpha. + float[] hueScoreHistogram = new float[360]; + float highScore = -1; + int bestHue = -1; + + for (int y = 0; y < height; y += sampleStride) { + for (int x = 0; x < width; x += sampleStride) { + int argb = bitmap.getPixel(x, y); + int alpha = 0xFF & (argb >> 24); + if (alpha < 0x80) { + // Drop mostly-transparent pixels. + continue; + } + // Remove the alpha channel. + int rgb = argb | 0xFF000000; + Color.colorToHSV(rgb, hsv); + // Bucket colors by the 360 integer hues. + int hue = (int) hsv[0]; + if (hue < 0 || hue >= hueScoreHistogram.length) { + // Defensively avoid array bounds violations. + continue; + } + float score = hsv[1] * hsv[2]; + hueScoreHistogram[hue] += score; + if (hueScoreHistogram[hue] > highScore) { + highScore = hueScoreHistogram[hue]; + bestHue = hue; + } + } + } + + SparseArray rgbScores = new SparseArray(); + int bestColor = 0xff000000; + highScore = -1; + // Go back over the RGB colors that match the winning hue, + // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets. + // The highest-scoring RGB color wins. + for (int y = 0; y < height; y += sampleStride) { + for (int x = 0; x < width; x += sampleStride) { + int rgb = bitmap.getPixel(x, y) | 0xff000000; + Color.colorToHSV(rgb, hsv); + int hue = (int) hsv[0]; + if (hue == bestHue) { + float s = hsv[1]; + float v = hsv[2]; + int bucket = (int) (s * 100) + (int) (v * 10000); + // Score by cumulative saturation * value. + float score = s * v; + Float oldTotal = rgbScores.get(bucket); + float newTotal = oldTotal == null ? score : oldTotal + score; + rgbScores.put(bucket, newTotal); + if (newTotal > highScore) { + highScore = newTotal; + // All the colors in the winning bucket are very similar. Last in wins. + bestColor = rgb; + } + } + } + } + return bestColor; + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index c8f2f33fb..711133215 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -73,6 +73,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; /** * The workspace is a wide area with a wallpaper and a finite number of pages. @@ -1986,6 +1987,12 @@ public class Workspace extends SmoothPagedView d.copyBounds(bounds); if (bounds.width() == 0 || bounds.height() == 0) { bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + } else { + bounds.offsetTo(0, 0); + } + if (d instanceof PreloadIconDrawable) { + int inset = -((PreloadIconDrawable) d).getOutset(); + bounds.inset(inset, inset); } return bounds; } @@ -2013,7 +2020,7 @@ public class Workspace extends SmoothPagedView Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); - drawDragView(v, c, 0, true); + drawDragView(v, c, 0); c.setBitmap(null); // The outline is used to visualize where the item will land if dropped @@ -2528,18 +2535,18 @@ public class Workspace extends SmoothPagedView * @param destCanvas the canvas to draw on * @param padding the horizontal and vertical padding to use when drawing */ - private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) { + private void drawDragView(View v, Canvas destCanvas, int padding) { final Rect clipRect = mTempRect; v.getDrawingRect(clipRect); boolean textVisible = false; destCanvas.save(); - if (v instanceof TextView && pruneToDrawable) { + if (v instanceof TextView) { Drawable d = ((TextView) v).getCompoundDrawables()[1]; Rect bounds = getDrawableBounds(d); clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding); - destCanvas.translate(padding / 2, padding / 2); + destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top); d.draw(destCanvas); } else { if (v instanceof FolderIcon) { @@ -2549,14 +2556,6 @@ public class Workspace extends SmoothPagedView ((FolderIcon) v).setTextVisible(false); textVisible = true; } - } else if (v instanceof BubbleTextView) { - final BubbleTextView tv = (BubbleTextView) v; - clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + - tv.getLayout().getLineTop(0); - } else if (v instanceof TextView) { - final TextView tv = (TextView) v; - clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() + - tv.getLayout().getLineTop(0); } destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2); destCanvas.clipRect(clipRect, Op.REPLACE); @@ -2573,22 +2572,26 @@ public class Workspace extends SmoothPagedView /** * Returns a new bitmap to show when the given View is being dragged around. * Responsibility for the bitmap is transferred to the caller. + * @param expectedPadding padding to add to the drag view. If a different padding was used + * its value will be changed */ - public Bitmap createDragBitmap(View v, Canvas canvas, int padding) { + public Bitmap createDragBitmap(View v, Canvas canvas, AtomicInteger expectedPadding) { Bitmap b; + int padding = expectedPadding.get(); if (v instanceof TextView) { Drawable d = ((TextView) v).getCompoundDrawables()[1]; Rect bounds = getDrawableBounds(d); b = Bitmap.createBitmap(bounds.width() + padding, bounds.height() + padding, Bitmap.Config.ARGB_8888); + expectedPadding.set(padding - bounds.left - bounds.top); } else { b = Bitmap.createBitmap( v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); } canvas.setBitmap(b); - drawDragView(v, canvas, padding, true); + drawDragView(v, canvas, padding); canvas.setBitmap(null); return b; @@ -2604,7 +2607,7 @@ public class Workspace extends SmoothPagedView v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); canvas.setBitmap(b); - drawDragView(v, canvas, padding, true); + drawDragView(v, canvas, padding); mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor); canvas.setBitmap(null); return b; @@ -2664,7 +2667,8 @@ public class Workspace extends SmoothPagedView public void beginDragShared(View child, DragSource source) { mLauncher.onDragStarted(child); // The drag bitmap follows the touch point around on the screen - final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING); + AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING); + final Bitmap b = createDragBitmap(child, new Canvas(), padding); final int bmpWidth = b.getWidth(); final int bmpHeight = b.getHeight(); @@ -2672,7 +2676,7 @@ public class Workspace extends SmoothPagedView float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY); int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2); int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - - DRAG_BITMAP_PADDING / 2); + - padding.get() / 2); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); @@ -2687,7 +2691,7 @@ public class Workspace extends SmoothPagedView dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. - dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2); + dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2); dragRect = new Rect(left, top, right, bottom); } else if (child instanceof FolderIcon) { int previewSize = grid.folderIconSizePx; @@ -2731,7 +2735,8 @@ public class Workspace extends SmoothPagedView mLauncher.onDragStarted(child); // Compose a new drag bitmap that is of the icon size - final Bitmap tmpB = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING); + AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING); + final Bitmap tmpB = createDragBitmap(child, new Canvas(), padding); Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); Paint p = new Paint(); p.setFilterBitmap(true); @@ -2749,7 +2754,7 @@ public class Workspace extends SmoothPagedView // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. - Point dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2); + Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2); Rect dragRect = new Rect(0, 0, iconSize, iconSize); if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) { @@ -3981,15 +3986,16 @@ public class Workspace extends SmoothPagedView } else { cellLayout.findCellForSpan(mTargetCell, 1, 1); } + // Add the item to DB before adding to screen ensures that the container and other + // values of the info is properly updated. + LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, + mTargetCell[0], mTargetCell[1]); + addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX, info.spanY, insertAtFirst); cellLayout.onDropChild(view); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); cellLayout.getShortcutsAndWidgets().measureChild(view); - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, - lp.cellX, lp.cellY); - if (d.dragView != null) { // We wrap the animation call in the temporary set and reset of the current // cellLayout to its final transform -- this means we animate the drag view to -- 2.11.0