diff options
author | Daniel Sandler <dsandler@android.com> | 2013-06-05 22:57:57 -0400 |
---|---|---|
committer | Daniel Sandler <dsandler@android.com> | 2013-06-05 23:30:20 -0400 |
commit | 325dc23624160689e59fbac708cf6f222b20d025 (patch) | |
tree | 3c6a13a52a6e5688c7e4404890e5e8f88d544856 /src/com/android/launcher3/BubbleTextView.java | |
parent | b582cd201fccece65d36b8915cf84fef3546cffa (diff) | |
download | android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.tar.gz android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.tar.bz2 android_packages_apps_Trebuchet-325dc23624160689e59fbac708cf6f222b20d025.zip |
Launcher2 is now Launcher3.
Changes include
- moving from com.android.launcher{,2} to
com.android.launcher3
- removing wallpapers
- new temporary icon
Change-Id: I1eabd06059e94a8f3bdf6b620777bd1d2b7c212b
Diffstat (limited to 'src/com/android/launcher3/BubbleTextView.java')
-rw-r--r-- | src/com/android/launcher3/BubbleTextView.java | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java new file mode 100644 index 000000000..7cac8a68c --- /dev/null +++ b/src/com/android/launcher3/BubbleTextView.java @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2008 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.content.res.Resources; +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.view.MotionEvent; +import android.widget.TextView; + +/** + * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan + * because we want to make the bubble taller than the text and TextView's clip is + * too aggressive. + */ +public class BubbleTextView extends TextView { + static final float CORNER_RADIUS = 4.0f; + 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; + static final float PADDING_V = 3.0f; + + private int mPrevAlpha = -1; + + private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper(); + 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 boolean mBackgroundSizeChanged; + private Drawable mBackground; + + private boolean mStayPressed; + private CheckLongPressHelper mLongPressHelper; + + public BubbleTextView(Context context) { + super(context); + init(); + } + + public BubbleTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + mLongPressHelper = new CheckLongPressHelper(this); + mBackground = getBackground(); + + final Resources res = getContext().getResources(); + mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = + res.getColor(android.R.color.holo_blue_light); + + setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR); + } + + public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) { + Bitmap b = info.getIcon(iconCache); + + setCompoundDrawablesWithIntrinsicBounds(null, + new FastBitmapDrawable(b), + null, null); + setText(info.title); + setTag(info); + } + + @Override + protected boolean setFrame(int left, int top, int right, int bottom) { + if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) { + mBackgroundSizeChanged = true; + } + return super.setFrame(left, top, right, bottom); + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return who == mBackground || super.verifyDrawable(who); + } + + @Override + public void setTag(Object tag) { + if (tag != null) { + LauncherModel.checkItemInfo((ItemInfo) tag); + } + super.setTag(tag); + } + + @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(); + } + } + + Drawable d = mBackground; + if (d != null && d.isStateful()) { + d.setState(getDrawableState()); + } + 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(); + } + + /** + * 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 = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS; + 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 + public boolean onTouchEvent(MotionEvent event) { + // Call the superclass onTouchEvent first, because sometimes it changes the state to + // isPressed() on an ACTION_UP + boolean result = super.onTouchEvent(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + // So that the pressed outline is visible immediately when isPressed() is true, + // 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; + } + + mLongPressHelper.postCheckForLongPress(); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + // 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; + } + + mLongPressHelper.cancelLongPress(); + break; + } + return result; + } + + void setStayPressed(boolean stayPressed) { + mStayPressed = stayPressed; + if (!stayPressed) { + mPressedOrFocusedBackground = null; + } + setCellLayoutPressedOrFocusedIcon(); + } + + void setCellLayoutPressedOrFocusedIcon() { + if (getParent() instanceof ShortcutAndWidgetContainer) { + ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent(); + if (parent != null) { + CellLayout layout = (CellLayout) parent.getParent(); + layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null); + } + } + } + + void clearPressedOrFocusedBackground() { + mPressedOrFocusedBackground = null; + setCellLayoutPressedOrFocusedIcon(); + } + + Bitmap getPressedOrFocusedBackground() { + return mPressedOrFocusedBackground; + } + + int getPressedOrFocusedBackgroundPadding() { + return HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2; + } + + @Override + public void draw(Canvas canvas) { + final Drawable background = mBackground; + if (background != null) { + final int scrollX = getScrollX(); + final int scrollY = getScrollY(); + + if (mBackgroundSizeChanged) { + background.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop()); + mBackgroundSizeChanged = false; + } + + if ((scrollX | scrollY) == 0) { + background.draw(canvas); + } else { + canvas.translate(scrollX, scrollY); + background.draw(canvas); + canvas.translate(-scrollX, -scrollY); + } + } + + // If text is transparent, don't draw any shadow + if (getCurrentTextColor() == getResources().getColor(android.R.color.transparent)) { + getPaint().clearShadowLayer(); + super.draw(canvas); + return; + } + + // We enhance the shadow by drawing the shadow twice + getPaint().setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR); + super.draw(canvas); + canvas.save(Canvas.CLIP_SAVE_FLAG); + canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(), + getScrollX() + getWidth(), + getScrollY() + getHeight(), Region.Op.INTERSECT); + getPaint().setShadowLayer(SHADOW_SMALL_RADIUS, 0.0f, 0.0f, SHADOW_SMALL_COLOUR); + super.draw(canvas); + canvas.restore(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mBackground != null) mBackground.setCallback(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mBackground != null) mBackground.setCallback(null); + } + + @Override + protected boolean onSetAlpha(int alpha) { + if (mPrevAlpha != alpha) { + mPrevAlpha = alpha; + super.onSetAlpha(alpha); + } + return true; + } + + @Override + public void cancelLongPress() { + super.cancelLongPress(); + + mLongPressHelper.cancelLongPress(); + } +} |