diff options
27 files changed, 892 insertions, 838 deletions
diff --git a/res/layout/apps_customize_application.xml b/res/layout/apps_customize_application.xml index 17f4a8e15..c56cdf3d2 100644 --- a/res/layout/apps_customize_application.xml +++ b/res/layout/apps_customize_application.xml @@ -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" /> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 552e84ca2..0db60c95c 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -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 diff --git a/res/values/colors.xml b/res/values/colors.xml index 5e9c6ecec..8aa2184e5 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -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> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 893d7c0db..b2e183c63 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -101,4 +101,8 @@ <!-- The amount that the preview contents are inset from the preview background --> <dimen name="folder_preview_padding">4dp</dimen> <dimen name="folder_name_padding">10dp</dimen> + +<!-- Sizes for managed profile badges --> + <dimen name="profile_badge_size">24dp</dimen> + <dimen name="profile_badge_margin">4dp</dimen> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 6079eee3b..056930604 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -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"> diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index b2228f7af..9f9c34bf4 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -28,7 +28,6 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -44,12 +43,12 @@ 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; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.compat.AppWidgetManagerCompat; import java.util.ArrayList; import java.util.Collections; @@ -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; @@ -179,7 +176,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mClingFocusedY; // Caching - private Canvas mCanvas; private IconCache mIconCache; // Dimens @@ -227,13 +223,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mApps = new ArrayList<AppInfo>(); mWidgets = new ArrayList<Object>(); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - mCanvas = new Canvas(); mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>(); // Save the default widget preview background TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); mClingFocusedX = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedX, 0); @@ -394,7 +387,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen if (!app.shouldShowAppOrWidgetProvider(widget.provider)) { continue; } - widget.label = widget.label.trim(); if (widget.minWidth > 0 && widget.minHeight > 0) { // Ensure that all widgets we show can be added on a workspace of this size int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget); @@ -444,39 +436,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 (v instanceof PagedViewIcon) { - // Animate some feedback to the click - final AppInfo appInfo = (AppInfo) v.getTag(); + if (!mLauncher.isAllAppsVisible() + || mLauncher.getWorkspace().isSwitchingState() + || !(v instanceof PagedViewWidget)) return; - // 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 +474,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } private void beginDraggingApplication(View v) { - mLauncher.getWorkspace().onDragStartedWithItem(v); mLauncher.getWorkspace().beginDragShared(v, this); } @@ -534,18 +515,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen @Override public void run() { mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); - // Options will be null for platforms with JB or lower, so this serves as an - // SDK level check. - if (options == null) { - if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed( - mWidgetLoadingId, info.componentName)) { - mWidgetCleanupState = WIDGET_BOUND; - } - } else { - if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed( - mWidgetLoadingId, info.componentName, options)) { - mWidgetCleanupState = WIDGET_BOUND; - } + if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( + mWidgetLoadingId, pInfo, options)) { + mWidgetCleanupState = WIDGET_BOUND; } } }; @@ -673,9 +645,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int[] previewSizeBeforeScale = new int[1]; - preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.componentName, - createWidgetInfo.previewImage, createWidgetInfo.icon, spanX, spanY, - maxWidth, maxHeight, null, previewSizeBeforeScale); + preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, + spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale); // Compare the size of the drag preview to the preview in the AppsCustomize tray int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], @@ -692,15 +663,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } else { PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo); - preview = Bitmap.createBitmap(icon.getIntrinsicWidth(), - icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - - mCanvas.setBitmap(preview); - mCanvas.save(); - WidgetPreviewLoader.renderDrawableToBitmap(icon, preview, 0, 0, - icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); - mCanvas.restore(); - mCanvas.setBitmap(null); + preview = Utilities.createIconBitmap(icon, mLauncher); createItemInfo.spanX = createItemInfo.spanY = 1; } @@ -726,7 +689,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 +704,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 +952,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 +1519,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. diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5c2bb9946..869b0ac88 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -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); - Drawable d = mBackground; - if (d != null && d.isStateful()) { - d.setState(getDrawableState()); + if (!mIgnorePressedStateChange) { + updateIconState(); } - 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; + private void updateIconState() { + Drawable top = getCompoundDrawables()[1]; + if (top instanceof FastBitmapDrawable) { + ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed); + } } @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(); - } - 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); - } + // 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 clearPressedOrFocusedBackground() { - mPressedOrFocusedBackground = null; - setCellLayoutPressedOrFocusedIcon(); + void clearPressedBackground() { + setPressed(false); + setStayPressed(false); } - Bitmap getPressedOrFocusedBackground() { - return mPressedOrFocusedBackground; + @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; } - int getPressedOrFocusedBackgroundPadding() { - return mOutlineHelper.mMaxOuterBlurRadius / 2; + @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); + + mPressedBackground = null; + mIgnorePressedStateChange = false; + updateIconState(); + return result; } @Override diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 89473c8b1..d0b3f4376 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -123,7 +123,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>(); @@ -288,6 +288,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); } @@ -334,14 +337,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; @@ -355,16 +350,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(); + } } } @@ -431,23 +433,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); diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index cf7c22eef..7c9e77eef 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -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); } diff --git a/src/com/android/launcher3/FastBitmapView.java b/src/com/android/launcher3/FastBitmapView.java new file mode 100644 index 000000000..0937eb75e --- /dev/null +++ b/src/com/android/launcher3/FastBitmapView.java @@ -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); + } + } +} diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index df5e0fc33..34e752b85 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -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 diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index c548a6f39..f26f87c39 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -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, diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 21efd7113..a359f1180 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -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); diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java index d7b960aba..b1e0e68a4 100644 --- a/src/com/android/launcher3/HolographicOutlineHelper.java +++ b/src/com/android/launcher3/HolographicOutlineHelper.java @@ -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)); @@ -77,37 +78,15 @@ public class HolographicOutlineHelper { } /** - * 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); - } - - void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, - int outlineColor) { - applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK); + Bitmap createMediumDropShadow(BubbleTextView view) { + final Bitmap result = Bitmap.createBitmap( + view.getWidth() + shadowBitmapPadding + shadowBitmapPadding, + view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset, + Bitmap.Config.ARGB_8888); + + mCanvas.setBitmap(result); + + 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); + + // 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; } - - void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, - int outlineColor, boolean clipAlpha) { - applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha, - MEDIUM); - } - - void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, - int outlineColor) { - applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM); - } - } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 06b77756d..76a85caae 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -146,6 +146,10 @@ public class IconCache { return getFullResDefaultActivityIcon(); } + public int getFullResIconDpi() { + return mIconDpi; + } + public Drawable getFullResIcon(ResolveInfo info) { return getFullResIcon(info.activityInfo); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 88a60d08c..5d9ff1d55 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -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; @@ -100,6 +99,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; +import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; @@ -258,7 +258,7 @@ public class Launcher extends Activity private View mWeightWatcher; private LauncherClings mLauncherClings; - private AppWidgetManager mAppWidgetManager; + private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; private ItemInfo mPendingAddInfo = new ItemInfo(); @@ -439,7 +439,7 @@ public class Launcher extends Activity mStats = new Stats(this); - mAppWidgetManager = AppWidgetManager.getInstance(this); + mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); mAppWidgetHost.startListening(); @@ -1026,10 +1026,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 @@ -1574,6 +1570,7 @@ public class Launcher extends Activity launcherInfo.spanY = spanXY[1]; launcherInfo.minSpanX = mPendingAddInfo.minSpanX; launcherInfo.minSpanY = mPendingAddInfo.minSpanY; + launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo); LauncherModel.addItemToDatabase(this, launcherInfo, container, screenId, cellXY[0], cellXY[1], false); @@ -2199,10 +2196,9 @@ public class Launcher extends Activity mPendingAddWidgetId = appWidgetId; // Launch over to configure widget, if needed - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); - intent.setComponent(appWidgetInfo.configure); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET); + mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this, + mAppWidgetHost, REQUEST_CREATE_APPWIDGET); + } else { // Otherwise just add it Runnable onComplete = new Runnable() { @@ -2286,14 +2282,8 @@ public class Launcher extends Activity appWidgetId = getAppWidgetHost().allocateAppWidgetId(); Bundle options = info.bindOptions; - boolean success = false; - if (options != null) { - success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - info.componentName, options); - } else { - success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - info.componentName); - } + boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( + appWidgetId, info.info, options); if (success) { addAppWidgetImpl(appWidgetId, info, null, info.info); } else { @@ -2301,6 +2291,8 @@ public class Launcher extends Activity Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName); + mAppWidgetManager.getUser(mPendingAddWidgetInfo) + .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE); // TODO: we need to make sure that this accounts for the options bundle. // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); startActivityForResult(intent, REQUEST_BIND_APPWIDGET); @@ -2451,6 +2443,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 +2452,10 @@ public class Launcher extends Activity } } + public void onClickPagedViewIcon(View v) { + startAppShortcutOrInfoActivity(v); + } + public boolean onTouch(View v, MotionEvent event) { return false; } @@ -2475,10 +2473,8 @@ public class Launcher extends Activity mPendingAddInfo.copyFrom(info); mPendingAddWidgetId = widgetId; - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); - intent.setComponent(appWidgetInfo.configure); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId); - Utilities.startActivityForResultSafely(this, intent, REQUEST_RECONFIGURE_APPWIDGET); + AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo, + info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET); } } } @@ -2539,17 +2535,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. * * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}. @@ -2586,7 +2571,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 +2588,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; + 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())); + + } else if (tag instanceof AppInfo) { + shortcut = null; + intent = ((AppInfo) tag).intent; + } else { + throw new IllegalArgumentException("Input must be a Shortcut or AppInfo"); } - final ShortcutInfo shortcut = (ShortcutInfo) tag; - final Intent 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())); boolean success = startActivitySafely(v, intent, tag); - mStats.recordLaunch(intent, shortcut); if (success && v instanceof BubbleTextView) { @@ -4383,15 +4373,9 @@ public class Launcher extends Activity Bundle options = AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); - boolean success = false; int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); - if (options != null) { - success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId, - appWidgetInfo.provider, options); - } else { - success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId, - appWidgetInfo.provider); - } + boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( + newWidgetId, appWidgetInfo, options); // TODO consider showing a permission dialog when the widget is clicked. if (!success) { diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 64e82c754..1ea562b3f 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -299,36 +299,26 @@ public class LauncherBackupHelper implements BackupHelper { Set<String> savedIds = getSavedIdsByType(Key.FAVORITE, in); if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size()); - // Don't backup apps in other profiles for now. - UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); - long userSerialNumber = - UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); - // persist things that have changed since the last backup ContentResolver cr = mContext.getContentResolver(); + // Don't backup apps in other profiles for now. Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, - null, null, null); + getUserSelectionArg(), null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); try { cursor.moveToPosition(-1); while(cursor.moveToNext()) { final long id = cursor.getLong(ID_INDEX); - final long profileId = cursor.getLong(PROFILE_ID_INDEX); - if (userSerialNumber == profileId) { - final long updateTime = cursor.getLong(ID_MODIFIED); - Key key = getKey(Key.FAVORITE, id); - keys.add(key); - final String backupKey = keyToBackupKey(key); - currentIds.add(backupKey); - if (!savedIds.contains(backupKey) || updateTime >= in.t) { - byte[] blob = packFavorite(cursor); - writeRowToBackup(key, blob, out, data); - } else { - if (VERBOSE) Log.v(TAG, "favorite " + id + " was too old: " + updateTime); - } + final long updateTime = cursor.getLong(ID_MODIFIED); + Key key = getKey(Key.FAVORITE, id); + keys.add(key); + final String backupKey = keyToBackupKey(key); + currentIds.add(backupKey); + if (!savedIds.contains(backupKey) || updateTime >= in.t) { + byte[] blob = packFavorite(cursor); + writeRowToBackup(key, blob, out, data); } else { - if (VERBOSE) Log.v(TAG, "favorite " + id + " is for other profile: " - + profileId); + if (VERBOSE) Log.v(TAG, "favorite " + id + " was too old: " + updateTime); } } } finally { @@ -469,20 +459,19 @@ public class LauncherBackupHelper implements BackupHelper { } final ContentResolver cr = mContext.getContentResolver(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; + final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); // read the old ID set Set<String> savedIds = getSavedIdsByType(Key.ICON, in); if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size()); // Don't backup apps in other profiles for now. - UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle(); - long userSerialNumber = - UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); int startRows = out.rows; if (DEBUG) Log.d(TAG, "starting here: " + startRows); + String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " + - Favorites.PROFILE_ID + "=" + userSerialNumber; + getUserSelectionArg(); Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, where, null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); @@ -617,7 +606,8 @@ public class LauncherBackupHelper implements BackupHelper { int startRows = out.rows; if (DEBUG) Log.d(TAG, "starting here: " + startRows); - String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET; + String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND " + + getUserSelectionArg(); Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, where, null, null); Set<String> currentIds = new HashSet<String>(cursor.getCount()); @@ -1192,6 +1182,11 @@ public class LauncherBackupHelper implements BackupHelper { return true; } + private String getUserSelectionArg() { + return Favorites.PROFILE_ID + '=' + UserManagerCompat.getInstance(mContext) + .getSerialNumberForUser(UserHandleCompat.myUserHandle()); + } + private class KeyParsingException extends Throwable { private KeyParsingException(Throwable cause) { super(cause); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 4c9d1a700..bcb45011e 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -52,7 +51,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData; +import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; @@ -3040,11 +3039,11 @@ public class LauncherModel extends BroadcastReceiver public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) { PackageManager packageManager = context.getPackageManager(); final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>(); - widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders()); + widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders()); + Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); - Collections.sort(widgetsAndShortcuts, - new LauncherModel.WidgetAndShortcutNameComparator(packageManager)); + Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context)); return widgetsAndShortcuts; } @@ -3390,44 +3389,6 @@ public class LauncherModel extends BroadcastReceiver return null; } - /** - * Returns a list of all the widgets that can handle configuration with a particular mimeType. - */ - List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) { - final PackageManager packageManager = context.getPackageManager(); - final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities = - new ArrayList<WidgetMimeTypeHandlerData>(); - - final Intent supportsIntent = - new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE); - supportsIntent.setType(mimeType); - - // Create a set of widget configuration components that we can test against - final List<AppWidgetProviderInfo> widgets = - AppWidgetManager.getInstance(context).getInstalledProviders(); - final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget = - new HashMap<ComponentName, AppWidgetProviderInfo>(); - for (AppWidgetProviderInfo info : widgets) { - configurationComponentToWidget.put(info.configure, info); - } - - // Run through each of the intents that can handle this type of clip data, and cross - // reference them with the components that are actual configuration components - final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent, - PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo info : activities) { - final ActivityInfo activityInfo = info.activityInfo; - final ComponentName infoComponent = new ComponentName(activityInfo.packageName, - activityInfo.name); - if (configurationComponentToWidget.containsKey(infoComponent)) { - supportedConfigurationActivities.add( - new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info, - configurationComponentToWidget.get(infoComponent))); - } - } - return supportedConfigurationActivities; - } - ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); @@ -3570,14 +3531,6 @@ public class LauncherModel extends BroadcastReceiver return 0; } }; - public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() { - final Collator collator = Collator.getInstance(); - return new Comparator<AppWidgetProviderInfo>() { - public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) { - return collator.compare(a.label.toString().trim(), b.label.toString().trim()); - } - }; - } static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) { if (info.activityInfo != null) { return new ComponentName(info.activityInfo.packageName, info.activityInfo.name); @@ -3618,11 +3571,14 @@ public class LauncherModel extends BroadcastReceiver } }; public static class WidgetAndShortcutNameComparator implements Comparator<Object> { - private Collator mCollator; - private PackageManager mPackageManager; - private HashMap<Object, String> mLabelCache; - WidgetAndShortcutNameComparator(PackageManager pm) { - mPackageManager = pm; + private final AppWidgetManagerCompat mManager; + private final PackageManager mPackageManager; + private final HashMap<Object, String> mLabelCache; + private final Collator mCollator; + + WidgetAndShortcutNameComparator(Context context) { + mManager = AppWidgetManagerCompat.getInstance(context); + mPackageManager = context.getPackageManager(); mLabelCache = new HashMap<Object, String>(); mCollator = Collator.getInstance(); } @@ -3631,17 +3587,17 @@ public class LauncherModel extends BroadcastReceiver if (mLabelCache.containsKey(a)) { labelA = mLabelCache.get(a); } else { - labelA = (a instanceof AppWidgetProviderInfo) ? - ((AppWidgetProviderInfo) a).label : - ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim(); + labelA = (a instanceof AppWidgetProviderInfo) + ? mManager.loadLabel((AppWidgetProviderInfo) a) + : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim(); mLabelCache.put(a, labelA); } if (mLabelCache.containsKey(b)) { labelB = mLabelCache.get(b); } else { - labelB = (b instanceof AppWidgetProviderInfo) ? - ((AppWidgetProviderInfo) b).label : - ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim(); + labelB = (b instanceof AppWidgetProviderInfo) + ? mManager.loadLabel((AppWidgetProviderInfo) b) + : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim(); mLabelCache.put(b, labelB); } return mCollator.compare(labelA, labelB); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index af655367a..842e0b0ff 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -1650,8 +1650,10 @@ public class LauncherProvider extends ContentProvider { resolved = systemApp; } final ActivityInfo info = resolved.activityInfo; - final Intent intent = buildMainIntent(); - intent.setComponent(new ComponentName(info.packageName, info.name)); + final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName); + if (intent == null) { + return -1; + } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java deleted file mode 100644 index e819d5edf..000000000 --- a/src/com/android/launcher3/PagedViewIcon.java +++ /dev/null @@ -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); - } - } -} diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java index db4aeb940..e6e11a312 100644 --- a/src/com/android/launcher3/PagedViewWidget.java +++ b/src/com/android/launcher3/PagedViewWidget.java @@ -30,6 +30,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.launcher3.compat.AppWidgetManagerCompat; + /** * The linear layout used strictly for the widget/wallpaper tab of the customization tray */ @@ -127,7 +129,7 @@ public class PagedViewWidget extends LinearLayout { image.setMaxWidth(maxWidth); } final TextView name = (TextView) findViewById(R.id.widget_name); - name.setText(info.label); + name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); final TextView dims = (TextView) findViewById(R.id.widget_dims); if (dims != null) { int hSpan = Math.min(cellSpan[0], (int) grid.numColumns); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index f70fc4d86..f20f261f9 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -78,7 +78,7 @@ public final class Utilities { /** * Returns a FastBitmapDrawable with the icon, accurately sized. */ - static FastBitmapDrawable createIconDrawable(Bitmap icon) { + public static FastBitmapDrawable createIconDrawable(Bitmap icon) { FastBitmapDrawable d = new FastBitmapDrawable(icon); d.setFilterBitmap(true); resizeIconDrawable(d); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 1b37700c6..5aa719027 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -5,7 +5,6 @@ import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; @@ -29,6 +28,8 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.Log; +import com.android.launcher3.compat.AppWidgetManagerCompat; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -42,124 +43,123 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -abstract class SoftReferenceThreadLocal<T> { - private ThreadLocal<SoftReference<T>> mThreadLocal; - public SoftReferenceThreadLocal() { - mThreadLocal = new ThreadLocal<SoftReference<T>>(); - } +public class WidgetPreviewLoader { - abstract T initialValue(); + private static abstract class SoftReferenceThreadLocal<T> { + private ThreadLocal<SoftReference<T>> mThreadLocal; + public SoftReferenceThreadLocal() { + mThreadLocal = new ThreadLocal<SoftReference<T>>(); + } - public void set(T t) { - mThreadLocal.set(new SoftReference<T>(t)); - } + abstract T initialValue(); - public T get() { - SoftReference<T> reference = mThreadLocal.get(); - T obj; - if (reference == null) { - obj = initialValue(); - mThreadLocal.set(new SoftReference<T>(obj)); - return obj; - } else { - obj = reference.get(); - if (obj == null) { + public void set(T t) { + mThreadLocal.set(new SoftReference<T>(t)); + } + + public T get() { + SoftReference<T> reference = mThreadLocal.get(); + T obj; + if (reference == null) { obj = initialValue(); mThreadLocal.set(new SoftReference<T>(obj)); + return obj; + } else { + obj = reference.get(); + if (obj == null) { + obj = initialValue(); + mThreadLocal.set(new SoftReference<T>(obj)); + } + return obj; } - return obj; } } -} -class CanvasCache extends SoftReferenceThreadLocal<Canvas> { - @Override - protected Canvas initialValue() { - return new Canvas(); + private static class CanvasCache extends SoftReferenceThreadLocal<Canvas> { + @Override + protected Canvas initialValue() { + return new Canvas(); + } } -} -class PaintCache extends SoftReferenceThreadLocal<Paint> { - @Override - protected Paint initialValue() { - return null; + private static class PaintCache extends SoftReferenceThreadLocal<Paint> { + @Override + protected Paint initialValue() { + return null; + } } -} -class BitmapCache extends SoftReferenceThreadLocal<Bitmap> { - @Override - protected Bitmap initialValue() { - return null; + private static class BitmapCache extends SoftReferenceThreadLocal<Bitmap> { + @Override + protected Bitmap initialValue() { + return null; + } } -} -class RectCache extends SoftReferenceThreadLocal<Rect> { - @Override - protected Rect initialValue() { - return new Rect(); + private static class RectCache extends SoftReferenceThreadLocal<Rect> { + @Override + protected Rect initialValue() { + return new Rect(); + } } -} -class BitmapFactoryOptionsCache extends SoftReferenceThreadLocal<BitmapFactory.Options> { - @Override - protected BitmapFactory.Options initialValue() { - return new BitmapFactory.Options(); + private static class BitmapFactoryOptionsCache extends + SoftReferenceThreadLocal<BitmapFactory.Options> { + @Override + protected BitmapFactory.Options initialValue() { + return new BitmapFactory.Options(); + } } -} -public class WidgetPreviewLoader { - static final String TAG = "WidgetPreviewLoader"; - static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version"; + private static final String TAG = "WidgetPreviewLoader"; + private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version"; + + private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; + private static final HashSet<String> sInvalidPackages = new HashSet<String>(); + + // Used for drawing shortcut previews + private final BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache(); + private final PaintCache mCachedShortcutPreviewPaint = new PaintCache(); + private final CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache(); + + // Used for drawing widget previews + private final CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache(); + private final RectCache mCachedAppWidgetPreviewSrcRect = new RectCache(); + private final RectCache mCachedAppWidgetPreviewDestRect = new RectCache(); + private final PaintCache mCachedAppWidgetPreviewPaint = new PaintCache(); + private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache(); + private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache(); + + private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>(); + private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>(); + + private final Context mContext; + private final int mAppIconSize; + private final IconCache mIconCache; + private final AppWidgetManagerCompat mManager; private int mPreviewBitmapWidth; private int mPreviewBitmapHeight; private String mSize; - private Context mContext; - private PackageManager mPackageManager; private PagedViewCellLayout mWidgetSpacingLayout; - // Used for drawing shortcut previews - private BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache(); - private PaintCache mCachedShortcutPreviewPaint = new PaintCache(); - private CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache(); - - // Used for drawing widget previews - private CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache(); - private RectCache mCachedAppWidgetPreviewSrcRect = new RectCache(); - private RectCache mCachedAppWidgetPreviewDestRect = new RectCache(); - private PaintCache mCachedAppWidgetPreviewPaint = new PaintCache(); - private PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache(); private String mCachedSelectQuery; - private BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache(); - private int mAppIconSize; - private IconCache mIconCache; - - private static final float sWidgetPreviewIconPaddingPercentage = 0.25f; private CacheDb mDb; - private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews; - private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps; - private final static HashSet<String> sInvalidPackages; - private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - static { - sInvalidPackages = new HashSet<String>(); - } - public WidgetPreviewLoader(Context context) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); mContext = context; - mPackageManager = mContext.getPackageManager(); mAppIconSize = grid.iconSizePx; mIconCache = app.getIconCache(); + mManager = AppWidgetManagerCompat.getInstance(context); + mDb = app.getWidgetPreviewCacheDb(); - mLoadedPreviews = new HashMap<String, WeakReference<Bitmap>>(); - mUnusedBitmaps = new ArrayList<SoftReference<Bitmap>>(); SharedPreferences sp = context.getSharedPreferences( LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); @@ -175,7 +175,7 @@ public class WidgetPreviewLoader { editor.commit(); } } - + public void recreateDb() { LauncherAppState app = LauncherAppState.getInstance(); app.recreateWidgetPreviewDb(); @@ -328,7 +328,7 @@ public class WidgetPreviewLoader { String output; if (o instanceof AppWidgetProviderInfo) { sb.append(WIDGET_PREFIX); - sb.append(((AppWidgetProviderInfo) o).provider.flattenToString()); + sb.append(((AppWidgetProviderInfo) o).toString()); output = sb.toString(); sb.setLength(0); } else { @@ -413,7 +413,7 @@ public class WidgetPreviewLoader { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } - public static void removeItemFromDb(final CacheDb cacheDb, final String objectName) { + private static void removeItemFromDb(final CacheDb cacheDb, final String objectName) { new AsyncTask<Void, Void, Void>() { public Void doInBackground(Void ... args) { SQLiteDatabase db = cacheDb.getWritableDatabase(); @@ -473,7 +473,7 @@ public class WidgetPreviewLoader { } } - public Bitmap generatePreview(Object info, Bitmap preview) { + private Bitmap generatePreview(Object info, Bitmap preview) { if (preview != null && (preview.getWidth() != mPreviewBitmapWidth || preview.getHeight() != mPreviewBitmapHeight)) { @@ -491,8 +491,8 @@ public class WidgetPreviewLoader { int[] cellSpans = Launcher.getSpanForWidget(mContext, info); int maxWidth = maxWidthForWidgetPreview(cellSpans[0]); int maxHeight = maxHeightForWidgetPreview(cellSpans[1]); - return generateWidgetPreview(info.provider, info.previewImage, info.icon, - cellSpans[0], cellSpans[1], maxWidth, maxHeight, preview, null); + return generateWidgetPreview(info, cellSpans[0], cellSpans[1], + maxWidth, maxHeight, preview, null); } public int maxWidthForWidgetPreview(int spanX) { @@ -505,22 +505,20 @@ public class WidgetPreviewLoader { mWidgetSpacingLayout.estimateCellHeight(spanY)); } - public Bitmap generateWidgetPreview(ComponentName provider, int previewImage, - int iconId, int cellHSpan, int cellVSpan, int maxPreviewWidth, int maxPreviewHeight, - Bitmap preview, int[] preScaledWidthOut) { + public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan, + int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible - String packageName = provider.getPackageName(); if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE; if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE; Drawable drawable = null; - if (previewImage != 0) { - drawable = mPackageManager.getDrawable(packageName, previewImage, null); + if (info.previewImage != 0) { + drawable = mManager.loadPreview(info); if (drawable != null) { drawable = mutateOnMainThread(drawable); } else { Log.w(TAG, "Can't load widget preview drawable 0x" + - Integer.toHexString(previewImage) + " for provider: " + provider); + Integer.toHexString(info.previewImage) + " for provider: " + info.provider); } } @@ -562,21 +560,16 @@ public class WidgetPreviewLoader { c.setBitmap(null); // Draw the icon in the top left corner - int minOffset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage); + int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE); int smallestSide = Math.min(previewWidth, previewHeight); float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), 1f); try { - Drawable icon = null; - int hoffset = - (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2); - int yoffset = - (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2); - if (iconId > 0) { - icon = mIconCache.getFullResIcon(packageName, iconId); - } + Drawable icon = mManager.loadIcon(info, mIconCache); if (icon != null) { + int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2); + int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2); icon = mutateOnMainThread(icon); renderDrawableToBitmap(icon, defaultPreview, hoffset, yoffset, (int) (mAppIconSize * iconScale), @@ -627,7 +620,7 @@ public class WidgetPreviewLoader { c.drawBitmap(defaultPreview, src, dest, p); c.setBitmap(null); } - return preview; + return mManager.getBadgeBitmap(info, preview); } private Bitmap generateShortcutPreview( @@ -685,18 +678,10 @@ public class WidgetPreviewLoader { return preview; } - - public static void renderDrawableToBitmap( - Drawable d, Bitmap bitmap, int x, int y, int w, int h) { - renderDrawableToBitmap(d, bitmap, x, y, w, h, 1f); - } - private static void renderDrawableToBitmap( - Drawable d, Bitmap bitmap, int x, int y, int w, int h, - float scale) { + Drawable d, Bitmap bitmap, int x, int y, int w, int h) { if (bitmap != null) { Canvas c = new Canvas(bitmap); - c.scale(scale, scale); Rect oldBounds = d.copyBounds(); d.setBounds(x, y, x + w, y + h); d.draw(c); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a6cce9346..ace5e84ed 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -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; @@ -1115,6 +1112,17 @@ public class Workspace extends SmoothPagedView return super.onInterceptTouchEvent(ev); } + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + // Ignore pointer scroll events if the custom content doesn't allow scrolling. + if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID) + && (mCustomContentCallbacks != null) + && !mCustomContentCallbacks.isScrollingAllowed()) { + return false; + } + return super.onGenericMotionEvent(event); + } + protected void reinflateWidgetsIfNecessary() { final int clCount = getChildCount(); for (int i = 0; i < clCount; i++) { @@ -1983,14 +1991,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) { @@ -2006,8 +2007,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(); @@ -2027,22 +2026,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() { @@ -2540,8 +2536,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; @@ -2580,7 +2576,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(); @@ -2595,9 +2591,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; } @@ -2606,15 +2602,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; } @@ -2622,11 +2618,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(), @@ -2638,10 +2634,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; } @@ -2659,21 +2655,20 @@ public class Workspace extends SmoothPagedView CellLayout layout = (CellLayout) child.getParent().getParent(); layout.prepareChildForDrag(child); + beginDragShared(child, this); + } + + public void beginDragShared(View child, DragSource source) { 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); - } + mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING); - public void beginDragShared(View child, DragSource source) { 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(); @@ -2687,7 +2682,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; @@ -2706,7 +2701,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. @@ -2741,14 +2736,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(); @@ -4022,12 +4017,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; } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java new file mode 100644 index 000000000..57fac7f8f --- /dev/null +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -0,0 +1,83 @@ +/* + * 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.compat; + +import android.app.Activity; +import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Bundle; + +import com.android.launcher3.IconCache; +import com.android.launcher3.Utilities; + +import java.util.List; + +public abstract class AppWidgetManagerCompat { + + private static final Object sInstanceLock = new Object(); + private static AppWidgetManagerCompat sInstance; + + + public static AppWidgetManagerCompat getInstance(Context context) { + synchronized (sInstanceLock) { + // TODO change this to use api version once L gets an API number. + if (sInstance == null) { + if (Utilities.isLmp()) { + sInstance = new AppWidgetManagerCompatVL(context); + } else { + sInstance = new AppWidgetManagerCompatV16(context); + } + } + return sInstance; + } + } + + final AppWidgetManager mAppWidgetManager; + final Context mContext; + + AppWidgetManagerCompat(Context context) { + mContext = context; + mAppWidgetManager = AppWidgetManager.getInstance(context); + } + + public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { + return mAppWidgetManager.getAppWidgetInfo(appWidgetId); + } + + public abstract List<AppWidgetProviderInfo> getAllProviders(); + + public abstract String loadLabel(AppWidgetProviderInfo info); + + public abstract boolean bindAppWidgetIdIfAllowed( + int appWidgetId, AppWidgetProviderInfo info, Bundle options); + + public abstract UserHandleCompat getUser(AppWidgetProviderInfo info); + + public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId, + Activity activity, AppWidgetHost host, int requestCode); + + public abstract Drawable loadPreview(AppWidgetProviderInfo info); + + public abstract Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache); + + public abstract Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap); + +} diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java new file mode 100644 index 000000000..f599f4303 --- /dev/null +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java @@ -0,0 +1,90 @@ +/* + * 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.compat; + +import android.app.Activity; +import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; + +import com.android.launcher3.IconCache; +import com.android.launcher3.Utilities; + +import java.util.List; + +class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { + + AppWidgetManagerCompatV16(Context context) { + super(context); + } + + @Override + public List<AppWidgetProviderInfo> getAllProviders() { + return mAppWidgetManager.getInstalledProviders(); + } + + @Override + public String loadLabel(AppWidgetProviderInfo info) { + return info.label.trim(); + } + + @Override + public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, + Bundle options) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider); + } else { + return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options); + } + } + + @Override + public UserHandleCompat getUser(AppWidgetProviderInfo info) { + return UserHandleCompat.myUserHandle(); + } + + @Override + public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity, + AppWidgetHost host, int requestCode) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); + intent.setComponent(info.configure); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); + Utilities.startActivityForResultSafely(activity, intent, requestCode); + } + + @Override + public Drawable loadPreview(AppWidgetProviderInfo info) { + return mContext.getPackageManager().getDrawable( + info.provider.getPackageName(), info.previewImage, null); + } + + @Override + public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) { + return cache.getFullResIcon(info.provider.getPackageName(), info.icon); + } + + @Override + public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) { + return bitmap; + } +} diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java new file mode 100644 index 000000000..535c74bbd --- /dev/null +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -0,0 +1,142 @@ +/* + * 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.compat; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetProviderInfo; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.view.View; +import android.widget.Toast; + +import com.android.launcher3.IconCache; +import com.android.launcher3.R; + +import java.util.ArrayList; +import java.util.List; + +@TargetApi(Build.VERSION_CODES.L) +class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { + + private final UserManager mUserManager; + private final PackageManager mPm; + + AppWidgetManagerCompatVL(Context context) { + super(context); + mPm = context.getPackageManager(); + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + } + + @Override + public List<AppWidgetProviderInfo> getAllProviders() { + ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>(); + for (UserHandle user : mUserManager.getUserProfiles()) { + providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user)); + } + return providers; + } + + @Override + public String loadLabel(AppWidgetProviderInfo info) { + return info.loadLabel(mPm); + } + + @Override + public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, + Bundle options) { + return mAppWidgetManager.bindAppWidgetIdIfAllowed( + appWidgetId, info.getProfile(), info.provider, options); + } + + @Override + public UserHandleCompat getUser(AppWidgetProviderInfo info) { + return UserHandleCompat.fromUser(info.getProfile()); + } + + @Override + public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity, + AppWidgetHost host, int requestCode) { + try { + host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null); + } catch (ActivityNotFoundException e) { + Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + } catch (SecurityException e) { + Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + } + } + + @Override + public Drawable loadPreview(AppWidgetProviderInfo info) { + return info.loadPreviewImage(mContext, 0); + } + + @Override + public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) { + return info.loadIcon(mContext, cache.getFullResIconDpi()); + } + + @Override + public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) { + if (info.getProfile().equals(android.os.Process.myUserHandle())) { + return bitmap; + } + + // Add a user badge in the bottom right of the image. + final Resources res = mContext.getResources(); + final int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size); + final int badgeMargin = res.getDimensionPixelSize(R.dimen.profile_badge_margin); + final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize); + + final int top = bitmap.getHeight() - badgeSize - badgeMargin; + if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + badgeLocation.offset(badgeMargin, top); + } else { + badgeLocation.offset(bitmap.getWidth() - badgeSize - badgeMargin, top); + } + + UserManager userManager = (UserManager) mContext.getSystemService( + Context.USER_SERVICE); + + Drawable drawable = userManager.getBadgedDrawableForUser(new BitmapDrawable(res, bitmap), + info.getProfile(), badgeLocation, 0); + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + bitmap.eraseColor(Color.TRANSPARENT); + Canvas c = new Canvas(bitmap); + drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); + drawable.draw(c); + c.setBitmap(null); + return bitmap; + } +} |