Merge "Fade QSB with the workspace as the overlay enters" into ub-launcher3-calgary
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/deep_shortcuts_drag_handle.xml
index d5fca2e..99d2b07 100644
--- a/res/drawable/deep_shortcuts_drag_handle.xml
+++ b/res/drawable/deep_shortcuts_drag_handle.xml
@@ -15,12 +15,12 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/deep_shortcut_drag_handle_size"
+ android:height="@dimen/deep_shortcut_drag_handle_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M20 9H4v2h16V9zM4 15h16v-2H4v2z"
- android:fillColor="#757575"/>
+ android:fillColor="#4D000000"/>
</vector>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index bdedff0..9438042 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -26,6 +26,7 @@
<!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
Note: This should be less than the config_overlayTransitionTime as they happen together. -->
<integer name="config_overlayRevealTime">220</integer>
+ <integer name="config_overlaySlideRevealTime">320</integer>
<integer name="config_overlayTransitionTime">300</integer>
<integer name="config_overlayItemsAlphaStagger">60</integer>
@@ -101,4 +102,5 @@
<item type="id" name="action_move_screen_backwards" />
<item type="id" name="action_move_screen_forwards" />
<item type="id" name="action_resize" />
+ <item type="id" name="action_deep_shortcuts" />
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6897269..7436478 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -155,7 +155,7 @@
<!-- Deep shortcuts -->
<dimen name="deep_shortcuts_elevation">9dp</dimen>
- <dimen name="bg_pill_width">180dp</dimen>
+ <dimen name="bg_pill_width">208dp</dimen>
<dimen name="bg_pill_height">48dp</dimen>
<dimen name="bg_pill_radius">24dp</dimen>
<dimen name="deep_shortcuts_spacing">4dp</dimen>
@@ -164,7 +164,18 @@
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_padding_start">6dp</dimen>
+ <dimen name="deep_shortcut_padding_end">16dp</dimen>
+ <dimen name="deep_shortcut_drawable_padding">8dp</dimen>
<dimen name="deep_shortcut_anim_translation_y">5dp</dimen>
+ <dimen name="deep_shortcut_drag_handle_size">16dp</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>
+ <!-- deep_shortcut_padding_start + deep_shortcut_icon_size / 2 - deep_shortcuts_arrow_width / 2-->
+ <!-- Note that this works for right-aligned shortcuts, too, because
+ deep_shortcut_padding_end + deep_shortcut_drag_handle_size / 2 - deep_shortcuts_arrow_width / 2
+ also happens to equal 19dp-->
+ <dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7bd9ff7..9f30752 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -249,4 +249,7 @@
<!-- Accessibility confirmation for widget resize. -->
<string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
+ <!-- Accessibility action to show quick actions menu for an icon. [CHAR_LIMIT=30] -->
+ <string name="action_deep_shortcut">Shortcuts</string>
+
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 627c433..a647cf2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -80,9 +80,9 @@
<style name="Icon.DeepShortcut">
<item name="android:gravity">start|center_vertical</item>
<item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
- <item name="android:paddingLeft">7dp</item>
- <item name="android:paddingRight">12dp</item>
- <item name="android:drawablePadding">9dp</item>
+ <item name="android:paddingStart">@dimen/deep_shortcut_padding_start</item>
+ <item name="android:paddingEnd">@dimen/deep_shortcut_padding_end</item>
+ <item name="android:drawablePadding">@dimen/deep_shortcut_drawable_padding</item>
<item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:shadowRadius">0</item>
<item name="customShadows">false</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index c0c6673..33b3ad3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -56,11 +56,12 @@
private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
- 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;
+ // Dimensions in DP
+ private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
+ private static final float KEY_SHADOW_RADIUS = 1f;
+ private static final float KEY_SHADOW_OFFSET = 0.5f;
+ private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
+ private static final int KEY_SHADOW_COLOR = 0x66000000;
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -136,6 +137,10 @@
// Draw the background itself as the parent is drawn twice.
mBackground = getBackground();
setBackground(null);
+
+ // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
+ float density = getResources().getDisplayMetrics().density;
+ setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
} else {
mBackground = null;
}
@@ -144,10 +149,6 @@
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
- if (mCustomShadowsEnabled) {
- setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
- }
-
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
}
@@ -408,13 +409,15 @@
}
// We enhance the shadow by drawing the shadow twice
- getPaint().setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
+ float density = getResources().getDisplayMetrics().density;
+ getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
super.draw(canvas);
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
getScrollX() + getWidth(),
getScrollY() + getHeight(), Region.Op.INTERSECT);
- getPaint().setShadowLayer(SHADOW_SMALL_RADIUS, 0.0f, 0.0f, SHADOW_SMALL_COLOUR);
+ getPaint().setShadowLayer(
+ density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
super.draw(canvas);
canvas.restore();
}
@@ -649,6 +652,13 @@
}
/**
+ * Returns true if the view can show custom shortcuts.
+ */
+ public boolean hasDeepShortcuts() {
+ return !mLauncher.getShortcutIdsForItem((ItemInfo) getTag()).isEmpty();
+ }
+
+ /**
* Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
*/
private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
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/Launcher.java b/src/com/android/launcher3/Launcher.java
index f8b7c74..f22219f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -997,8 +997,8 @@
// Don't update the predicted apps if the user is returning to launcher in the apps
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
- showAppsView(false /* animated */, false /* resetListToTop */,
- !launchedFromApp /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
+ false /* focusSearchBar */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
@@ -2601,8 +2601,8 @@
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.TAP,
LauncherLogProto.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, false /* resetListToTop */,
- true /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(true /* animated */, true /* updatePredictedApps */,
+ false /* focusSearchBar */);
}
}
@@ -2611,7 +2611,7 @@
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.LONGPRESS,
LauncherLogProto.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, false /* resetListToTop */,
+ showAppsView(true /* animated */,
true /* updatePredictedApps */, true /* focusSearchBar */);
}
}
@@ -3296,7 +3296,7 @@
public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
- if (changed) {
+ if (changed || mAllAppsController.isTransitioning()) {
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.NORMAL, animated, onCompleteRunnable);
@@ -3355,12 +3355,9 @@
/**
* Shows the apps view.
*/
- public void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
+ public void showAppsView(boolean animated, boolean updatePredictedApps,
boolean focusSearchBar) {
markAppsViewShown();
- if (resetListToTop) {
- mAppsView.scrollToTop();
- }
if (updatePredictedApps) {
tryAndUpdatePredictedApps();
}
@@ -3393,8 +3390,10 @@
// TODO: calling method should use the return value so that when {@code false} is returned
// the workspace transition doesn't fall into invalid state.
private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
- if (mState != State.WORKSPACE && mState != State.APPS_SPRING_LOADED &&
- mState != State.WIDGETS_SPRING_LOADED) {
+ if (!(mState == State.WORKSPACE ||
+ mState == State.APPS_SPRING_LOADED ||
+ mState == State.WIDGETS_SPRING_LOADED ||
+ (mState == State.APPS && mAllAppsController.isTransitioning()))) {
return false;
}
if (toState != State.APPS && toState != State.WIDGETS) {
@@ -3485,7 +3484,7 @@
void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
- showAppsView(true /* animated */, false /* resetListToTop */,
+ showAppsView(true /* animated */,
false /* updatePredictedApps */, false /* focusSearchBar */);
} else if (mState == State.WIDGETS_SPRING_LOADED) {
showWidgetsView(true, false);
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/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 1fe0813..b0358e8 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -207,7 +207,8 @@
Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
}
- if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
+ if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
+ || mAllAppsController.isTransitioning()) {
int animType = CIRCULAR_REVEAL;
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
animType = PULLUP;
@@ -236,6 +237,8 @@
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
+ final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
+
final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View fromView = mLauncher.getWorkspace();
@@ -434,7 +437,7 @@
pCb.onTransitionComplete();
}
});
- mAllAppsController.animateToAllApps(animation, revealDuration, false);
+ mAllAppsController.animateToAllApps(animation, revealDurationSlide);
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -679,6 +682,7 @@
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
+ final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -898,9 +902,8 @@
@Override
public void onAnimationEnd(Animator animation) {
if (canceled) return;
- dispatchOnLauncherTransitionEnd(fromView, animated, false);
- dispatchOnLauncherTransitionEnd(toView, animated, false);
-
+ dispatchOnLauncherTransitionEnd(fromView, animated, true);
+ dispatchOnLauncherTransitionEnd(toView, animated, true);
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
@@ -918,7 +921,7 @@
}
});
- mAllAppsController.animateToWorkspace(animation, revealDuration, false);
+ mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
// Dispatch the prepare transition signal
dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
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 121acc6..3c057e6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2305,11 +2305,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);
@@ -2366,20 +2374,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());
@@ -2588,6 +2588,7 @@
fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
postAnimationRunnable);
} else {
+ fi.prepareCreate(v);
fi.addItem(destInfo);
fi.addItem(sourceInfo);
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 6bf8abf..0562cf5 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -18,6 +18,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragSource;
@@ -36,6 +37,8 @@
import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.shortcuts.DeepShortcutTextView;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -45,13 +48,14 @@
private static final String TAG = "LauncherAccessibilityDelegate";
- private static final int REMOVE = R.id.action_remove;
- private static final int INFO = R.id.action_info;
- private static final int UNINSTALL = R.id.action_uninstall;
- private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
- private static final int MOVE = R.id.action_move;
- private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
- private static final int RESIZE = R.id.action_resize;
+ protected static final int REMOVE = R.id.action_remove;
+ protected static final int INFO = R.id.action_info;
+ protected static final int UNINSTALL = R.id.action_uninstall;
+ protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
+ protected static final int MOVE = R.id.action_move;
+ protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
+ protected static final int RESIZE = R.id.action_resize;
+ protected static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
public enum DragType {
ICON,
@@ -65,7 +69,7 @@
public View item;
}
- private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
+ protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
@Thunk final Launcher mLauncher;
private DragInfo mDragInfo = null;
@@ -88,14 +92,24 @@
launcher.getText(R.string.action_move_to_workspace)));
mActions.put(RESIZE, new AccessibilityAction(RESIZE,
launcher.getText(R.string.action_resize)));
+ mActions.put(DEEP_SHORTCUTS, new AccessibilityAction(DEEP_SHORTCUTS,
+ launcher.getText(R.string.action_deep_shortcut)));
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
+ addActions(host, info);
+ }
+
+ protected void addActions(View host, AccessibilityNodeInfo info) {
if (!(host.getTag() instanceof ItemInfo)) return;
ItemInfo item = (ItemInfo) host.getTag();
+ if (host instanceof BubbleTextView && ((BubbleTextView) host).hasDeepShortcuts()) {
+ info.addAction(mActions.get(DEEP_SHORTCUTS));
+ }
+
if (DeleteDropTarget.supportsAccessibleDrop(item)) {
info.addAction(mActions.get(REMOVE));
}
@@ -215,6 +229,9 @@
}
})
.show();
+ return true;
+ } else if (action == DEEP_SHORTCUTS) {
+ return DeepShortcutsContainer.showForIcon((BubbleTextView) host) != null;
}
return false;
}
@@ -397,7 +414,7 @@
/**
* Find empty space on the workspace and returns the screenId.
*/
- private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
+ protected long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
Workspace workspace = mLauncher.getWorkspace();
ArrayList<Long> workspaceScreens = workspace.getScreenOrder();
long screenId;
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
new file mode 100644
index 0000000..ff70279
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -0,0 +1,73 @@
+/*
+ * 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.accessibility;
+
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Extension of {@link LauncherAccessibilityDelegate} with actions specific to shortcuts in
+ * deep shortcuts menu.
+ */
+public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDelegate {
+
+ public ShortcutMenuAccessibilityDelegate(Launcher launcher) {
+ super(launcher);
+ }
+
+ @Override
+ protected void addActions(View host, AccessibilityNodeInfo info) {
+ info.addAction(mActions.get(ADD_TO_WORKSPACE));
+ }
+
+ @Override
+ public boolean performAction(View host, ItemInfo item, int action) {
+ if (action == ADD_TO_WORKSPACE) {
+ final ShortcutInfo info = (ShortcutInfo) item;
+ final int[] coordinates = new int[2];
+ final long screenId = findSpaceOnWorkspace(item, coordinates);
+ Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ LauncherModel.addItemToDatabase(mLauncher, info,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ screenId, coordinates[0], coordinates[1]);
+ ArrayList<ItemInfo> itemList = new ArrayList<>();
+ itemList.add(info);
+ mLauncher.bindItems(itemList, 0, itemList.size(), true);
+ mLauncher.closeShortcutsContainer();
+ announceConfirmation(R.string.item_added_to_workspace);
+ }
+ };
+
+ if (!mLauncher.showWorkspace(true, onComplete)) {
+ onComplete.run();
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 428f784..d860189 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -348,9 +348,10 @@
mAppsRecyclerView.preMeasureViews(mAdapter);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
- // TODO(hyunyoungs): clean up setting the content and the reveal view.
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
getRevealView().setVisibility(View.VISIBLE);
+ getContentView().setVisibility(View.VISIBLE);
+ getContentView().setBackground(null);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index e75210b..b965d74 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -140,10 +140,15 @@
* Resets the search bar state.
*/
public void reset() {
- mQuery = null;
unfocusSearchField();
mCb.clearSearchResult();
mInput.setText("");
+ // We need to reset this after we clear the input text
+ mQuery = null;
+ hideKeyboard();
+ }
+
+ protected void hideKeyboard() {
mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 78280f7..0e9cac8 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -6,12 +6,12 @@
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
@@ -43,7 +43,8 @@
private static final boolean DBG = false;
private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
- private final Interpolator mDecelInterpolator = new DecelerateInterpolator(1f);
+ private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
+ private final Interpolator mScrollInterpolator = new PagedView.ScrollInterpolator();
private static final float ANIMATION_DURATION = 1200;
@@ -153,7 +154,7 @@
if (mDetector.isSettlingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) {
return false;
}
- return mDetector.shouldIntercept();
+ return mDetector.isDraggingOrSettling();
}
private boolean shouldPossiblyIntercept(MotionEvent ev) {
@@ -222,27 +223,19 @@
LauncherLogProto.Action.FLING,
LauncherLogProto.Action.UP,
LauncherLogProto.HOTSEAT);
- mLauncher.showAppsView(true, true, false, false);
- } else {
- animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
}
+ mLauncher.showAppsView(true /* animated */,
+ false /* updatePredictedApps */,
+ false /* focusSearchBar */);
} else {
calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
- if (mLauncher.isAllAppsVisible()) {
- mLauncher.showWorkspace(true);
- } else {
- animateToWorkspace(mCurrentAnimation, mAnimationDuration, true);
- }
+ mLauncher.showWorkspace(true);
}
// snap to top or bottom using the release velocity
} else {
if (mAppsView.getTranslationY() > mShiftRange / 2) {
calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
- if (mLauncher.isAllAppsVisible()) {
- mLauncher.showWorkspace(true);
- } else {
- animateToWorkspace(mCurrentAnimation, mAnimationDuration, true);
- }
+ mLauncher.showWorkspace(true);
} else {
calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
if (!mLauncher.isAllAppsVisible()) {
@@ -250,15 +243,18 @@
LauncherLogProto.Action.SWIPE,
LauncherLogProto.Action.UP,
LauncherLogProto.HOTSEAT);
- mLauncher.showAppsView(true, true, false, false);
- } else {
- animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
}
-
+ mLauncher.showAppsView(true, /* animated */
+ false /* updatePredictedApps */,
+ false /* focusSearchBar */);
}
}
}
+ public boolean isTransitioning() {
+ return mDetector.isDraggingOrSettling();
+ }
+
/**
* @param start {@code true} if start of new drag.
*/
@@ -267,15 +263,11 @@
// Initialize values that should not change until #onDragEnd
mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
mHotseat.setVisibility(View.VISIBLE);
- mHotseat.bringToFront();
if (!mLauncher.isAllAppsVisible()) {
mLauncher.tryAndUpdatePredictedApps();
mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor();
mHotseat.setBackgroundTransparent(true /* transparent */);
mAppsView.setVisibility(View.VISIBLE);
- mAppsView.getContentView().setVisibility(View.VISIBLE);
- mAppsView.getContentView().setBackground(null);
- mAppsView.getRevealView().setVisibility(View.VISIBLE);
mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
}
}
@@ -354,7 +346,8 @@
}
}
- public void animateToAllApps(AnimatorSet animationOut, long duration, boolean start) {
+ public void animateToAllApps(AnimatorSet animationOut, long duration) {
+ Interpolator interpolator;
if (animationOut == null) {
return;
}
@@ -362,12 +355,15 @@
preparePull(true);
mAnimationDuration = duration;
mShiftStart = mAppsView.getTranslationY();
+ interpolator = mFastOutSlowInInterpolator;
+ } else {
+ interpolator = mScrollInterpolator;
}
final float fromAllAppsTop = mAppsView.getTranslationY();
ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
fromAllAppsTop / mShiftRange, 0f);
driftAndAlpha.setDuration(mAnimationDuration);
- driftAndAlpha.setInterpolator(new PagedView.ScrollInterpolator());
+ driftAndAlpha.setInterpolator(interpolator);
animationOut.play(driftAndAlpha);
animationOut.addListener(new AnimatorListenerAdapter() {
@@ -390,9 +386,6 @@
}
});
mCurrentAnimation = animationOut;
- if (start) {
- mCurrentAnimation.start();
- }
}
public void showDiscoveryBounce() {
@@ -425,21 +418,25 @@
});
}
- public void animateToWorkspace(AnimatorSet animationOut, long duration, boolean start) {
+ public void animateToWorkspace(AnimatorSet animationOut, long duration) {
if (animationOut == null) {
return;
}
+ Interpolator interpolator;
if (mDetector.isIdleState()) {
preparePull(true);
mAnimationDuration = duration;
mShiftStart = mAppsView.getTranslationY();
+ interpolator = mFastOutSlowInInterpolator;
+ } else {
+ interpolator = mScrollInterpolator;
}
final float fromAllAppsTop = mAppsView.getTranslationY();
ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
fromAllAppsTop / mShiftRange, 1f);
driftAndAlpha.setDuration(mAnimationDuration);
- driftAndAlpha.setInterpolator(new PagedView.ScrollInterpolator());
+ driftAndAlpha.setInterpolator(interpolator);
animationOut.play(driftAndAlpha);
animationOut.addListener(new AnimatorListenerAdapter() {
@@ -462,9 +459,6 @@
}
});
mCurrentAnimation = animationOut;
- if (start) {
- mCurrentAnimation.start();
- }
}
public void finishPullUp() {
@@ -530,6 +524,7 @@
mCaretAnimator.setDuration(mCaretAnimationDuration);
mCaretAnimator.setInterpolator(mCaretInterpolator);
mHotseat.addOnLayoutChangeListener(this);
+ mHotseat.bringToFront();
}
@Override
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
index 0e8ba7f..8bb845a 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -72,7 +72,7 @@
mState = newState;
}
- public boolean shouldIntercept() {
+ public boolean isDraggingOrSettling() {
return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d08cf54..eebbfe8 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -262,14 +262,19 @@
}
};
+ public Drawable prepareCreate(final View destView) {
+ Drawable animateDrawable = getTopDrawable((TextView) destView);
+ computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+ destView.getMeasuredWidth());
+ return animateDrawable;
+ }
+
public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
// These correspond two the drawable and view that the icon was dropped _onto_
- Drawable animateDrawable = getTopDrawable((TextView) destView);
- computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
- destView.getMeasuredWidth());
+ Drawable animateDrawable = prepareCreate(destView);
mReferenceDrawable = animateDrawable;
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 82c79a9..3df1d24 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -197,7 +197,7 @@
*/
public int allocateRankForNewItem(ShortcutInfo info) {
int rank = getItemCount();
- ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
+ ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
views.add(rank, null);
arrangeChildren(views, views.size(), false);
setCurrentPage(rank / mMaxItemsPerPage);
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 c9eee81..a693f15 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -17,19 +17,24 @@
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;
import android.view.View;
@@ -44,15 +49,18 @@
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;
import com.android.launcher3.Workspace;
+import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.dragndrop.DragController;
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;
@@ -70,10 +78,12 @@
UserEventDispatcher.LaunchSourceProvider {
private static final String TAG = "ShortcutsContainer";
- private Launcher mLauncher;
- private DeepShortcutManager mDeepShortcutsManager;
+ private final Launcher mLauncher;
+ private final DeepShortcutManager mDeepShortcutsManager;
private final int mDragDeadzone;
private final int mStartDragThreshold;
+ private final ShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+
private BubbleTextView mDeferredDragIcon;
private int mActivePointerId;
private int[] mTouchDown = null;
@@ -85,7 +95,11 @@
private Point mIconLastTouchPos = new Point();
private boolean mIsLeftAligned;
private boolean mIsAboveIcon;
- private boolean mIsAnimatingOpen;
+ private View mArrow;
+ private boolean mSrcIconDragStarted;
+ private LauncherViewPropertyAnimator mArrowHoverAnimator;
+ private boolean mIsRtl;
+ private int mArrowHorizontalOffset;
/**
* Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
@@ -115,6 +129,8 @@
mDragDeadzone = ViewConfiguration.get(context).getScaledTouchSlop();
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
+ mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
+ mIsRtl = Utilities.isRtl(getResources());
}
public DeepShortcutsContainer(Context context, AttributeSet attrs) {
@@ -128,17 +144,32 @@
public void populateAndShow(final BubbleTextView originalIcon, final List<String> ids) {
// Add dummy views first, and populate with real shortcut info when ready.
final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
- final LayoutInflater inflator = mLauncher.getLayoutInflater();
+ final LayoutInflater inflater = mLauncher.getLayoutInflater();
for (int i = 0; i < ids.size(); i++) {
- final View shortcut = inflator.inflate(R.layout.deep_shortcut, this, false);
+ final DeepShortcutView shortcut =
+ (DeepShortcutView) inflater.inflate(R.layout.deep_shortcut, this, false);
if (i < ids.size() - 1) {
((LayoutParams) shortcut.getLayoutParams()).bottomMargin = spacing;
}
+ shortcut.getBubbleText().setAccessibilityDelegate(mAccessibilityDelegate);
addView(shortcut);
}
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- animateOpen(originalIcon);
+ orientAboutIcon(originalIcon);
+
+ // Add the arrow.
+ 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);
+ mArrowHorizontalOffset = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcuts_arrow_horizontal_offset);
+ final int arrowVerticalOffset = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcuts_arrow_vertical_offset);
+ mArrow = addArrowView(mArrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
+ mArrowHoverAnimator = new LauncherViewPropertyAnimator(mArrow);
+
+ animateOpen();
deferDrag(originalIcon);
@@ -159,8 +190,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,
@@ -203,34 +234,44 @@
}
private DeepShortcutView getShortcutAt(int index) {
+ if (!mIsAboveIcon) {
+ // Opening down, so arrow is the first view.
+ index++;
+ }
return (DeepShortcutView) getChildAt(index);
}
- private void animateOpen(BubbleTextView originalIcon) {
- orientAboutIcon(originalIcon);
+ private int getShortcutCount() {
+ // All children except the arrow are shortcuts.
+ return getChildCount() - 1;
+ }
+ private void animateOpen() {
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 ? mArrowHorizontalOffset
+ : getMeasuredWidth() - mArrowHorizontalOffset;
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();
}
@@ -245,8 +286,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();
@@ -257,20 +296,40 @@
Rect insets = dragLayer.getInsets();
// Align left (right in RTL) if there is room.
- boolean isRtl = Utilities.isRtl(getResources());
int leftAlignedX = mTempRect.left + icon.getPaddingLeft();
int rightAlignedX = mTempRect.right - width - icon.getPaddingRight();
int x = leftAlignedX;
boolean canBeLeftAligned = leftAlignedX + width < dragLayer.getRight() - insets.right;
boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
- if (!canBeLeftAligned || (isRtl && canBeRightAligned)) {
+ if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
x = rightAlignedX;
}
mIsLeftAligned = x == leftAlignedX;
- if (isRtl) {
+ if (mIsRtl) {
x -= dragLayer.getWidth() - width;
}
+ // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
+ int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight();
+ iconWidth *= icon.getScaleX();
+ Resources resources = getResources();
+ int xOffset;
+ if (isAlignedWithStart()) {
+ // Aligning with the shortcut icon.
+ int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
+ int shortcutPaddingStart = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcut_padding_start);
+ xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
+ } else {
+ // Aligning with the drag handle.
+ int shortcutDragHandleWidth = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcut_drag_handle_size);
+ int shortcutPaddingEnd = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcut_padding_end);
+ xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
+ }
+ x += mIsLeftAligned ? xOffset : -xOffset;
+
// Open above icon if there is room.
int y = mTempRect.top - height;
mIsAboveIcon = mTempRect.top - height > dragLayer.getTop() + insets.top;
@@ -285,6 +344,40 @@
setY(y);
}
+ private boolean isAlignedWithStart() {
+ return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+ }
+
+ /**
+ * 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);
@@ -339,7 +432,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);
@@ -347,49 +440,27 @@
mLastX = x;
mLastY = y;
- boolean containerContainsTouch = x >= 0 && y >= 0 && x < getWidth() && y < getHeight();
- if (shouldStartDeferredDrag((int) x, (int) y, containerContainsTouch)) {
- cleanupDeferredDrag();
+ 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();
- // 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;
- }
- }
+ cleanupDeferredDrag(true);
} else if (action == MotionEvent.ACTION_CANCEL) {
- cleanupDeferredDrag();
+ // Do not change the source icon visibility if we are already dragging the source icon.
+ cleanupDeferredDrag(!mSrcIconDragStarted);
}
return true;
}
@@ -399,26 +470,23 @@
* 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() {
+ public void cleanupDeferredDrag(boolean updateSrcVisibility) {
if (mDragView != null) {
mDragView.remove();
}
- mDeferredDragIcon.setVisibility(VISIBLE);
+ if (updateSrcVisibility) {
+ mDeferredDragIcon.setVisibility(VISIBLE);
+ }
}
@Override
@@ -439,8 +507,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();
@@ -501,4 +575,43 @@
// TODO: add target.rank
targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
}
+
+ /**
+ * Shows the shortcuts container for {@param icon}
+ * @return the container if shown or null.
+ */
+ public static DeepShortcutsContainer showForIcon(BubbleTextView icon) {
+ Launcher launcher = Launcher.getLauncher(icon.getContext());
+ List<String> ids = launcher.getShortcutIdsForItem((ItemInfo) icon.getTag());
+ if (!ids.isEmpty()) {
+ // There are shortcuts associated with the app, so defer its drag.
+ final DeepShortcutsContainer container =
+ (DeepShortcutsContainer) launcher.getLayoutInflater().inflate(
+ R.layout.deep_shortcuts_container, launcher.getDragLayer(), false);
+ container.setVisibility(View.INVISIBLE);
+ launcher.getDragLayer().addView(container);
+ container.populateAndShow(icon, ids);
+ icon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ return container;
+ }
+ 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 f3b0d04..507939a 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
@@ -1,24 +1,16 @@
package com.android.launcher3.shortcuts;
-import android.content.Context;
import android.os.SystemClock;
-import android.view.HapticFeedbackConstants;
-import android.view.LayoutInflater;
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;
-import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
-import java.util.List;
-
/**
* A {@link android.view.View.OnTouchListener} that creates a {@link DeepShortcutsContainer} and
* forwards touch events to it. This listener should be put on any icon that supports shortcuts.
@@ -29,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;
@@ -58,11 +44,11 @@
/** If true, the gesture is not handled. The value is reset when next gesture starts. */
private boolean mIgnoreCurrentGesture;
+ private DeepShortcutsContainer mShortcutsContainer;
public ShortcutsContainerListener(BubbleTextView icon) {
mSrcIcon = icon;
mScaledTouchSlop = ViewConfiguration.get(icon.getContext()).getScaledTouchSlop();
- mTapTimeout = ViewConfiguration.getTapTimeout();
mLongPressTimeout = CheckLongPressHelper.DEFAULT_LONG_PRESS_TIMEOUT;
@@ -78,8 +64,7 @@
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// There are no shortcuts associated with this item,
// so return to normal touch handling.
- mIgnoreCurrentGesture =
- (mLauncher.getShortcutIdsForItem((ItemInfo) v.getTag())).isEmpty();
+ mIgnoreCurrentGesture = !mSrcIcon.hasDeepShortcuts();
mTouchDown[0] = (int) event.getX();
mTouchDown[1] = (int) event.getY();
@@ -120,10 +105,6 @@
public void onViewDetachedFromWindow(View v) {
mForwarding = false;
mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-
- if (mDisallowIntercept != null) {
- mSrcIcon.removeCallbacks(mDisallowIntercept);
- }
}
/**
@@ -134,21 +115,8 @@
* @return true to start forwarding, false otherwise
*/
protected boolean onForwardingStarted() {
- List<String> ids = mLauncher.getShortcutIdsForItem((ItemInfo) mSrcIcon.getTag());
- if (!ids.isEmpty()) {
- // There are shortcuts associated with the app, so defer its drag.
- LayoutInflater layoutInflater = (LayoutInflater) mLauncher.getSystemService
- (Context.LAYOUT_INFLATER_SERVICE);
- final DeepShortcutsContainer deepShortcutsContainer = (DeepShortcutsContainer)
- layoutInflater.inflate(R.layout.deep_shortcuts_container, mDragLayer, false);
- deepShortcutsContainer.setVisibility(View.INVISIBLE);
- mDragLayer.addView(deepShortcutsContainer);
- deepShortcutsContainer.populateAndShow(mSrcIcon, ids);
- mSrcIcon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- return true;
- }
- return false;
+ mShortcutsContainer = DeepShortcutsContainer.showForIcon(mSrcIcon);
+ return mShortcutsContainer != null;
}
/**
@@ -157,6 +125,7 @@
* @return true to stop forwarding, false otherwise
*/
protected boolean onForwardingStopped() {
+ mShortcutsContainer = null;
return true;
}
@@ -177,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();
}
@@ -214,17 +178,13 @@
if (mTriggerLongPress != null) {
mSrcIcon.removeCallbacks(mTriggerLongPress);
}
-
- if (mDisallowIntercept != null) {
- mSrcIcon.removeCallbacks(mDisallowIntercept);
- }
}
private void onLongPress() {
clearCallbacks();
- final View src = mSrcIcon;
- if (!src.isEnabled() || mLauncher.getShortcutIdsForItem((ItemInfo) src.getTag()).isEmpty()) {
+ final BubbleTextView src = mSrcIcon;
+ if (!src.isEnabled() || !src.hasDeepShortcuts()) {
// Ignore long-press if the view is disabled or doesn't have shortcuts.
return;
}
@@ -254,8 +214,7 @@
*/
private boolean onTouchForwarded(MotionEvent srcEvent) {
final View src = mSrcIcon;
-
- final DeepShortcutsContainer dst = mLauncher.getOpenShortcutsContainer();
+ final DeepShortcutsContainer dst = mShortcutsContainer;
if (dst == null) {
return false;
}
@@ -285,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;
+ }
+}