Merge "Moved hide keyboard to its own method" into ub-launcher3-calgary
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6897269..6bfd069 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -164,7 +164,9 @@
of the shortcut container before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">35dp</dimen>
<dimen name="deep_shortcut_icon_size">36dp</dimen>
- <dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
<dimen name="deep_shortcut_anim_translation_y">5dp</dimen>
+ <dimen name="deep_shortcuts_arrow_width">10dp</dimen>
+ <dimen name="deep_shortcuts_arrow_height">8dp</dimen>
+ <dimen name="deep_shortcuts_arrow_vertical_offset">-2dp</dimen>
</resources>
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 921e90c..df87cc2 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -348,7 +348,7 @@
public ShortcutInfo getShortcutInfo() {
if (activityInfo != null) {
- return ShortcutInfo.fromActivityInfo(activityInfo, mContext);
+ return new ShortcutInfo(activityInfo, mContext);
} else {
return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ddd60c8..4561111 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1957,8 +1957,7 @@
}
}
incrementPinnedShortcutCount(key, shouldPin);
- info = ShortcutInfo.fromDeepShortcutInfo(
- pinnedShortcut, context);
+ info = new ShortcutInfo(pinnedShortcut, context);
} else { // item type == ITEM_TYPE_SHORTCUT
info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
@@ -3291,6 +3290,22 @@
}
}
+ /**
+ * Repopulates the shortcut info, possibly updating any icon already on the workspace.
+ */
+ public void updateShortcutInfo(final ShortcutInfoCompat fullDetail, final ShortcutInfo info) {
+ enqueueItemUpdatedTask(new Runnable() {
+ @Override
+ public void run() {
+ info.updateFromDeepShortcutInfo(
+ fullDetail, LauncherAppState.getInstance().getContext());
+ ArrayList<ShortcutInfo> update = new ArrayList<ShortcutInfo>();
+ update.add(info);
+ bindUpdatedShortcuts(update, fullDetail.getUserHandle());
+ }
+ });
+ }
+
private class ShortcutsChangedTask implements Runnable {
private String mPackageName;
private List<ShortcutInfoCompat> mShortcuts;
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index a9f73c2..0cc5a1b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -162,19 +162,17 @@
this.user = user;
}
- public ShortcutInfo(Context context, ShortcutInfo info) {
+ public ShortcutInfo(ShortcutInfo info) {
super(info);
- title = Utilities.trim(info.title);
+ title = info.title;
intent = new Intent(info.intent);
- if (info.iconResource != null) {
- iconResource = new Intent.ShortcutIconResource();
- iconResource.packageName = info.iconResource.packageName;
- iconResource.resourceName = info.iconResource.resourceName;
- }
+ iconResource = info.iconResource;
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
flags = info.flags;
- user = info.user;
status = info.status;
+ mInstallProgress = info.mInstallProgress;
+ isDisabled = info.isDisabled;
+ usingFallbackIcon = info.usingFallbackIcon;
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
@@ -186,6 +184,28 @@
isDisabled = info.isDisabled;
}
+ public ShortcutInfo(LauncherActivityInfoCompat info, Context context) {
+ user = info.getUser();
+ title = Utilities.trim(info.getLabel());
+ contentDescription = UserManagerCompat.getInstance(context)
+ .getBadgedLabelForUser(info.getLabel(), info.getUser());
+ intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ flags = AppInfo.initFlags(info);
+ }
+
+ /**
+ * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}.
+ */
+ @TargetApi(Build.VERSION_CODES.N)
+ public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+ user = shortcutInfo.getUserHandle();
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+ intent = shortcutInfo.makeIntent(context);
+ flags = 0;
+ updateFromDeepShortcutInfo(shortcutInfo, context);
+ }
+
public void setIcon(Bitmap b) {
mIcon = b;
}
@@ -265,33 +285,6 @@
return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
}
- public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) {
- final ShortcutInfo shortcut = new ShortcutInfo();
- shortcut.user = info.getUser();
- shortcut.title = Utilities.trim(info.getLabel());
- shortcut.contentDescription = UserManagerCompat.getInstance(context)
- .getBadgedLabelForUser(info.getLabel(), info.getUser());
- shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
- shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- shortcut.flags = AppInfo.initFlags(info);
- return shortcut;
- }
-
- /**
- * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}. Pardon the overloaded name.
- */
- @TargetApi(Build.VERSION_CODES.N)
- public static ShortcutInfo fromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo,
- Context context) {
- ShortcutInfo si = new ShortcutInfo();
- si.user = shortcutInfo.getUserHandle();
- si.itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
- si.intent = shortcutInfo.makeIntent(context);
- si.flags = 0;
- si.updateFromDeepShortcutInfo(shortcutInfo, context);
- return si;
- }
-
public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
title = shortcutInfo.getShortLabel();
@@ -306,11 +299,14 @@
Drawable unbadgedIcon = launcherAppState.getShortcutManager()
.getShortcutIconDrawable(shortcutInfo,
launcherAppState.getInvariantDeviceProfile().fillResIconDpi);
- Bitmap icon = unbadgedIcon == null ? null
- : Utilities.createBadgedIconBitmapWithShadow(unbadgedIcon, user, context);
+ Bitmap icon = unbadgedIcon == null ? null : getBadgedIcon(unbadgedIcon, context);
setIcon(icon != null ? icon : launcherAppState.getIconCache().getDefaultIcon(user));
}
+ protected Bitmap getBadgedIcon(Drawable unbadgedIcon, Context context) {
+ return Utilities.createBadgedIconBitmapWithShadow(unbadgedIcon, user, context);
+ }
+
/** Returns the ShortcutInfo id associated with the deep shortcut. */
public String getDeepShortcutId() {
return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
@@ -322,4 +318,3 @@
return isDisabled != 0;
}
}
-
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ef78195..f09b7cc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -264,18 +264,25 @@
}
/**
+ * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
+ * normalized with other icons and has enough spacing to add shadow.
+ */
+ public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) {
+ RectF iconBounds = new RectF();
+ float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
+ 1 : IconNormalizer.getInstance().getScale(icon, iconBounds);
+ scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds));
+ return createIconBitmap(icon, context, scale);
+ }
+
+ /**
* Same as {@link #createBadgedIconBitmap} but adds a shadow before badging the icon
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Bitmap createBadgedIconBitmapWithShadow(
Drawable icon, UserHandleCompat user, Context context) {
- RectF iconBounds = new RectF();
- float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
- 1 : IconNormalizer.getInstance().getScale(icon, iconBounds);
- scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds));
-
- Bitmap bitmap = createIconBitmap(icon, context, scale);
- bitmap = ShadowGenerator.getInstance().recreateIcon(bitmap);
+ Bitmap bitmap = ShadowGenerator.getInstance().recreateIcon(
+ createScaledBitmapWithoutShadow(icon, context));
if (Utilities.ATLEAST_LOLLIPOP && user != null
&& !UserHandleCompat.myUserHandle().equals(user)) {
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 946c306..4ed2467 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2301,11 +2301,19 @@
}
public void beginDragShared(View child, DragSource source, boolean accessible) {
- beginDragShared(child, new Point(), source, accessible, new DragPreviewProvider(child));
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+ beginDragShared(child, new Point(), source, accessible,
+ (ItemInfo) dragObject, new DragPreviewProvider(child));
}
public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
- boolean accessible, DragPreviewProvider previewProvider) {
+ boolean accessible, ItemInfo dragObject, DragPreviewProvider previewProvider) {
child.clearFocus();
child.setPressed(false);
@@ -2362,20 +2370,12 @@
icon.clearPressedBackground();
}
- Object dragObject = child.getTag();
- if (!(dragObject instanceof ItemInfo)) {
- String msg = "Drag started with a view that has no tag set. This "
- + "will cause a crash (issue 11627249) down the line. "
- + "View: " + child + " tag: " + child.getTag();
- throw new IllegalStateException(msg);
- }
-
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
- (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
+ dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
dragRect, scale, accessible);
dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
diff --git a/src/com/android/launcher3/graphics/TriangleShape.java b/src/com/android/launcher3/graphics/TriangleShape.java
new file mode 100644
index 0000000..cce4e3c
--- /dev/null
+++ b/src/com/android/launcher3/graphics/TriangleShape.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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.graphics;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+import android.support.annotation.NonNull;
+
+/**
+ * Wrapper around {@link android.graphics.drawable.shapes.PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ */
+public class TriangleShape extends PathShape {
+ private Path mTriangularPath;
+
+ public TriangleShape(Path path, float stdWidth, float stdHeight) {
+ super(path, stdWidth, stdHeight);
+ mTriangularPath = path;
+ }
+
+ public static TriangleShape create(float width, float height, boolean isPointingUp) {
+ Path triangularPath = new Path();
+ if (isPointingUp) {
+ triangularPath.moveTo(0, height);
+ triangularPath.lineTo(width, height);
+ triangularPath.lineTo(width / 2, 0);
+ triangularPath.close();
+ } else {
+ triangularPath.moveTo(0, 0);
+ triangularPath.lineTo(width / 2, height);
+ triangularPath.lineTo(width, 0);
+ triangularPath.close();
+ }
+ return new TriangleShape(triangularPath, width, height);
+ }
+
+ @Override
+ public void getOutline(@NonNull Outline outline) {
+ outline.setConvexPath(mTriangularPath);
+ }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 0b94791..0d771ad 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -9,14 +9,12 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
-import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewConfiguration;
@@ -24,6 +22,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.util.TransformingTouchDelegate;
/**
* A PageIndicator that briefly shows a fraction of a line when moving between pages.
@@ -61,7 +60,7 @@
private Paint mLinePaint;
private Launcher mLauncher;
private final int mLineHeight;
- private final Rect mTouchHitRect = new Rect();
+ private final TransformingTouchDelegate mTouchDelegate;
private final int mTouchExtensionHeight;
private final int mCaretSizePx;
private final int mCaretWorkspaceOffsetPx;
@@ -142,6 +141,13 @@
mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
mTouchExtensionHeight = res.getDimensionPixelSize(
R.dimen.dynamic_grid_page_indicator_extra_touch_height);
+ mTouchDelegate = new TransformingTouchDelegate(this);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mLauncher.getDragLayer().setTouchDelegate(mTouchDelegate);
}
@Override
@@ -157,9 +163,8 @@
View parent = mLauncher.getDragLayer();
sTempCoords[0] = sTempCoords[1] = 0;
Utilities.getDescendantCoordRelativeToAncestor(this, parent, sTempCoords, true);
- mTouchHitRect.set(sTempCoords[0], sTempCoords[1], sTempCoords[0] + this.getWidth(),
+ mTouchDelegate.setBounds(sTempCoords[0], sTempCoords[1], sTempCoords[0] + this.getWidth(),
sTempCoords[1] + getHeight() + mTouchExtensionHeight);
- parent.setTouchDelegate(new TouchDelegate(mTouchHitRect, this));
}
@Override
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index f9dd336..7cb2d43 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
-import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
@@ -40,26 +39,8 @@
*/
public class DeepShortcutView extends FrameLayout {
- private static final float HOVER_SCALE = 1.05f;
-
- // The direction this view should translate when animating the hover state.
- // This allows hovered shortcuts to "push" other shortcuts away.
- @IntDef({DIRECTION_UP, DIRECTION_NONE, DIRECTION_DOWN})
- public @interface TranslationDirection {}
-
- public static final int DIRECTION_UP = -1;
- public static final int DIRECTION_NONE = 0;
- public static final int DIRECTION_DOWN = 1;
-
- @TranslationDirection
- private int mTranslationDirection = DIRECTION_NONE;
-
- private int mSpacing;
private int mRadius;
private Rect mPillRect;
- private int mTop;
- private boolean mIsHoveringOver = false;
- private LauncherViewPropertyAnimator mHoverAnimator;
private BubbleTextView mBubbleText;
@@ -74,10 +55,8 @@
public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mSpacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
mRadius = getResources().getDimensionPixelSize(R.dimen.bg_pill_radius);
mPillRect = new Rect();
- mHoverAnimator = new LauncherViewPropertyAnimator(this);
}
@Override
@@ -110,11 +89,8 @@
/**
* Creates an animator to play when the shortcut container is being opened.
- *
- * @param animationIndex The index at which this animation will be started
- * relative to other DeepShortcutView open animations.
*/
- public Animator createOpenAnimation(int animationIndex, boolean isContainerAboveIcon) {
+ public Animator createOpenAnimation(long animationDelay, boolean isContainerAboveIcon) {
final Resources res = getResources();
setVisibility(INVISIBLE);
@@ -140,59 +116,9 @@
.scaleX(1).scaleY(1);
openAnimation.playTogether(reveal, translationY, scale);
- openAnimation.setStartDelay(animationIndex * res.getInteger(
- R.integer.config_deepShortcutOpenStagger));
+ openAnimation.setStartDelay(animationDelay);
openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration));
openAnimation.setInterpolator(new DecelerateInterpolator());
return openAnimation;
}
-
- /**
- * Updates the state of this view based on touches over the container before user lifts finger.
- *
- * @param containerContainsTouch whether the {@link DeepShortcutsContainer} this shortcut
- * is inside contains the current touch
- * @param isBelowHoveredShortcut whether a sibling shortcut before this one in the
- * view hierarchy is being hovered over
- * @param touchY the y coordinate of the touch, relative to the {@link DeepShortcutsContainer}
- * this shortcut is inside
- * @return whether this shortcut is being hovered over
- */
- public boolean updateHoverState(boolean containerContainsTouch, boolean isBelowHoveredShortcut,
- float touchY) {
- if (!containerContainsTouch) {
- mIsHoveringOver = false;
- mTranslationDirection = DIRECTION_NONE;
- } else if (isBelowHoveredShortcut) {
- mIsHoveringOver = false;
- mTranslationDirection = DIRECTION_DOWN;
- } else {
- // Include space around the view when determining hover state to avoid gaps.
- mTop = (int) (getY() - getTranslationY());
- mIsHoveringOver = (touchY >= mTop - mSpacing / 2)
- && (touchY < mTop + getHeight() + mSpacing / 2);
- mTranslationDirection = mIsHoveringOver ? DIRECTION_NONE : DIRECTION_UP;
- }
- animateHoverState();
- return mIsHoveringOver;
- }
-
- /**
- * If this shortcut is being hovered over, we scale it up. If another shortcut is being hovered
- * over, we translate this one away from it to account for its increased size.
- */
- private void animateHoverState() {
- if (mHoverAnimator.isRunning()) {
- return;
- }
- float scale = mIsHoveringOver ? HOVER_SCALE : 1f;
- float translateY = (HOVER_SCALE - 1f) * getHeight() * mTranslationDirection;
- mHoverAnimator.scaleX(scale).scaleY(scale).translationY(translateY)
- .setDuration(getResources().getInteger(R.integer.config_deepShortcutHoverDuration))
- .start();
- }
-
- public boolean isHoveringOver() {
- return mIsHoveringOver;
- }
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 92afeb9..c881c8c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -17,19 +17,23 @@
package com.android.launcher3.shortcuts;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -45,6 +49,7 @@
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherViewPropertyAnimator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
@@ -55,6 +60,7 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.graphics.ScaledPreviewProvider;
+import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -89,7 +95,7 @@
private Point mIconLastTouchPos = new Point();
private boolean mIsLeftAligned;
private boolean mIsAboveIcon;
- private boolean mIsAnimatingOpen;
+ private View mArrow;
private boolean mSrcIconDragStarted;
@@ -168,8 +174,8 @@
Collections.sort(shortcuts, shortcutsComparator);
for (int i = 0; i < shortcuts.size(); i++) {
final ShortcutInfoCompat shortcut = shortcuts.get(i);
- final ShortcutInfo launcherShortcutInfo = ShortcutInfo
- .fromDeepShortcutInfo(shortcut, mLauncher);
+ final ShortcutInfo launcherShortcutInfo =
+ new UnbadgedShortcutInfo(shortcut, mLauncher);
CharSequence shortLabel = shortcut.getShortLabel();
CharSequence longLabel = shortcut.getLongLabel();
uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo,
@@ -212,34 +218,56 @@
}
private DeepShortcutView getShortcutAt(int index) {
+ if (!mIsAboveIcon) {
+ // Opening down, so arrow is the first view.
+ index++;
+ }
return (DeepShortcutView) getChildAt(index);
}
+ private int getShortcutCount() {
+ // All children except the arrow are shortcuts.
+ return getChildCount() - 1;
+ }
+
private void animateOpen(BubbleTextView originalIcon) {
orientAboutIcon(originalIcon);
+ final Resources resources = getResources();
+ final int arrowWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_width);
+ final int arrowHeight = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_height);
+ int iconWidth = originalIcon.getWidth() - originalIcon.getTotalPaddingLeft()
+ - originalIcon.getTotalPaddingRight();
+ iconWidth *= originalIcon.getScaleX();
+ final int arrowHorizontalOffset = iconWidth / 2 - arrowWidth / 2;
+ final int arrowVerticalOffset = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcuts_arrow_vertical_offset);
+ mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
setVisibility(View.VISIBLE);
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
- final int numShortcuts = getChildCount();
- final int arrowOffset = getResources().getDimensionPixelSize(
- R.dimen.deep_shortcuts_arrow_horizontal_offset);
- final int pivotX = mIsLeftAligned ? arrowOffset : getMeasuredWidth() - arrowOffset;
+ final int shortcutCount = getShortcutCount();
+ final int pivotX = mIsLeftAligned ? arrowHorizontalOffset
+ : getMeasuredWidth() - arrowHorizontalOffset;
final int pivotY = getShortcutAt(0).getMeasuredHeight() / 2;
- for (int i = 0; i < numShortcuts; i++) {
+ for (int i = 0; i < shortcutCount; i++) {
DeepShortcutView deepShortcutView = getShortcutAt(i);
deepShortcutView.setPivotX(pivotX);
deepShortcutView.setPivotY(pivotY);
- int animationIndex = mIsAboveIcon ? numShortcuts - i - 1 : i;
- shortcutAnims.play(deepShortcutView.createOpenAnimation(animationIndex, mIsAboveIcon));
+ int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i;
+ long animationDelay = animationIndex * getResources().getInteger(
+ R.integer.config_deepShortcutOpenStagger);
+ shortcutAnims.play(deepShortcutView.createOpenAnimation(animationDelay, mIsAboveIcon));
}
- shortcutAnims.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingOpen = false;
- }
- });
- mIsAnimatingOpen = true;
+ mArrow.setScaleX(0);
+ mArrow.setScaleY(0);
+ final long shortcutAnimDuration = shortcutAnims.getChildAnimations().get(0).getDuration();
+ final long arrowScaleDelay = shortcutAnimDuration / 6;
+ final long arrowScaleDuration = shortcutAnimDuration - arrowScaleDelay;
+ Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1);
+ arrowScale.setStartDelay(arrowScaleDelay);
+ arrowScale.setDuration(arrowScaleDuration);
+ shortcutAnims.play(arrowScale);
shortcutAnims.start();
}
@@ -254,8 +282,6 @@
*
* So we always align left if there is enough horizontal space
* and align above if there is enough vertical space.
- *
- * TODO: draw arrow based on orientation.
*/
private void orientAboutIcon(BubbleTextView icon) {
int width = getMeasuredWidth();
@@ -294,6 +320,36 @@
setY(y);
}
+ /**
+ * Adds an arrow view pointing at the original icon.
+ * @param horizontalOffset the horizontal offset of the arrow, so that it
+ * points at the center of the original icon
+ */
+ private View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
+ LinearLayout.LayoutParams layoutParams = new LayoutParams(width, height);
+ if (mIsLeftAligned) {
+ layoutParams.gravity = Gravity.LEFT;
+ layoutParams.leftMargin = horizontalOffset;
+ } else {
+ layoutParams.gravity = Gravity.RIGHT;
+ layoutParams.rightMargin = horizontalOffset;
+ }
+ if (mIsAboveIcon) {
+ layoutParams.topMargin = verticalOffset;
+ } else {
+ layoutParams.bottomMargin = verticalOffset;
+ }
+
+ View arrowView = new View(getContext());
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ width, height, !mIsAboveIcon));
+ arrowDrawable.getPaint().setColor(Color.WHITE);
+ arrowView.setBackground(arrowDrawable);
+ arrowView.setElevation(getElevation());
+ addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
+ return arrowView;
+ }
+
private void deferDrag(BubbleTextView originalIcon) {
mDeferredDragIcon = originalIcon;
showDragView(originalIcon);
@@ -348,7 +404,7 @@
Utilities.translateEventCoordinates(this, mLauncher.getDragLayer(), ev);
final int dragLayerX = (int) ev.getX();
final int dragLayerY = (int) ev.getY();
- int childCount = getChildCount();
+ int shortcutCount = getShortcutCount();
if (action == MotionEvent.ACTION_MOVE) {
if (mLastX != 0 || mLastY != 0) {
mDistanceDragged += Math.hypot(mLastX - x, mLastY - y);
@@ -356,48 +412,24 @@
mLastX = x;
mLastY = y;
- boolean containerContainsTouch = x >= 0 && y >= 0 && x < getWidth() && y < getHeight();
- if (shouldStartDeferredDrag((int) x, (int) y, containerContainsTouch)) {
+ if (shouldStartDeferredDrag((int) x, (int) y)) {
+ DeepShortcutView topShortcut = getShortcutAt(0);
+ DeepShortcutView bottomShortcut = getShortcutAt(shortcutCount - 1);
mSrcIconDragStarted = true;
cleanupDeferredDrag(true);
mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
mDeferredDragIcon.getOnLongClickListener().onLongClick(mDeferredDragIcon);
mLauncher.getDragController().onTouchEvent(ev);
return true;
- } else {
- // Determine whether touch is over a shortcut.
- boolean hoveringOverShortcut = false;
- for (int i = 0; i < childCount && !mIsAnimatingOpen; i++) {
- DeepShortcutView shortcut = getShortcutAt(i);
- if (shortcut.updateHoverState(containerContainsTouch, hoveringOverShortcut, y)) {
- hoveringOverShortcut = true;
- }
- }
-
- if (!hoveringOverShortcut && mDistanceDragged > mDragDeadzone) {
- // After dragging further than a small deadzone,
- // have the drag view follow the user's finger.
- mDragView.setVisibility(VISIBLE);
- mDragView.move(dragLayerX, dragLayerY);
- mDeferredDragIcon.setVisibility(INVISIBLE);
- } else if (hoveringOverShortcut) {
- // Jump drag view back to original place on grid,
- // so user doesn't think they are still dragging.
- // TODO: can we improve this interaction? maybe with a ghost icon or similar?
- mDragView.setVisibility(INVISIBLE);
- mDeferredDragIcon.setVisibility(VISIBLE);
- }
+ } else if (mDistanceDragged > mDragDeadzone) {
+ // After dragging further than a small deadzone,
+ // have the drag view follow the user's finger.
+ mDragView.setVisibility(VISIBLE);
+ mDragView.move(dragLayerX, dragLayerY);
+ mDeferredDragIcon.setVisibility(INVISIBLE);
}
} else if (action == MotionEvent.ACTION_UP) {
cleanupDeferredDrag(true);
- // Launch a shortcut if user was hovering over it.
- for (int i = 0; i < childCount; i++) {
- DeepShortcutView shortcut = getShortcutAt(i);
- if (shortcut.isHoveringOver()) {
- shortcut.getBubbleText().performClick();
- break;
- }
- }
} else if (action == MotionEvent.ACTION_CANCEL) {
// Do not change the source icon visibility if we are already dragging the source icon.
cleanupDeferredDrag(!mSrcIconDragStarted);
@@ -410,19 +442,14 @@
* relative to the original icon and the shortcuts container.
*
* Current behavior:
- * - Compute distance from original touch down to closest container edge.
- * - Compute distance from latest touch (given x and y) and compare to original distance;
- * if the new distance is larger than a threshold, the deferred drag should start.
- * - Never defer the drag if this container contains the touch.
+ * - Start the drag if the touch passes a certain distance from the original touch down.
*
* @param x the x touch coordinate relative to this container
* @param y the y touch coordinate relative to this container
*/
- private boolean shouldStartDeferredDrag(int x, int y, boolean containerContainsTouch) {
- int closestEdgeY = mIsAboveIcon ? getMeasuredHeight() : 0;
- double distToEdge = Math.abs(mTouchDown[1] - closestEdgeY);
- double newDistToEdge = Math.hypot(x - mTouchDown[0], y - closestEdgeY);
- return !containerContainsTouch && (newDistToEdge - distToEdge > mStartDragThreshold);
+ private boolean shouldStartDeferredDrag(int x, int y) {
+ double distFromTouchDown = Math.hypot(x - mTouchDown[0], y - mTouchDown[1]);
+ return distFromTouchDown > mStartDragThreshold;
}
public void cleanupDeferredDrag(boolean updateSrcVisibility) {
@@ -452,8 +479,14 @@
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return false;
+ UnbadgedShortcutInfo unbadgedInfo = (UnbadgedShortcutInfo) v.getTag();
+ ShortcutInfo badged = new ShortcutInfo(unbadgedInfo);
+ // Queue an update task on the worker thread. This ensures that the badged
+ // shortcut eventually gets its icon updated.
+ mLauncher.getModel().updateShortcutInfo(unbadgedInfo.mDetail, badged);
+
// Long clicked on a shortcut.
- mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false,
+ mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false, badged,
new ScaledPreviewProvider(v));
// TODO: support dragging from within folder without having to close it
mLauncher.closeFolder();
@@ -536,4 +569,21 @@
}
return null;
}
+
+ /**
+ * Extension of {@link ShortcutInfo} which does not badge the icons.
+ */
+ private static class UnbadgedShortcutInfo extends ShortcutInfo {
+ private final ShortcutInfoCompat mDetail;
+
+ public UnbadgedShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+ super(shortcutInfo, context);
+ mDetail = shortcutInfo;
+ }
+
+ @Override
+ protected Bitmap getBadgedIcon(Drawable unbadgedIcon, Context context) {
+ return Utilities.createScaledBitmapWithoutShadow(unbadgedIcon, context);
+ }
+ }
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
index f94595b..507939a 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
@@ -4,7 +4,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewParent;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CheckLongPressHelper;
@@ -22,18 +21,12 @@
/** Scaled touch slop, used for detecting movement outside bounds. */
private final float mScaledTouchSlop;
- /** Timeout before disallowing intercept on the source's parent. */
- private final int mTapTimeout;
-
/** Timeout before accepting a long-press to start forwarding. */
private final int mLongPressTimeout;
/** Source view from which events are forwarded. */
private final BubbleTextView mSrcIcon;
- /** Runnable used to prevent conflicts with scrolling parents. */
- private Runnable mDisallowIntercept;
-
/** Runnable used to trigger forwarding on long-press. */
private Runnable mTriggerLongPress;
@@ -56,7 +49,6 @@
public ShortcutsContainerListener(BubbleTextView icon) {
mSrcIcon = icon;
mScaledTouchSlop = ViewConfiguration.get(icon.getContext()).getScaledTouchSlop();
- mTapTimeout = ViewConfiguration.getTapTimeout();
mLongPressTimeout = CheckLongPressHelper.DEFAULT_LONG_PRESS_TIMEOUT;
@@ -113,10 +105,6 @@
public void onViewDetachedFromWindow(View v) {
mForwarding = false;
mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-
- if (mDisallowIntercept != null) {
- mSrcIcon.removeCallbacks(mDisallowIntercept);
- }
}
/**
@@ -158,11 +146,6 @@
case MotionEvent.ACTION_DOWN:
mActivePointerId = srcEvent.getPointerId(0);
- if (mDisallowIntercept == null) {
- mDisallowIntercept = new DisallowIntercept();
- }
- src.postDelayed(mDisallowIntercept, mTapTimeout);
-
if (mTriggerLongPress == null) {
mTriggerLongPress = new TriggerLongPress();
}
@@ -195,10 +178,6 @@
if (mTriggerLongPress != null) {
mSrcIcon.removeCallbacks(mTriggerLongPress);
}
-
- if (mDisallowIntercept != null) {
- mSrcIcon.removeCallbacks(mDisallowIntercept);
- }
}
private void onLongPress() {
@@ -265,14 +244,6 @@
return handled && keepForwarding;
}
- private class DisallowIntercept implements Runnable {
- @Override
- public void run() {
- final ViewParent parent = mSrcIcon.getParent();
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
private class TriggerLongPress implements Runnable {
@Override
public void run() {
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 7dbc0e7a..6661429 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -111,7 +111,7 @@
for (int i = 0; i < count; i++) {
LauncherActivityInstallInfo info = apps.get(i);
- ShortcutInfo si = ShortcutInfo.fromActivityInfo(info.info, mContext);
+ ShortcutInfo si = new ShortcutInfo(info.info, mContext);
((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
}
diff --git a/src/com/android/launcher3/util/TransformingTouchDelegate.java b/src/com/android/launcher3/util/TransformingTouchDelegate.java
new file mode 100644
index 0000000..da1de5e
--- /dev/null
+++ b/src/com/android/launcher3/util/TransformingTouchDelegate.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+
+/**
+ * This class differs from the framework {@link TouchDelegate} in that it transforms the
+ * coordinates of the motion event to the provided bounds.
+ *
+ * You can also modify the bounds post construction. Since the bounds are available during layout,
+ * this avoids new object creation during every layout.
+ */
+public class TransformingTouchDelegate extends TouchDelegate {
+ private static final Rect sTempRect = new Rect();
+
+ private final RectF mBounds;
+
+ private View mDelegateView;
+ private boolean mDelegateTargeted;
+
+ public TransformingTouchDelegate(View delegateView) {
+ super(sTempRect, delegateView);
+
+ mDelegateView = delegateView;
+ mBounds = new RectF();
+ }
+
+ public void setBounds(int left, int top, int right, int bottom) {
+ mBounds.set(left, top, right, bottom);
+ }
+
+ public void setDelegateView(View view) {
+ mDelegateView = view;
+ }
+
+ /**
+ * Will forward touch events to the delegate view if the event is within the bounds
+ * specified in the constructor.
+ *
+ * @param event The touch event to forward
+ * @return True if the event was forwarded to the delegate, false otherwise.
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean sendToDelegate = false;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDelegateTargeted = mBounds.contains(event.getX(), event.getY());
+ if (mDelegateTargeted) {
+ sendToDelegate = true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ sendToDelegate = mDelegateTargeted;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ sendToDelegate = mDelegateTargeted;
+ mDelegateTargeted = false;
+ break;
+ }
+ boolean handled = false;
+ if (sendToDelegate) {
+ event.offsetLocation(-mBounds.left, -mBounds.top);
+ handled = mDelegateView.dispatchTouchEvent(event);
+ event.offsetLocation(mBounds.left, mBounds.top);
+ }
+ return handled;
+ }
+}