Updating the icon click feedback

> Using BubbleTextView everywhere, removed PagedIconView
> There is a brightness feedback on touch and shadow
feedback on click, until app launches

issue: 16878374
Change-Id: I3dc1149a123c8a75feca6210948398bf2187f1f2
This commit is contained in:
Sunny Goyal
2014-08-14 10:53:27 -07:00
parent f8177d383a
commit 508da15509
16 changed files with 393 additions and 579 deletions
+1 -3
View File
@@ -14,10 +14,8 @@
limitations under the License.
-->
<com.android.launcher3.PagedViewIcon
<com.android.launcher3.BubbleTextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
style="@style/WorkspaceIcon.AppsCustomize"
android:id="@+id/application_icon"
android:focusable="true" />
-1
View File
@@ -106,7 +106,6 @@
<declare-styleable name="BubbleTextView">
<!-- A spacing override for the icons within a page -->
<attr name="customShadows" format="boolean" />
<attr name="glowColor" format="color" />
</declare-styleable>
<!-- AppsCustomizePagedView specific attributes. These attributes are used to
-1
View File
@@ -34,7 +34,6 @@
<color name="quantum_panel_text_color">#FF666666</color>
<color name="quantum_panel_text_shadow_color">#FFC4C4C4</color>
<color name="folder_items_glow_color">#FFCCCCCC</color>
<color name="outline_color">#FFFFFFFF</color>
<color name="widget_text_panel">#FF374248</color>
+2 -2
View File
@@ -91,7 +91,8 @@
<item name="android:shadowRadius">2.0</item>
<item name="android:shadowDx">0</item>
<item name="android:shadowDy">2</item>
<item name="android:shadowColor">#FFC4C4C4</item>
<item name="android:shadowColor">@color/quantum_panel_text_shadow_color</item>
<item name="customShadows">false</item>
</style>
<style name="WorkspaceIcon.Folder">
@@ -103,7 +104,6 @@
<item name="android:shadowDy">2</item>
<item name="customShadows">false</item>
<item name="glowColor">@color/folder_items_glow_color</item>
</style>
<style name="SearchDropTargetBar">
@@ -44,7 +44,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;
@@ -144,8 +143,7 @@ class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTas
*/
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
View.OnClickListener, View.OnKeyListener, DragSource,
PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
LauncherTransitionable {
PagedViewWidget.ShortPressListener, LauncherTransitionable {
static final String TAG = "AppsCustomizePagedView";
private static Rect sTmpRect = new Rect();
@@ -167,7 +165,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// Save and Restore
private int mSaveInstanceStateItemIndex = -1;
private PagedViewIcon mPressedIcon;
// Content
private ArrayList<AppInfo> mApps;
@@ -444,39 +441,29 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
@Override
public void onClick(View v) {
// When we have exited all apps or are in transition, disregard clicks
if (!mLauncher.isAllAppsVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return;
if (!mLauncher.isAllAppsVisible()
|| mLauncher.getWorkspace().isSwitchingState()
|| !(v instanceof PagedViewWidget)) return;
if (v instanceof PagedViewIcon) {
// Animate some feedback to the click
final AppInfo appInfo = (AppInfo) v.getTag();
// Lock the drawable state to pressed until we return to Launcher
if (mPressedIcon != null) {
mPressedIcon.lockDrawableState();
}
mLauncher.onClickPagedViewIcon(v, appInfo);
} else if (v instanceof PagedViewWidget) {
// Let the user know that they have to long press to add a widget
if (mWidgetInstructionToast != null) {
mWidgetInstructionToast.cancel();
}
mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
Toast.LENGTH_SHORT);
mWidgetInstructionToast.show();
// Create a little animation to show that the widget can move
float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
tyuAnim.setDuration(125);
ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
tydAnim.setDuration(100);
bounce.play(tyuAnim).before(tydAnim);
bounce.setInterpolator(new AccelerateInterpolator());
bounce.start();
// Let the user know that they have to long press to add a widget
if (mWidgetInstructionToast != null) {
mWidgetInstructionToast.cancel();
}
mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
Toast.LENGTH_SHORT);
mWidgetInstructionToast.show();
// Create a little animation to show that the widget can move
float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
tyuAnim.setDuration(125);
ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
tydAnim.setDuration(100);
bounce.play(tyuAnim).before(tydAnim);
bounce.setInterpolator(new AccelerateInterpolator());
bounce.start();
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -492,7 +479,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
private void beginDraggingApplication(View v) {
mLauncher.getWorkspace().onDragStartedWithItem(v);
mLauncher.getWorkspace().beginDragShared(v, this);
}
@@ -726,7 +712,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
protected boolean beginDragging(final View v) {
if (!super.beginDragging(v)) return false;
if (v instanceof PagedViewIcon) {
if (v instanceof BubbleTextView) {
beginDraggingApplication(v);
} else if (v instanceof PagedViewWidget) {
if (!beginDraggingWidget(v)) {
@@ -741,9 +727,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
public void run() {
// We don't enter spring-loaded mode if the drag has been cancelled
if (mLauncher.getDragController().isDragging()) {
// Reset the alpha on the dragged icon before we drag
resetDrawableState();
// Go into spring loaded mode (must happen before we startDrag())
mLauncher.enterSpringLoadedDragMode();
}
@@ -992,10 +975,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
ArrayList<Bitmap> images = new ArrayList<Bitmap>();
for (int i = startIndex; i < endIndex; ++i) {
AppInfo info = mApps.get(i);
PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.apps_customize_application, layout, false);
icon.applyFromApplicationInfo(info, true, this);
icon.setOnClickListener(this);
icon.applyFromApplicationInfo(info);
icon.setOnClickListener(mLauncher);
icon.setOnLongClickListener(this);
icon.setOnTouchListener(this);
icon.setOnKeyListener(this);
@@ -1559,23 +1542,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
cancelAllTasks();
}
@Override
public void iconPressed(PagedViewIcon icon) {
// Reset the previously pressed icon and store a reference to the pressed icon so that
// we can reset it on return to Launcher (in Launcher.onResume())
if (mPressedIcon != null) {
mPressedIcon.resetDrawableState();
}
mPressedIcon = icon;
}
public void resetDrawableState() {
if (mPressedIcon != null) {
mPressedIcon.resetDrawableState();
mPressedIcon = null;
}
}
/*
* We load an extra page on each side to prevent flashes from scrolling and loading of the
* widget previews in the background with the AsyncTasks.
+74 -140
View File
@@ -23,14 +23,13 @@ import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
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.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.TextView;
@@ -44,12 +43,11 @@ public class BubbleTextView extends TextView {
private static SparseArray<Theme> 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;
static final int SHADOW_LARGE_COLOUR = 0xDD000000;
static final int SHADOW_SMALL_COLOUR = 0xCC000000;
static final float PADDING_H = 8.0f;
private static final float SHADOW_LARGE_RADIUS = 4.0f;
private static final float SHADOW_SMALL_RADIUS = 1.75f;
private static final float SHADOW_Y_OFFSET = 2.0f;
private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
static final float PADDING_V = 3.0f;
private static final String TAG = "BubbleTextView";
@@ -57,14 +55,7 @@ public class BubbleTextView extends TextView {
private static final boolean DEBUG = false;
private HolographicOutlineHelper mOutlineHelper;
private final Canvas mTempCanvas = new Canvas();
private final Rect mTempRect = new Rect();
private boolean mDidInvalidateForPressedState;
private Bitmap mPressedOrFocusedBackground;
private int mFocusedOutlineColor;
private int mFocusedGlowColor;
private int mPressedOutlineColor;
private int mPressedGlowColor;
private Bitmap mPressedBackground;
private float mSlop;
@@ -72,14 +63,15 @@ public class BubbleTextView extends TextView {
private final boolean mCustomShadowsEnabled;
private boolean mIsTextVisible;
// TODO: Remove custom background handling code, as no instance of BubbleTextView use any
// background.
private boolean mBackgroundSizeChanged;
private final Drawable mBackground;
private boolean mStayPressed;
private boolean mIgnorePressedStateChange;
private CheckLongPressHelper mLongPressHelper;
private CharSequence mDefaultText = "";
public BubbleTextView(Context context) {
this(context, null, 0);
}
@@ -91,11 +83,8 @@ public class BubbleTextView extends TextView {
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
setGlowColor(a.getColor(R.styleable.BubbleTextView_glowColor,
res.getColor(R.color.outline_color)));
mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true);
a.recycle();
@@ -143,6 +132,7 @@ public class BubbleTextView extends TextView {
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
}
setText(info.title);
setTag(info);
if (info.wasPromise) {
@@ -150,6 +140,22 @@ public class BubbleTextView extends TextView {
}
}
public void applyFromApplicationInfo(AppInfo info) {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap);
topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
setCompoundDrawables(null, topDrawable, null, null);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
}
setTag(info);
}
@Override
protected boolean setFrame(int left, int top, int right, int bottom) {
if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
@@ -169,98 +175,22 @@ public class BubbleTextView extends TextView {
LauncherModel.checkItemInfo((ItemInfo) tag);
}
super.setTag(tag);
if (tag instanceof ShortcutInfo) {
final ShortcutInfo info = (ShortcutInfo) tag;
mDefaultText = info.title;
setText(mDefaultText);
}
}
@Override
protected void drawableStateChanged() {
if (isPressed()) {
// In this case, we have already created the pressed outline on ACTION_DOWN,
// so we just need to do an invalidate to trigger draw
if (!mDidInvalidateForPressedState) {
setCellLayoutPressedOrFocusedIcon();
}
} else {
// Otherwise, either clear the pressed/focused background, or create a background
// for the focused state
final boolean backgroundEmptyBefore = mPressedOrFocusedBackground == null;
if (!mStayPressed) {
mPressedOrFocusedBackground = null;
}
if (isFocused()) {
if (getLayout() == null) {
// In some cases, we get focus before we have been layed out. Set the
// background to null so that it will get created when the view is drawn.
mPressedOrFocusedBackground = null;
} else {
mPressedOrFocusedBackground = createGlowingOutline(
mTempCanvas, mFocusedGlowColor, mFocusedOutlineColor);
}
mStayPressed = false;
setCellLayoutPressedOrFocusedIcon();
}
final boolean backgroundEmptyNow = mPressedOrFocusedBackground == null;
if (!backgroundEmptyBefore && backgroundEmptyNow) {
setCellLayoutPressedOrFocusedIcon();
}
public void setPressed(boolean pressed) {
super.setPressed(pressed);
if (!mIgnorePressedStateChange) {
updateIconState();
}
}
Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
private void updateIconState() {
Drawable top = getCompoundDrawables()[1];
if (top instanceof FastBitmapDrawable) {
((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed);
}
super.drawableStateChanged();
}
/**
* Draw this BubbleTextView into the given Canvas.
*
* @param destCanvas the canvas to draw on
* @param padding the horizontal and vertical padding to use when drawing
*/
private void drawWithPadding(Canvas destCanvas, int padding) {
final Rect clipRect = mTempRect;
getDrawingRect(clipRect);
// adjust the clip rect so that we don't include the text label
clipRect.bottom =
getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + getLayout().getLineTop(0);
// Draw the View into the bitmap.
// The translate of scrollX and scrollY is necessary when drawing TextViews, because
// they set scrollX and scrollY to large values to achieve centered text
destCanvas.save();
destCanvas.scale(getScaleX(), getScaleY(),
(getWidth() + padding) / 2, (getHeight() + padding) / 2);
destCanvas.translate(-getScrollX() + padding / 2, -getScrollY() + padding / 2);
destCanvas.clipRect(clipRect, Op.REPLACE);
draw(destCanvas);
destCanvas.restore();
}
public void setGlowColor(int color) {
mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = color;
}
/**
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
private Bitmap createGlowingOutline(Canvas canvas, int outlineColor, int glowColor) {
final int padding = mOutlineHelper.mMaxOuterBlurRadius;
final Bitmap b = Bitmap.createBitmap(
getWidth() + padding, getHeight() + padding, Bitmap.Config.ARGB_8888);
canvas.setBitmap(b);
drawWithPadding(canvas, padding);
mOutlineHelper.applyExtraThickExpensiveOutlineWithBlur(b, canvas, glowColor, outlineColor);
canvas.setBitmap(null);
return b;
}
@Override
@@ -271,20 +201,11 @@ public class BubbleTextView extends TextView {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// So that the pressed outline is visible immediately when isPressed() is true,
// So that the pressed outline is visible immediately on setStayPressed(),
// we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time
// to create it)
if (mPressedOrFocusedBackground == null) {
mPressedOrFocusedBackground = createGlowingOutline(
mTempCanvas, mPressedGlowColor, mPressedOutlineColor);
}
// Invalidate so the pressed state is visible, or set a flag so we know that we
// have to call invalidate as soon as the state is "pressed"
if (isPressed()) {
mDidInvalidateForPressedState = true;
setCellLayoutPressedOrFocusedIcon();
} else {
mDidInvalidateForPressedState = false;
if (mPressedBackground == null) {
mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
}
mLongPressHelper.postCheckForLongPress();
@@ -294,7 +215,7 @@ public class BubbleTextView extends TextView {
// If we've touched down and up on an item, and it's still not "pressed", then
// destroy the pressed outline
if (!isPressed()) {
mPressedOrFocusedBackground = null;
mPressedBackground = null;
}
mLongPressHelper.cancelLongPress();
@@ -311,34 +232,47 @@ public class BubbleTextView extends TextView {
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
if (!stayPressed) {
mPressedOrFocusedBackground = null;
mPressedBackground = null;
}
setCellLayoutPressedOrFocusedIcon();
// Only show the shadow effect when persistent pressed state is set.
if (getParent() instanceof ShortcutAndWidgetContainer) {
CellLayout layout = (CellLayout) getParent().getParent();
layout.setPressedIcon(this, mPressedBackground, mOutlineHelper.shadowBitmapPadding);
}
updateIconState();
}
void setCellLayoutPressedOrFocusedIcon() {
// 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();
layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null);
void clearPressedBackground() {
setPressed(false);
setStayPressed(false);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (super.onKeyDown(keyCode, event)) {
// Pre-create shadow so show immediately on click.
if (mPressedBackground == null) {
mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
}
return true;
}
return false;
}
void clearPressedOrFocusedBackground() {
mPressedOrFocusedBackground = null;
setCellLayoutPressedOrFocusedIcon();
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Unlike touch events, keypress event propagate pressed state change immediately,
// without waiting for onClickHandler to execute. Disable pressed state changes here
// to avoid flickering.
mIgnorePressedStateChange = true;
boolean result = super.onKeyUp(keyCode, event);
Bitmap getPressedOrFocusedBackground() {
return mPressedOrFocusedBackground;
}
int getPressedOrFocusedBackgroundPadding() {
return mOutlineHelper.mMaxOuterBlurRadius / 2;
mPressedBackground = null;
mIgnorePressedStateChange = false;
updateIconState();
return result;
}
@Override
+21 -36
View File
@@ -122,7 +122,7 @@ public class CellLayout extends ViewGroup {
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
private BubbleTextView mPressedOrFocusedIcon;
private final FastBitmapView mTouchFeedbackView;
private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
HashMap<CellLayout.LayoutParams, Animator>();
@@ -287,6 +287,9 @@ public class CellLayout extends ViewGroup {
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
mCountX, mCountY);
mTouchFeedbackView = new FastBitmapView(context);
// Make the feedback view large enough to hold the blur bitmap.
addView(mTouchFeedbackView, (int) (grid.cellWidthPx * 1.5), (int) (grid.cellHeightPx * 1.5));
addView(mShortcutsAndWidgets);
}
@@ -333,14 +336,6 @@ public class CellLayout extends ViewGroup {
return mDropPending;
}
private void invalidateBubbleTextView(BubbleTextView icon) {
final int padding = icon.getPressedOrFocusedBackgroundPadding();
invalidate(icon.getLeft() + getPaddingLeft() - padding,
icon.getTop() + getPaddingTop() - padding,
icon.getRight() + getPaddingLeft() + padding,
icon.getBottom() + getPaddingTop() + padding);
}
void setOverScrollAmount(float r, boolean left) {
if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
mOverScrollForegroundDrawable = mOverScrollLeft;
@@ -354,16 +349,23 @@ public class CellLayout extends ViewGroup {
invalidate();
}
void setPressedOrFocusedIcon(BubbleTextView icon) {
// We draw the pressed or focused BubbleTextView's background in CellLayout because it
// requires an expanded clip rect (due to the glow's blur radius)
BubbleTextView oldIcon = mPressedOrFocusedIcon;
mPressedOrFocusedIcon = icon;
if (oldIcon != null) {
invalidateBubbleTextView(oldIcon);
}
if (mPressedOrFocusedIcon != null) {
invalidateBubbleTextView(mPressedOrFocusedIcon);
void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) {
if (icon == null || background == null) {
mTouchFeedbackView.setBitmap(null);
mTouchFeedbackView.animate().cancel();
} else {
int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()
- (mCountX * mCellWidth);
mTouchFeedbackView.setTranslationX(icon.getLeft() + (int) Math.ceil(offset / 2f)
- padding);
mTouchFeedbackView.setTranslationY(icon.getTop() - padding);
if (mTouchFeedbackView.setBitmap(background)) {
mTouchFeedbackView.setAlpha(0);
mTouchFeedbackView.animate().alpha(1)
.setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION)
.setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR)
.start();
}
}
}
@@ -422,23 +424,6 @@ public class CellLayout extends ViewGroup {
}
}
// We draw the pressed or focused BubbleTextView's background in CellLayout because it
// requires an expanded clip rect (due to the glow's blur radius)
if (mPressedOrFocusedIcon != null) {
final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
if (b != null) {
int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
(mCountX * mCellWidth);
int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
int top = getPaddingTop();
canvas.drawBitmap(b,
mPressedOrFocusedIcon.getLeft() + left - padding,
mPressedOrFocusedIcon.getTop() + top - padding,
null);
}
}
if (DEBUG_VISUALIZE_OCCUPIED) {
int[] pt = new int[2];
ColorDrawable cd = new ColorDrawable(Color.RED);
@@ -16,30 +16,62 @@
package com.android.launcher3;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
class FastBitmapDrawable extends Drawable {
static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
@Override
public float getInterpolation(float input) {
if (input < 0.05f) {
return input / 0.05f;
} else if (input < 0.3f){
return 1;
} else {
return (1 - input) / 0.7f;
}
}
};
static final long CLICK_FEEDBACK_DURATION = 2000;
private static final int PRESSED_BRIGHTNESS = 100;
private static ColorMatrix sGhostModeMatrix;
private static final ColorMatrix sTempMatrix = new ColorMatrix();
/**
* Store the brightness colors filters to optimize animations during icon press. This
* only works for non-ghost-mode icons.
*/
private static final SparseArray<ColorFilter> sCachedBrightnessFilter =
new SparseArray<ColorFilter>();
private static final int GHOST_MODE_MIN_COLOR_RANGE = 130;
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
private Bitmap mBitmap;
private final Bitmap mBitmap;
private int mAlpha;
private int mBrightness = 0;
private boolean mGhostModeEnabled = false;
private boolean mPressed = false;
private ObjectAnimator mPressedAnimator;
FastBitmapDrawable(Bitmap b) {
mAlpha = 255;
mBitmap = b;
@@ -114,6 +146,23 @@ class FastBitmapDrawable extends Drawable {
}
}
public void setPressed(boolean pressed) {
if (mPressed != pressed) {
mPressed = pressed;
if (mPressed) {
mPressedAnimator = ObjectAnimator
.ofInt(this, "brightness", PRESSED_BRIGHTNESS)
.setDuration(CLICK_FEEDBACK_DURATION);
mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
mPressedAnimator.start();
} else if (mPressedAnimator != null) {
mPressedAnimator.cancel();
setBrightness(0);
}
}
invalidateSelf();
}
public boolean isGhostModeEnabled() {
return mGhostModeEnabled;
}
@@ -122,14 +171,11 @@ class FastBitmapDrawable extends Drawable {
return mBrightness;
}
public void addBrightness(int amount) {
setBrightness(mBrightness + amount);
}
public void setBrightness(int brightness) {
if (mBrightness != brightness) {
mBrightness = brightness;
updateFilter();
invalidateSelf();
}
}
@@ -157,8 +203,13 @@ class FastBitmapDrawable extends Drawable {
mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
}
} else if (mBrightness != 0) {
setBrightnessMatrix(sTempMatrix, mBrightness);
mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
ColorFilter filter = sCachedBrightnessFilter.get(mBrightness);
if (filter == null) {
filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255),
PorterDuff.Mode.SRC_ATOP);
sCachedBrightnessFilter.put(mBrightness, filter);
}
mPaint.setColorFilter(filter);
} else {
mPaint.setColorFilter(null);
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class FastBitmapView extends View {
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
private Bitmap mBitmap;
public FastBitmapView(Context context) {
super(context);
}
/**
* Applies the new bitmap.
* @return true if the view was invalidated.
*/
public boolean setBitmap(Bitmap b) {
if (b != mBitmap){
if (mBitmap != null) {
invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
mBitmap = b;
if (mBitmap != null) {
invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
return true;
}
return false;
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
}
}
+7 -14
View File
@@ -22,8 +22,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ScrollView;
import android.widget.TabHost;
import android.widget.TabWidget;
import java.util.ArrayList;
import java.util.Collections;
@@ -89,7 +87,6 @@ public class FocusHelper {
final PagedViewGridLayout parent = (PagedViewGridLayout) w.getParent();
final PagedView container = (PagedView) parent.getParent();
final AppsCustomizeTabHost tabHost = findTabHostParent(container);
final int widgetIndex = parent.indexOfChild(w);
final int widgetCount = parent.getChildCount();
final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parent));
@@ -228,6 +225,13 @@ public class FocusHelper {
* Handles key events in a PageViewCellLayout containing PagedViewIcons.
*/
static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) {
final int action = e.getAction();
if (((action == KeyEvent.ACTION_DOWN) && v.onKeyDown(keyCode, e))
|| ((action == KeyEvent.ACTION_UP) && v.onKeyUp(keyCode, e))) {
// Let the view handle the confirmation key.
return true;
}
ViewGroup parentLayout;
ViewGroup itemContainer;
int countX;
@@ -246,7 +250,6 @@ public class FocusHelper {
// Note we have an extra parent because of the
// PagedViewCellLayout/PagedViewCellLayoutChildren relationship
final PagedView container = (PagedView) parentLayout.getParent();
final AppsCustomizeTabHost tabHost = findTabHostParent(container);
final int iconIndex = itemContainer.indexOfChild(v);
final int itemCount = itemContainer.getChildCount();
final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parentLayout));
@@ -255,7 +258,6 @@ public class FocusHelper {
final int x = iconIndex % countX;
final int y = iconIndex / countX;
final int action = e.getAction();
final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
ViewGroup newParent = null;
// Side pages do not always load synchronously, so check before focusing child siblings
@@ -319,15 +321,6 @@ public class FocusHelper {
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
if (handleKeyEvent) {
// Simulate a click on the icon
View.OnClickListener clickListener = (View.OnClickListener) container;
clickListener.onClick(v);
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_PAGE_UP:
if (handleKeyEvent) {
// Select the first icon on the previous page, or the first icon on this page
-9
View File
@@ -118,10 +118,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private FocusIndicatorView mFocusIndicatorHandler;
private int DRAG_MODE_NONE = 0;
private int DRAG_MODE_REORDER = 1;
private int mDragMode = DRAG_MODE_NONE;
// We avoid measuring the scroll view with a 0 width or height, as this
// results in CellLayout being measured as UNSPECIFIED, which it does
// not support.
@@ -254,7 +250,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mLauncher.getLauncherClings().dismissFolderCling(null);
mLauncher.getWorkspace().onDragStartedWithItem(v);
mLauncher.getWorkspace().beginDragShared(v, this);
mCurrentDragInfo = item;
@@ -783,9 +778,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mReorderAlarm.setAlarm(REORDER_DELAY);
mPreviousTargetCell[0] = mTargetCell[0];
mPreviousTargetCell[1] = mTargetCell[1];
mDragMode = DRAG_MODE_REORDER;
} else {
mDragMode = DRAG_MODE_NONE;
}
}
}
@@ -841,7 +833,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY);
}
mReorderAlarm.cancelAlarm();
mDragMode = DRAG_MODE_NONE;
}
public void onDropCompleted(final View target, final DragObject d,
+3 -2
View File
@@ -583,9 +583,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
if (d instanceof FastBitmapDrawable) {
FastBitmapDrawable fd = (FastBitmapDrawable) d;
fd.addBrightness(params.overlayAlpha);
int oldBrightness = fd.getBrightness();
fd.setBrightness(params.overlayAlpha);
d.draw(canvas);
fd.addBrightness(-params.overlayAlpha);
fd.setBrightness(oldBrightness);
} else {
d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
PorterDuff.Mode.SRC_ATOP);
@@ -20,48 +20,49 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Region.Op;
public class HolographicOutlineHelper {
private final Paint mHolographicPaint = new Paint();
private static final Rect sTempRect = new Rect();
private final Canvas mCanvas = new Canvas();
private final Paint mDrawPaint = new Paint();
private final Paint mBlurPaint = new Paint();
private final Paint mErasePaint = new Paint();
public int mMaxOuterBlurRadius;
public int mMinOuterBlurRadius;
private final BlurMaskFilter mMediumOuterBlurMaskFilter;
private final BlurMaskFilter mThinOuterBlurMaskFilter;
private final BlurMaskFilter mMediumInnerBlurMaskFilter;
private BlurMaskFilter mExtraThickOuterBlurMaskFilter;
private BlurMaskFilter mThickOuterBlurMaskFilter;
private BlurMaskFilter mMediumOuterBlurMaskFilter;
private BlurMaskFilter mThinOuterBlurMaskFilter;
private BlurMaskFilter mThickInnerBlurMaskFilter;
private BlurMaskFilter mExtraThickInnerBlurMaskFilter;
private BlurMaskFilter mMediumInnerBlurMaskFilter;
private final BlurMaskFilter mShaowBlurMaskFilter;
private final int mShadowOffset;
private static final int THICK = 0;
private static final int MEDIUM = 1;
private static final int EXTRA_THICK = 2;
/**
* Padding used when creating shadow bitmap;
*/
final int shadowBitmapPadding;
static HolographicOutlineHelper INSTANCE;
private HolographicOutlineHelper(Context context) {
final float scale = LauncherAppState.getInstance().getScreenDensity();
mMinOuterBlurRadius = (int) (scale * 1.0f);
mMaxOuterBlurRadius = (int) (scale * 12.0f);
mExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
mThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
mExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
mThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
mHolographicPaint.setFilterBitmap(true);
mHolographicPaint.setAntiAlias(true);
mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
mShadowOffset = (int) (scale * 2.0f);
shadowBitmapPadding = (int) (scale * 4.0f);
mDrawPaint.setFilterBitmap(true);
mDrawPaint.setAntiAlias(true);
mBlurPaint.setFilterBitmap(true);
mBlurPaint.setAntiAlias(true);
mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
@@ -76,38 +77,16 @@ public class HolographicOutlineHelper {
return INSTANCE;
}
/**
* Returns the interpolated holographic highlight alpha for the effect we want when scrolling
* pages.
*/
public static float highlightAlphaInterpolator(float r) {
float maxAlpha = 0.6f;
return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
}
/**
* Returns the interpolated view alpha for the effect we want when scrolling pages.
*/
public static float viewAlphaInterpolator(float r) {
final float pivot = 0.95f;
if (r < pivot) {
return (float) Math.pow(r / pivot, 1.5f);
} else {
return 1.0f;
}
}
/**
* Applies a more expensive and accurate outline to whatever is currently drawn in a specified
* bitmap.
*/
void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor, int thickness) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true,
thickness);
int outlineColor) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
}
void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor, boolean clipAlpha, int thickness) {
int outlineColor, boolean clipAlpha) {
// We start by removing most of the alpha channel so as to ignore shadows, and
// other types of partial transparency when defining the shape of the object
@@ -127,50 +106,18 @@ public class HolographicOutlineHelper {
Bitmap glowShape = srcDst.extractAlpha();
// calculate the outer blur first
BlurMaskFilter outerBlurMaskFilter;
switch (thickness) {
case EXTRA_THICK:
outerBlurMaskFilter = mExtraThickOuterBlurMaskFilter;
break;
case THICK:
outerBlurMaskFilter = mThickOuterBlurMaskFilter;
break;
case MEDIUM:
outerBlurMaskFilter = mMediumOuterBlurMaskFilter;
break;
default:
throw new RuntimeException("Invalid blur thickness");
}
mBlurPaint.setMaskFilter(outerBlurMaskFilter);
mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
int[] outerBlurOffset = new int[2];
Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
if (thickness == EXTRA_THICK) {
mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
} else {
mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
}
mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
int[] brightOutlineOffset = new int[2];
Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
// calculate the inner blur
srcDstCanvas.setBitmap(glowShape);
srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
BlurMaskFilter innerBlurMaskFilter;
switch (thickness) {
case EXTRA_THICK:
innerBlurMaskFilter = mExtraThickInnerBlurMaskFilter;
break;
case THICK:
innerBlurMaskFilter = mThickInnerBlurMaskFilter;
break;
case MEDIUM:
innerBlurMaskFilter = mMediumInnerBlurMaskFilter;
break;
default:
throw new RuntimeException("Invalid blur thickness");
}
mBlurPaint.setMaskFilter(innerBlurMaskFilter);
mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
int[] thickInnerBlurOffset = new int[2];
Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
@@ -186,16 +133,16 @@ public class HolographicOutlineHelper {
// draw the inner and outer blur
srcDstCanvas.setBitmap(srcDst);
srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
mHolographicPaint.setColor(color);
mDrawPaint.setColor(color);
srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
mHolographicPaint);
mDrawPaint);
srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
mHolographicPaint);
mDrawPaint);
// draw the bright outline
mHolographicPaint.setColor(outlineColor);
mDrawPaint.setColor(outlineColor);
srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
mHolographicPaint);
mDrawPaint);
// cleanup
srcDstCanvas.setBitmap(null);
@@ -205,25 +152,52 @@ public class HolographicOutlineHelper {
glowShape.recycle();
}
void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK);
}
Bitmap createMediumDropShadow(BubbleTextView view) {
final Bitmap result = Bitmap.createBitmap(
view.getWidth() + shadowBitmapPadding + shadowBitmapPadding,
view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset,
Bitmap.Config.ARGB_8888);
void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
}
mCanvas.setBitmap(result);
void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor, boolean clipAlpha) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha,
MEDIUM);
}
final Rect clipRect = sTempRect;
view.getDrawingRect(sTempRect);
// adjust the clip rect so that we don't include the text label
clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V
+ view.getLayout().getLineTop(0);
void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
int outlineColor) {
applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
}
// Draw the View into the bitmap.
// The translate of scrollX and scrollY is necessary when drawing TextViews, because
// they set scrollX and scrollY to large values to achieve centered text
mCanvas.save();
mCanvas.scale(view.getScaleX(), view.getScaleY(),
view.getWidth() / 2 + shadowBitmapPadding,
view.getHeight() / 2 + shadowBitmapPadding);
mCanvas.translate(-view.getScrollX() + shadowBitmapPadding,
-view.getScrollY() + shadowBitmapPadding);
mCanvas.clipRect(clipRect, Op.REPLACE);
view.draw(mCanvas);
mCanvas.restore();
int[] blurOffst = new int[2];
mBlurPaint.setMaskFilter(mShaowBlurMaskFilter);
Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst);
mCanvas.save();
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mCanvas.translate(blurOffst[0], blurOffst[1]);
mDrawPaint.setColor(Color.BLACK);
mDrawPaint.setAlpha(30);
mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint);
mDrawPaint.setAlpha(60);
mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint);
mCanvas.restore();
mCanvas.setBitmap(null);
blurBitmap.recycle();
return result;
}
}
+24 -29
View File
@@ -82,7 +82,6 @@ import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -1026,10 +1025,6 @@ public class Launcher extends Activity
// Resets the previous workspace icon press state
mWaitingForResume.setStayPressed(false);
}
if (mAppsCustomizeContent != null) {
// Resets the previous all apps icon press state
mAppsCustomizeContent.resetDrawableState();
}
// It is possible that widgets can receive updates while launcher is not in the foreground.
// Consequently, the widgets will be inflated in the orientation of the foreground activity
@@ -2451,6 +2446,8 @@ public class Launcher extends Activity
}
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v);
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v);
@@ -2458,6 +2455,10 @@ public class Launcher extends Activity
}
}
public void onClickPagedViewIcon(View v) {
startAppShortcutOrInfoActivity(v);
}
public boolean onTouch(View v, MotionEvent event) {
return false;
}
@@ -2538,17 +2539,6 @@ public class Launcher extends Activity
}
}
/**
* Event handler for a paged view icon click.
* @param v The view that was clicked.
* @param appInfo The {link AppInfo} of the view.
*/
public void onClickPagedViewIcon(View v, AppInfo appInfo) {
if (LOGD) Log.d(TAG, "onClickPagedViewIcon");
startActivitySafely(v, appInfo.intent, appInfo);
getStats().recordLaunch(appInfo.intent);
}
/**
* Event handler for an app shortcut click.
*
@@ -2586,7 +2576,7 @@ public class Launcher extends Activity
builder.setPositiveButton(R.string.abandoned_search,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
startAppShortcutActivity(v);
startAppShortcutOrInfoActivity(v);
}
}
);
@@ -2603,24 +2593,29 @@ public class Launcher extends Activity
}
// Start activities
startAppShortcutActivity(v);
startAppShortcutOrInfoActivity(v);
}
private void startAppShortcutActivity(View v) {
private void startAppShortcutOrInfoActivity(View v) {
Object tag = v.getTag();
if (!(tag instanceof ShortcutInfo)) {
throw new IllegalArgumentException("Input must be a Shortcut");
}
final ShortcutInfo shortcut = (ShortcutInfo) tag;
final Intent intent = shortcut.intent;
final ShortcutInfo shortcut;
final Intent intent;
if (tag instanceof ShortcutInfo) {
shortcut = (ShortcutInfo) tag;
intent = shortcut.intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
} else if (tag instanceof AppInfo) {
shortcut = null;
intent = ((AppInfo) tag).intent;
} else {
throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
}
boolean success = startActivitySafely(v, intent, tag);
mStats.recordLaunch(intent, shortcut);
if (success && v instanceof BubbleTextView) {
@@ -1,114 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
/**
* An icon on a PagedView, specifically for items in the launcher's paged view (with compound
* drawables on the top).
*/
public class PagedViewIcon extends TextView {
/** A simple callback interface to allow a PagedViewIcon to notify when it has been pressed */
public static interface PressedCallback {
void iconPressed(PagedViewIcon icon);
}
@SuppressWarnings("unused")
private static final String TAG = "PagedViewIcon";
private static final float PRESS_ALPHA = 0.4f;
private PagedViewIcon.PressedCallback mPressedCallback;
private boolean mLockDrawableState = false;
private Bitmap mIcon;
public PagedViewIcon(Context context) {
this(context, null);
}
public PagedViewIcon(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void onFinishInflate() {
super.onFinishInflate();
// Ensure we are using the right text size
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
}
public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
PagedViewIcon.PressedCallback cb) {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
mIcon = info.iconBitmap;
mPressedCallback = cb;
Drawable icon = Utilities.createIconDrawable(mIcon);
icon.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
setCompoundDrawables(null, icon, null, null);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
}
setTag(info);
}
public void lockDrawableState() {
mLockDrawableState = true;
}
public void resetDrawableState() {
mLockDrawableState = false;
post(new Runnable() {
@Override
public void run() {
refreshDrawableState();
}
});
}
protected void drawableStateChanged() {
super.drawableStateChanged();
// We keep in the pressed state until resetDrawableState() is called to reset the press
// feedback
if (isPressed()) {
setAlpha(PRESS_ALPHA);
if (mPressedCallback != null) {
mPressedCallback.iconPressed(this);
}
} else if (!mLockDrawableState) {
setAlpha(1f);
}
}
}
+41 -57
View File
@@ -27,7 +27,6 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.WallpaperManager;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -47,9 +46,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
@@ -76,9 +73,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -212,7 +207,7 @@ public class Workspace extends SmoothPagedView
private HolographicOutlineHelper mOutlineHelper;
private Bitmap mDragOutline = null;
private final Rect mTempRect = new Rect();
private static final Rect sTempRect = new Rect();
private final int[] mTempXY = new int[2];
private int[] mTempVisiblePagesRange = new int[2];
private boolean mOverscrollEffectSet;
@@ -241,6 +236,8 @@ public class Workspace extends SmoothPagedView
private DropTarget.DragEnforcer mDragEnforcer;
private float mMaxDistanceForFolderCreation;
private final Canvas mCanvas = new Canvas();
// Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
private float mXDown;
private float mYDown;
@@ -1980,14 +1977,7 @@ public class Workspace extends SmoothPagedView
* appearance).
*
*/
public void onDragStartedWithItem(View v) {
final Canvas canvas = new Canvas();
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
}
private Rect getDrawableBounds(Drawable d) {
private static Rect getDrawableBounds(Drawable d) {
Rect bounds = new Rect();
d.copyBounds(bounds);
if (bounds.width() == 0 || bounds.height() == 0) {
@@ -2003,8 +1993,6 @@ public class Workspace extends SmoothPagedView
}
public void onExternalDragStartedWithItem(View v) {
final Canvas canvas = new Canvas();
// Compose a drag bitmap with the view scaled to the icon size
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -2024,22 +2012,19 @@ public class Workspace extends SmoothPagedView
// Compose the bitmap to create the icon from
Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
drawDragView(v, c, 0);
c.setBitmap(null);
mCanvas.setBitmap(b);
drawDragView(v, mCanvas, 0);
mCanvas.setBitmap(null);
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
}
public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
final Canvas canvas = new Canvas();
int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
size[1], clipAlpha);
mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
}
public void exitWidgetResizeMode() {
@@ -2537,8 +2522,8 @@ 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) {
final Rect clipRect = mTempRect;
private static void drawDragView(View v, Canvas destCanvas, int padding) {
final Rect clipRect = sTempRect;
v.getDrawingRect(clipRect);
boolean textVisible = false;
@@ -2577,7 +2562,7 @@ public class Workspace extends SmoothPagedView
* @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, AtomicInteger expectedPadding) {
public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
Bitmap b;
int padding = expectedPadding.get();
@@ -2592,9 +2577,9 @@ public class Workspace extends SmoothPagedView
v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
}
canvas.setBitmap(b);
drawDragView(v, canvas, padding);
canvas.setBitmap(null);
mCanvas.setBitmap(b);
drawDragView(v, mCanvas, padding);
mCanvas.setBitmap(null);
return b;
}
@@ -2603,15 +2588,15 @@ public class Workspace extends SmoothPagedView
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
private Bitmap createDragOutline(View v, int padding) {
final int outlineColor = getResources().getColor(R.color.outline_color);
final Bitmap b = Bitmap.createBitmap(
v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
canvas.setBitmap(b);
drawDragView(v, canvas, padding);
mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
canvas.setBitmap(null);
mCanvas.setBitmap(b);
drawDragView(v, mCanvas, padding);
mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
mCanvas.setBitmap(null);
return b;
}
@@ -2619,11 +2604,11 @@ public class Workspace extends SmoothPagedView
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
boolean clipAlpha) {
final int outlineColor = getResources().getColor(R.color.outline_color);
final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas.setBitmap(b);
mCanvas.setBitmap(b);
Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
@@ -2635,10 +2620,10 @@ public class Workspace extends SmoothPagedView
// center the image
dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
canvas.drawBitmap(orig, src, dst, null);
mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
mCanvas.drawBitmap(orig, src, dst, null);
mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
clipAlpha);
canvas.setBitmap(null);
mCanvas.setBitmap(null);
return b;
}
@@ -2656,21 +2641,20 @@ public class Workspace extends SmoothPagedView
CellLayout layout = (CellLayout) child.getParent().getParent();
layout.prepareChildForDrag(child);
child.clearFocus();
child.setPressed(false);
final Canvas canvas = new Canvas();
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
beginDragShared(child, this);
}
public void beginDragShared(View child, DragSource source) {
child.clearFocus();
child.setPressed(false);
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
mLauncher.onDragStarted(child);
// The drag bitmap follows the touch point around on the screen
AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
final Bitmap b = createDragBitmap(child, new Canvas(), padding);
final Bitmap b = createDragBitmap(child, padding);
final int bmpWidth = b.getWidth();
final int bmpHeight = b.getHeight();
@@ -2684,7 +2668,7 @@ public class Workspace extends SmoothPagedView
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Point dragVisualizeOffset = null;
Rect dragRect = null;
if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
if (child instanceof BubbleTextView) {
int iconSize = grid.iconSizePx;
int top = child.getPaddingTop();
int left = (bmpWidth - iconSize) / 2;
@@ -2703,7 +2687,7 @@ public class Workspace extends SmoothPagedView
// Clear the pressed state if necessary
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
icon.clearPressedOrFocusedBackground();
icon.clearPressedBackground();
} else if (child instanceof FolderIcon) {
// The folder cling isn't flexible enough to be shown in non-default workspace positions
// Also if they are dragging it a folder, we assume they don't need to see the cling.
@@ -2738,14 +2722,14 @@ public class Workspace extends SmoothPagedView
// Compose a new drag bitmap that is of the icon size
AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
final Bitmap tmpB = createDragBitmap(child, new Canvas(), padding);
final Bitmap tmpB = createDragBitmap(child, padding);
Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
Paint p = new Paint();
p.setFilterBitmap(true);
Canvas c = new Canvas(b);
c.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
mCanvas.setBitmap(b);
mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
new Rect(0, 0, iconSize, iconSize), p);
c.setBitmap(null);
mCanvas.setBitmap(null);
// Find the child's location on the screen
int bmpWidth = tmpB.getWidth();
@@ -4020,12 +4004,12 @@ public class Workspace extends SmoothPagedView
int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
mCanvas.setBitmap(b);
layout.measure(width, height);
layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
layout.draw(c);
c.setBitmap(null);
layout.draw(mCanvas);
mCanvas.setBitmap(null);
layout.setVisibility(visibility);
return b;
}