summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout/deep_shortcut.xml2
-rw-r--r--res/layout/popup_container.xml (renamed from res/layout/deep_shortcuts_container.xml)4
-rw-r--r--src/com/android/launcher3/AbstractFloatingView.java6
-rw-r--r--src/com/android/launcher3/BubbleTextView.java2
-rw-r--r--src/com/android/launcher3/Launcher.java10
-rw-r--r--src/com/android/launcher3/Workspace.java9
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java4
-rw-r--r--src/com/android/launcher3/badge/BadgeInfo.java2
-rw-r--r--src/com/android/launcher3/badge/NotificationInfo.java (renamed from src/com/android/launcher3/badging/NotificationInfo.java)6
-rw-r--r--src/com/android/launcher3/badge/NotificationListener.java (renamed from src/com/android/launcher3/badging/NotificationListener.java)2
-rw-r--r--src/com/android/launcher3/keyboard/CustomActionsPopup.java4
-rw-r--r--src/com/android/launcher3/popup/PopupContainerWithArrow.java (renamed from src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java)265
-rw-r--r--src/com/android/launcher3/popup/PopupDataProvider.java2
-rw-r--r--src/com/android/launcher3/popup/PopupItemView.java208
-rw-r--r--src/com/android/launcher3/popup/PopupPopulator.java179
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutView.java145
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutFilter.java92
-rw-r--r--tests/src/com/android/launcher3/popup/PopupPopulatorTest.java (renamed from tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java)31
18 files changed, 563 insertions, 410 deletions
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 7b42ec70e..1f67682b8 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -30,7 +30,7 @@
android:background="@drawable/bg_pill_focused" />
<View
- android:id="@+id/deep_shortcut_icon"
+ android:id="@+id/popup_item_icon"
android:layout_width="@dimen/deep_shortcut_icon_size"
android:layout_height="@dimen/deep_shortcut_icon_size"
android:layout_margin="@dimen/deep_shortcut_padding_start"
diff --git a/res/layout/deep_shortcuts_container.xml b/res/layout/popup_container.xml
index 68bb60f24..e9cfe24ee 100644
--- a/res/layout/deep_shortcuts_container.xml
+++ b/res/layout/popup_container.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.launcher3.shortcuts.DeepShortcutsContainer
+<com.android.launcher3.popup.PopupContainerWithArrow
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/deep_shortcuts_container"
android:layout_width="wrap_content"
@@ -26,4 +26,4 @@
android:elevation="@dimen/deep_shortcuts_elevation"
android:orientation="vertical">
-</com.android.launcher3.shortcuts.DeepShortcutsContainer> \ No newline at end of file
+</com.android.launcher3.popup.PopupContainerWithArrow> \ No newline at end of file
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 65da00211..c834c6bdb 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -32,11 +32,11 @@ import java.lang.annotation.RetentionPolicy;
*/
public abstract class AbstractFloatingView extends LinearLayout {
- @IntDef(flag = true, value = {TYPE_FOLDER, TYPE_DEEPSHORTCUT_CONTAINER})
+ @IntDef(flag = true, value = {TYPE_FOLDER, TYPE_POPUP_CONTAINER_WITH_ARROW})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
public static final int TYPE_FOLDER = 1 << 0;
- public static final int TYPE_DEEPSHORTCUT_CONTAINER = 1 << 1;
+ public static final int TYPE_POPUP_CONTAINER_WITH_ARROW = 1 << 1;
protected boolean mIsOpen;
@@ -119,6 +119,6 @@ public abstract class AbstractFloatingView extends LinearLayout {
}
public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_FOLDER | TYPE_DEEPSHORTCUT_CONTAINER);
+ return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW);
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 47a5b4fbd..ed8b53132 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -41,14 +41,12 @@ import com.android.launcher3.IconCache.IconLoadRequest;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.badging.NotificationInfo;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.model.PackageItemInfo;
import java.text.NumberFormat;
-import java.util.List;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 26e388d15..e2108a710 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,7 @@ import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.badging.NotificationListener;
+import com.android.launcher3.badge.NotificationListener;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -109,8 +109,8 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -1738,7 +1738,7 @@ public class Launcher extends BaseActivity
mWorkspace.exitWidgetResizeMode();
AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
- if (topOpenView instanceof DeepShortcutsContainer) {
+ if (topOpenView instanceof PopupContainerWithArrow) {
ued.logActionCommand(Action.Command.HOME_INTENT,
topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
} else if (topOpenView instanceof Folder) {
@@ -2256,7 +2256,7 @@ public class Launcher extends BaseActivity
if (topView.getActiveTextView() != null) {
topView.getActiveTextView().dispatchBackKey();
} else {
- if (topView instanceof DeepShortcutsContainer) {
+ if (topView instanceof PopupContainerWithArrow) {
ued.logActionCommand(Action.Command.BACK,
topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
} else if (topView instanceof Folder) {
@@ -4077,7 +4077,7 @@ public class Launcher extends BaseActivity
&& mAccessibilityDelegate.performAction(focusedView,
(ItemInfo) focusedView.getTag(),
LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
- DeepShortcutsContainer.getOpen(this).requestFocus();
+ PopupContainerWithArrow.getOpen(this).requestFocus();
return true;
}
break;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e646dd916..cd1d8d9a2 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -72,7 +72,7 @@ import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -2267,9 +2267,10 @@ public class Workspace extends PagedView
}
if (child instanceof BubbleTextView) {
- DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon((BubbleTextView) child);
- if (dsc != null) {
- dragOptions.preDragCondition = dsc.createPreDragCondition();
+ PopupContainerWithArrow popupContainer = PopupContainerWithArrow
+ .showForIcon((BubbleTextView) child);
+ if (popupContainer != null) {
+ dragOptions.preDragCondition = popupContainer.createPreDragCondition();
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 93478523c..b77493bef 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.popup.PopupContainerWithArrow;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FolderInfo;
@@ -36,7 +37,6 @@ import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -231,7 +231,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
.show();
return true;
} else if (action == DEEP_SHORTCUTS) {
- return DeepShortcutsContainer.showForIcon((BubbleTextView) host) != null;
+ return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
}
return false;
}
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
index 98d2277d0..4255c5132 100644
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ b/src/com/android/launcher3/badge/BadgeInfo.java
@@ -30,7 +30,7 @@ public class BadgeInfo {
private PackageUserKey mPackageUserKey;
/**
* The keys of the notifications that this badge represents. These keys can later be
- * used to retrieve {@link com.android.launcher3.badging.NotificationInfo}'s.
+ * used to retrieve {@link NotificationInfo}'s.
*/
private Set<String> mNotificationKeys;
diff --git a/src/com/android/launcher3/badging/NotificationInfo.java b/src/com/android/launcher3/badge/NotificationInfo.java
index 2590add53..51f6a4f3a 100644
--- a/src/com/android/launcher3/badging/NotificationInfo.java
+++ b/src/com/android/launcher3/badge/NotificationInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.badging;
+package com.android.launcher3.badge;
import android.app.Notification;
import android.app.PendingIntent;
@@ -25,7 +25,7 @@ import android.service.notification.StatusBarNotification;
import android.view.View;
import com.android.launcher3.Launcher;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.util.PackageUserKey;
/**
@@ -77,6 +77,6 @@ public class NotificationInfo implements View.OnClickListener {
if (autoCancel) {
launcher.getPopupDataProvider().cancelNotification(notificationKey);
}
- DeepShortcutsContainer.getOpen(launcher).close(true);
+ PopupContainerWithArrow.getOpen(launcher).close(true);
}
}
diff --git a/src/com/android/launcher3/badging/NotificationListener.java b/src/com/android/launcher3/badge/NotificationListener.java
index 0a85d566b..1668a6267 100644
--- a/src/com/android/launcher3/badging/NotificationListener.java
+++ b/src/com/android/launcher3/badge/NotificationListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.badging;
+package com.android.launcher3.badge;
import android.app.Notification;
import android.os.Handler;
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 6603e93b0..bb0b58add 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -24,10 +24,10 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import java.util.ArrayList;
import java.util.Collections;
@@ -46,7 +46,7 @@ public class CustomActionsPopup implements OnMenuItemClickListener {
public CustomActionsPopup(Launcher launcher, View icon) {
mLauncher = launcher;
mIcon = icon;
- DeepShortcutsContainer container = DeepShortcutsContainer.getOpen(launcher);
+ PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
if (container != null) {
mDelegate = container.getAccessibilityDelegate();
} else {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 5e12a57c7..95d51dc9c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.shortcuts;
+package com.android.launcher3.popup;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -22,8 +22,8 @@ import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
-import android.content.ComponentName;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
@@ -33,7 +33,6 @@ import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -43,7 +42,6 @@ import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
@@ -57,69 +55,101 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherViewPropertyAnimator;
import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.TriangleShape;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.util.PackageUserKey;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.*;
+
/**
* A container for shortcuts to deep links within apps.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class DeepShortcutsContainer extends AbstractFloatingView
- implements View.OnLongClickListener,
- View.OnTouchListener, DragSource, DragController.DragListener {
+public class PopupContainerWithArrow extends AbstractFloatingView
+ implements View.OnLongClickListener, View.OnTouchListener, DragSource,
+ DragController.DragListener {
private final Point mIconShift = new Point();
+ private final Point mIconLastTouchPos = new Point();
- private final Launcher mLauncher;
- private final DeepShortcutManager mDeepShortcutsManager;
+ protected final Launcher mLauncher;
private final int mStartDragThreshold;
- private final ShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+ private LauncherAccessibilityDelegate mAccessibilityDelegate;
private final boolean mIsRtl;
- private BubbleTextView mOriginalIcon;
+ protected BubbleTextView mOriginalIcon;
private final Rect mTempRect = new Rect();
private PointF mInterceptTouchDown = new PointF();
- private Point mIconLastTouchPos = new Point();
private boolean mIsLeftAligned;
- private boolean mIsAboveIcon;
+ protected boolean mIsAboveIcon;
private View mArrow;
- private Animator mOpenCloseAnimator;
+ protected Animator mOpenCloseAnimator;
private boolean mDeferContainerRemoval;
- public DeepShortcutsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
- mDeepShortcutsManager = DeepShortcutManager.getInstance(context);
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
+ // TODO: make sure the delegate works for all items, not just shortcuts.
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
mIsRtl = Utilities.isRtl(getResources());
}
- public DeepShortcutsContainer(Context context, AttributeSet attrs) {
+ public PopupContainerWithArrow(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public DeepShortcutsContainer(Context context) {
+ public PopupContainerWithArrow(Context context) {
this(context, null, 0);
}
- public void populateAndShow(final BubbleTextView originalIcon, final List<String> ids) {
+ public LauncherAccessibilityDelegate getAccessibilityDelegate() {
+ return mAccessibilityDelegate;
+ }
+
+ /**
+ * Shows the notifications and deep shortcuts associated with {@param icon}.
+ * @return the container if shown or null.
+ */
+ public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
+ Launcher launcher = Launcher.getLauncher(icon.getContext());
+ if (getOpen(launcher) != null) {
+ // There is already an items container open, so don't open this one.
+ icon.clearFocus();
+ return null;
+ }
+ ItemInfo itemInfo = (ItemInfo) icon.getTag();
+ List<String> shortcutIds = launcher.getPopupDataProvider().getShortcutIdsForItem(itemInfo);
+ if (shortcutIds.size() > 0) {
+ final PopupContainerWithArrow container =
+ (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
+ R.layout.popup_container, launcher.getDragLayer(), false);
+ container.setVisibility(View.INVISIBLE);
+ launcher.getDragLayer().addView(container);
+ container.populateAndShow(icon, shortcutIds);
+ return container;
+ }
+ return null;
+ }
+
+ public void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds) {
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);
@@ -128,25 +158,37 @@ public class DeepShortcutsContainer extends AbstractFloatingView
final int arrowVerticalOffset = resources.getDimensionPixelSize(
R.dimen.deep_shortcuts_arrow_vertical_offset);
- // Add dummy views first, and populate with real shortcut info when ready.
- final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
- final LayoutInflater inflater = mLauncher.getLayoutInflater();
- int numShortcuts = Math.min(ids.size(), ShortcutFilter.MAX_SHORTCUTS);
- for (int i = 0; i < numShortcuts; i++) {
- final DeepShortcutView shortcut =
- (DeepShortcutView) inflater.inflate(R.layout.deep_shortcut, this, false);
- if (i < numShortcuts - 1) {
- ((LayoutParams) shortcut.getLayoutParams()).bottomMargin = spacing;
- }
- shortcut.getBubbleText().setAccessibilityDelegate(mAccessibilityDelegate);
- addView(shortcut);
- }
- setContentDescription(getContext().getString(R.string.shortcuts_menu_description,
- numShortcuts, originalIcon.getContentDescription().toString()));
+ // Add dummy views first, and populate with real info when ready.
+ PopupPopulator.Item[] itemsToPopulate = PopupPopulator.getItemsToPopulate(shortcutIds);
+ addDummyViews(originalIcon, itemsToPopulate);
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
+ boolean reverseOrder = mIsAboveIcon;
+ if (reverseOrder) {
+ removeAllViews();
+ itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
+ addDummyViews(originalIcon, itemsToPopulate);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
+ }
+
+ List<DeepShortcutView> shortcutViews = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ View item = getChildAt(i);
+ switch (itemsToPopulate[i]) {
+ case SHORTCUT:
+ if (reverseOrder) {
+ shortcutViews.add(0, (DeepShortcutView) item);
+ } else {
+ shortcutViews.add((DeepShortcutView) item);
+ }
+ break;
+ }
+ }
+
// Add the arrow.
mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
mArrow.setPivotX(arrowWidth / 2);
@@ -155,66 +197,44 @@ public class DeepShortcutsContainer extends AbstractFloatingView
animateOpen();
mOriginalIcon = originalIcon;
+
mLauncher.getDragController().addDragListener(this);
// Load the shortcuts on a background thread and update the container as it animates.
final Looper workerLooper = LauncherModel.getWorkerLooper();
- final Handler uiHandler = new Handler(Looper.getMainLooper());
- final ItemInfo originalInfo = (ItemInfo) originalIcon.getTag();
- final UserHandle user = originalInfo.user;
- final ComponentName activity = originalInfo.getTargetComponent();
- new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
- @Override
- public void run() {
- final List<ShortcutInfoCompat> shortcuts = ShortcutFilter.sortAndFilterShortcuts(
- mDeepShortcutsManager.queryForShortcutsContainer(activity, ids, user));
- // We want the lowest rank to be closest to the user's finger.
- if (mIsAboveIcon) {
- Collections.reverse(shortcuts);
- }
- for (int i = 0; i < shortcuts.size(); i++) {
- final ShortcutInfoCompat shortcut = shortcuts.get(i);
- ShortcutInfo si = new ShortcutInfo(shortcut, mLauncher);
- // Use unbadged icon for the menu.
- si.iconBitmap = LauncherIcons.createShortcutIcon(
- shortcut, mLauncher, false /* badged */);
- uiHandler.post(new UpdateShortcutChild(i, si, shortcut));
- }
- }
- });
+ new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
+ mLauncher, (ItemInfo) originalIcon.getTag(), new Handler(Looper.getMainLooper()),
+ this, shortcutIds, shortcutViews));
}
- /** Updates the child of this container at the given index based on the given shortcut info. */
- private class UpdateShortcutChild implements Runnable {
- private final int mShortcutChildIndex;
- private final ShortcutInfo mShortcutChildInfo;
- private final ShortcutInfoCompat mDetail;
-
-
- public UpdateShortcutChild(int shortcutChildIndex, ShortcutInfo shortcutChildInfo,
- ShortcutInfoCompat detail) {
- mShortcutChildIndex = shortcutChildIndex;
- mShortcutChildInfo = shortcutChildInfo;
- mDetail = detail;
- }
-
- @Override
- public void run() {
- getShortcutAt(mShortcutChildIndex)
- .applyShortcutInfo(mShortcutChildInfo, mDetail, DeepShortcutsContainer.this);
+ private void addDummyViews(BubbleTextView originalIcon, PopupPopulator.Item[] itemsToPopulate) {
+ final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+ final LayoutInflater inflater = mLauncher.getLayoutInflater();
+ int numItems = itemsToPopulate.length;
+ for (int i = 0; i < numItems; i++) {
+ final PopupItemView item = (PopupItemView) inflater.inflate(
+ itemsToPopulate[i].layoutId, this, false);
+ if (i < numItems - 1) {
+ ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
+ }
+ item.setAccessibilityDelegate(mAccessibilityDelegate);
+ addView(item);
}
+ // TODO: update this, since not all items are shortcuts
+ setContentDescription(getContext().getString(R.string.shortcuts_menu_description,
+ numItems, originalIcon.getContentDescription().toString()));
}
- private DeepShortcutView getShortcutAt(int index) {
+ protected PopupItemView getItemViewAt(int index) {
if (!mIsAboveIcon) {
// Opening down, so arrow is the first view.
index++;
}
- return (DeepShortcutView) getChildAt(index);
+ return (PopupItemView) getChildAt(index);
}
- private int getShortcutCount() {
- // All children except the arrow are shortcuts.
+ protected int getItemCount() {
+ // All children except the arrow are items.
return getChildCount() - 1;
}
@@ -223,7 +243,7 @@ public class DeepShortcutsContainer extends AbstractFloatingView
mIsOpen = true;
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
- final int shortcutCount = getShortcutCount();
+ final int itemCount = getItemCount();
final long duration = getResources().getInteger(
R.integer.config_deepShortcutOpenDuration);
@@ -236,25 +256,25 @@ public class DeepShortcutsContainer extends AbstractFloatingView
// Animate shortcuts
DecelerateInterpolator interpolator = new DecelerateInterpolator();
- for (int i = 0; i < shortcutCount; i++) {
- final DeepShortcutView deepShortcutView = getShortcutAt(i);
- deepShortcutView.setVisibility(INVISIBLE);
- deepShortcutView.setAlpha(0);
+ for (int i = 0; i < itemCount; i++) {
+ final PopupItemView popupItemView = getItemViewAt(i);
+ popupItemView.setVisibility(INVISIBLE);
+ popupItemView.setAlpha(0);
- Animator anim = deepShortcutView.createOpenAnimation(mIsAboveIcon, mIsLeftAligned);
+ Animator anim = popupItemView.createOpenAnimation(mIsAboveIcon, mIsLeftAligned);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- deepShortcutView.setVisibility(VISIBLE);
+ popupItemView.setVisibility(VISIBLE);
}
});
anim.setDuration(duration);
- int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i;
+ int animationIndex = mIsAboveIcon ? itemCount - i - 1 : i;
anim.setStartDelay(stagger * animationIndex);
anim.setInterpolator(interpolator);
shortcutAnims.play(anim);
- Animator fadeAnim = new LauncherViewPropertyAnimator(deepShortcutView).alpha(1);
+ Animator fadeAnim = new LauncherViewPropertyAnimator(popupItemView).alpha(1);
fadeAnim.setInterpolator(fadeInterpolator);
// We want the shortcut to be fully opaque before the arrow starts animating.
fadeAnim.setDuration(arrowScaleDelay);
@@ -265,7 +285,7 @@ public class DeepShortcutsContainer extends AbstractFloatingView
public void onAnimationEnd(Animator animation) {
mOpenCloseAnimator = null;
Utilities.sendCustomAccessibilityEvent(
- DeepShortcutsContainer.this,
+ PopupContainerWithArrow.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.action_deep_shortcut));
}
@@ -405,7 +425,7 @@ public class DeepShortcutsContainer extends AbstractFloatingView
* 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);
+ LayoutParams layoutParams = new LayoutParams(width, height);
if (mIsLeftAligned) {
layoutParams.gravity = Gravity.LEFT;
layoutParams.leftMargin = horizontalOffset;
@@ -593,7 +613,7 @@ public class DeepShortcutsContainer extends AbstractFloatingView
}
}
- private void animateClose() {
+ protected void animateClose() {
if (!mIsOpen) {
return;
}
@@ -603,10 +623,10 @@ public class DeepShortcutsContainer extends AbstractFloatingView
mIsOpen = false;
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
- final int shortcutCount = getShortcutCount();
+ final int itemCount = getItemCount();
int numOpenShortcuts = 0;
- for (int i = 0; i < shortcutCount; i++) {
- if (getShortcutAt(i).isOpenOrOpening()) {
+ for (int i = 0; i < itemCount; i++) {
+ if (getItemViewAt(i).isOpenOrOpening()) {
numOpenShortcuts++;
}
}
@@ -618,13 +638,13 @@ public class DeepShortcutsContainer extends AbstractFloatingView
R.integer.config_deepShortcutCloseStagger);
final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
- int firstOpenShortcutIndex = mIsAboveIcon ? shortcutCount - numOpenShortcuts : 0;
- for (int i = firstOpenShortcutIndex; i < firstOpenShortcutIndex + numOpenShortcuts; i++) {
- final DeepShortcutView view = getShortcutAt(i);
+ int firstOpenItemIndex = mIsAboveIcon ? itemCount - numOpenShortcuts : 0;
+ for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) {
+ final PopupItemView view = getItemViewAt(i);
Animator anim;
if (view.willDrawIcon()) {
anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
- int animationIndex = mIsAboveIcon ? i - firstOpenShortcutIndex
+ int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
: numOpenShortcuts - i - 1;
anim.setStartDelay(stagger * animationIndex);
@@ -681,14 +701,10 @@ public class DeepShortcutsContainer extends AbstractFloatingView
shortcutAnims.start();
}
- public ShortcutMenuAccessibilityDelegate getAccessibilityDelegate() {
- return mAccessibilityDelegate;
- }
-
/**
* Closes the folder without animation.
*/
- private void closeComplete() {
+ protected void closeComplete() {
if (mOpenCloseAnimator != null) {
mOpenCloseAnimator.cancel();
mOpenCloseAnimator = null;
@@ -704,38 +720,13 @@ public class DeepShortcutsContainer extends AbstractFloatingView
@Override
protected boolean isOfType(int type) {
- return (type & TYPE_DEEPSHORTCUT_CONTAINER) != 0;
- }
-
- /**
- * 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());
- if (getOpen(launcher) != null) {
- // There is already a shortcuts container open, so don't open this one.
- icon.clearFocus();
- return null;
- }
- List<String> ids = launcher.getPopupDataProvider().getShortcutIdsForItem(
- (ItemInfo) icon.getTag());
- if (!ids.isEmpty()) {
- 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);
- return container;
- }
- return null;
+ return (type & TYPE_POPUP_CONTAINER_WITH_ARROW) != 0;
}
/**
* Returns a DeepShortcutsContainer which is already open or null
*/
- public static DeepShortcutsContainer getOpen(Launcher launcher) {
- return getOpenView(launcher, TYPE_DEEPSHORTCUT_CONTAINER);
+ public static PopupContainerWithArrow getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_POPUP_CONTAINER_WITH_ARROW);
}
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 4ed32b543..b671c364f 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -23,7 +23,7 @@ import android.util.Log;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.badging.NotificationListener;
+import com.android.launcher3.badge.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.MultiHashMap;
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
new file mode 100644
index 000000000..25d496a4b
--- /dev/null
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2017 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.popup;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.LogAccelerateInterpolator;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.util.PillWidthRevealOutlineProvider;
+
+/**
+ * An abstract {@link FrameLayout} that supports animating an item's content
+ * (e.g. icon and text) separate from the item's background.
+ */
+public abstract class PopupItemView extends FrameLayout
+ implements ValueAnimator.AnimatorUpdateListener {
+
+ protected static final Point sTempPoint = new Point();
+
+ private final Rect mPillRect;
+ private float mOpenAnimationProgress;
+
+ protected View mIconView;
+
+ public PopupItemView(Context context) {
+ this(context, null, 0);
+ }
+
+ public PopupItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PopupItemView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mPillRect = new Rect();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconView = findViewById(R.id.popup_item_icon);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ public boolean willDrawIcon() {
+ return true;
+ }
+
+ /**
+ * Creates an animator to play when the shortcut container is being opened.
+ */
+ public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
+ Point center = getIconCenter();
+ ValueAnimator openAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
+ mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
+ .createRevealAnimator(this, false);
+ mOpenAnimationProgress = 0f;
+ openAnimator.addUpdateListener(this);
+ return openAnimator;
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mOpenAnimationProgress = valueAnimator.getAnimatedFraction();
+ }
+
+ public boolean isOpenOrOpening() {
+ return mOpenAnimationProgress > 0;
+ }
+
+ /**
+ * Creates an animator to play when the shortcut container is being closed.
+ */
+ public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
+ long duration) {
+ Point center = getIconCenter();
+ ValueAnimator closeAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
+ mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
+ .createRevealAnimator(this, true);
+ // Scale down the duration and interpolator according to the progress
+ // that the open animation was at when the close started.
+ closeAnimator.setDuration((long) (duration * mOpenAnimationProgress));
+ closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress));
+ closeAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenAnimationProgress = 0;
+ }
+ });
+ return closeAnimator;
+ }
+
+ /**
+ * Creates an animator which clips the container to form a circle around the icon.
+ */
+ public Animator collapseToIcon() {
+ int halfHeight = getMeasuredHeight() / 2;
+ int iconCenterX = getIconCenter().x;
+ return new PillWidthRevealOutlineProvider(mPillRect,
+ iconCenterX - halfHeight, iconCenterX + halfHeight)
+ .createRevealAnimator(this, true);
+ }
+
+ /**
+ * Returns the position of the center of the icon relative to the container.
+ */
+ public Point getIconCenter() {
+ sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
+ if (Utilities.isRtl(getResources())) {
+ sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
+ }
+ return sTempPoint;
+ }
+
+ /**
+ * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
+ */
+ private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
+
+ private final View mTranslateView;
+ private final View mZoomView;
+
+ private final float mFullHeight;
+ private final float mTranslateYMultiplier;
+
+ private final boolean mPivotLeft;
+ private final float mTranslateX;
+
+ public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
+ View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
+ super(x, y, pillRect);
+ mTranslateView = translateView;
+ mZoomView = zoomView;
+ mFullHeight = pillRect.height();
+
+ mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
+
+ mPivotLeft = pivotLeft;
+ mTranslateX = pivotLeft ? pillRect.height() / 2 : pillRect.right - pillRect.height() / 2;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ super.setProgress(progress);
+
+ mZoomView.setScaleX(progress);
+ mZoomView.setScaleY(progress);
+
+ float height = mOutline.height();
+ mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
+
+ float pivotX = mPivotLeft ? (mOutline.left + height / 2) : (mOutline.right - height / 2);
+ mTranslateView.setTranslationX(mTranslateX - pivotX);
+ }
+ }
+
+ /**
+ * An interpolator that reverses the current open animation progress.
+ */
+ private static class CloseInterpolator extends LogAccelerateInterpolator {
+ private float mStartProgress;
+ private float mRemainingProgress;
+
+ /**
+ * @param openAnimationProgress The progress that the open interpolator ended at.
+ */
+ public CloseInterpolator(float openAnimationProgress) {
+ super(100, 0);
+ mStartProgress = 1f - openAnimationProgress;
+ mRemainingProgress = openAnimationProgress;
+ }
+
+ @Override
+ public float getInterpolation(float v) {
+ return mStartProgress + super.getInterpolation(v) * mRemainingProgress;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
new file mode 100644
index 000000000..b5a59b02b
--- /dev/null
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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.popup;
+
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Contains logic relevant to populating a {@link PopupContainerWithArrow}. In particular,
+ * this class determines which items appear in the container, and in what order.
+ */
+public class PopupPopulator {
+
+ public static final int MAX_ITEMS = 4;
+ @VisibleForTesting static final int NUM_DYNAMIC = 2;
+
+ public enum Item {
+ SHORTCUT(R.layout.deep_shortcut);
+
+ public final int layoutId;
+
+ Item(int layoutId) {
+ this.layoutId = layoutId;
+ }
+ }
+
+ public static Item[] getItemsToPopulate(List<String> shortcutIds) {
+ int numItems = Math.min(MAX_ITEMS, shortcutIds.size());
+ Item[] items = new Item[numItems];
+ for (int i = 0; i < numItems; i++) {
+ items[i] = Item.SHORTCUT;
+ }
+ return items;
+ }
+
+ public static Item[] reverseItems(Item[] items) {
+ if (items == null) return null;
+ int numItems = items.length;
+ Item[] reversedArray = new Item[numItems];
+ for (int i = 0; i < numItems; i++) {
+ reversedArray[i] = items[numItems - i - 1];
+ }
+ return reversedArray;
+ }
+
+ /**
+ * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
+ */
+ private static final Comparator<ShortcutInfoCompat> SHORTCUT_RANK_COMPARATOR
+ = new Comparator<ShortcutInfoCompat>() {
+ @Override
+ public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
+ if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
+ return -1;
+ }
+ if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
+ return 1;
+ }
+ return Integer.compare(a.getRank(), b.getRank());
+ }
+ };
+
+ /**
+ * Filters the shortcuts so that only MAX_ITEMS or fewer shortcuts are retained.
+ * We want the filter to include both static and dynamic shortcuts, so we always
+ * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
+ *
+ * @return a subset of shortcuts, in sorted order, with size <= MAX_ITEMS.
+ */
+ public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
+ List<ShortcutInfoCompat> shortcuts) {
+ Collections.sort(shortcuts, SHORTCUT_RANK_COMPARATOR);
+ if (shortcuts.size() <= MAX_ITEMS) {
+ return shortcuts;
+ }
+
+ // The list of shortcuts is now sorted with static shortcuts followed by dynamic
+ // shortcuts. We want to preserve this order, but only keep MAX_ITEMS.
+ List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_ITEMS);
+ int numDynamic = 0;
+ int size = shortcuts.size();
+ for (int i = 0; i < size; i++) {
+ ShortcutInfoCompat shortcut = shortcuts.get(i);
+ int filteredSize = filteredShortcuts.size();
+ if (filteredSize < MAX_ITEMS) {
+ // Always add the first MAX_ITEMS to the filtered list.
+ filteredShortcuts.add(shortcut);
+ if (shortcut.isDynamic()) {
+ numDynamic++;
+ }
+ continue;
+ }
+ // At this point, we have MAX_ITEMS already, but they may all be static.
+ // If there are dynamic shortcuts, remove static shortcuts to add them.
+ if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
+ numDynamic++;
+ int lastStaticIndex = filteredSize - numDynamic;
+ filteredShortcuts.remove(lastStaticIndex);
+ filteredShortcuts.add(shortcut);
+ }
+ }
+ return filteredShortcuts;
+ }
+
+ public static Runnable createUpdateRunnable(final Launcher launcher, ItemInfo originalInfo,
+ final Handler uiHandler, final PopupContainerWithArrow container,
+ final List<String> shortcutIds, final List<DeepShortcutView> shortcutViews) {
+ final ComponentName activity = originalInfo.getTargetComponent();
+ final UserHandle user = originalInfo.user;
+ return new Runnable() {
+ @Override
+ public void run() {
+ final List<ShortcutInfoCompat> shortcuts = PopupPopulator.sortAndFilterShortcuts(
+ DeepShortcutManager.getInstance(launcher).queryForShortcutsContainer(
+ activity, shortcutIds, user));
+ for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
+ final ShortcutInfoCompat shortcut = shortcuts.get(i);
+ ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
+ // Use unbadged icon for the menu.
+ si.iconBitmap = LauncherIcons.createShortcutIcon(
+ shortcut, launcher, false /* badged */);
+ uiHandler.post(new UpdateShortcutChild(container, shortcutViews.get(i),
+ si, shortcut));
+ }
+ }
+ };
+ }
+
+ /** Updates the child of this container at the given index based on the given shortcut info. */
+ private static class UpdateShortcutChild implements Runnable {
+ private final PopupContainerWithArrow mContainer;
+ private final DeepShortcutView mShortcutChild;
+ private final ShortcutInfo mShortcutChildInfo;
+ private final ShortcutInfoCompat mDetail;
+
+ public UpdateShortcutChild(PopupContainerWithArrow container, DeepShortcutView shortcutChild,
+ ShortcutInfo shortcutChildInfo, ShortcutInfoCompat detail) {
+ mContainer = container;
+ mShortcutChild = shortcutChild;
+ mShortcutChildInfo = shortcutChildInfo;
+ mDetail = detail;
+ }
+
+ @Override
+ public void run() {
+ mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail, mContainer);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 6e9810007..2f07c9ac9 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -16,37 +16,27 @@
package com.android.launcher3.shortcuts;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.FrameLayout;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PillRevealOutlineProvider;
-import com.android.launcher3.util.PillWidthRevealOutlineProvider;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupItemView;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
* This lets us animate the DeepShortcutView (icon and text) separately from the background.
*/
-public class DeepShortcutView extends FrameLayout implements ValueAnimator.AnimatorUpdateListener {
-
- private static final Point sTempPoint = new Point();
+public class DeepShortcutView extends PopupItemView {
private final Rect mPillRect;
private DeepShortcutTextView mBubbleText;
- private View mIconView;
- private float mOpenAnimationProgress;
private ShortcutInfo mInfo;
private ShortcutInfoCompat mDetail;
@@ -68,7 +58,6 @@ public class DeepShortcutView extends FrameLayout implements ValueAnimator.Anima
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mIconView = findViewById(R.id.deep_shortcut_icon);
mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut);
}
@@ -91,8 +80,8 @@ public class DeepShortcutView extends FrameLayout implements ValueAnimator.Anima
}
/** package private **/
- void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
- DeepShortcutsContainer container) {
+ public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
+ PopupContainerWithArrow container) {
mInfo = info;
mDetail = detail;
mBubbleText.applyFromShortcutInfo(info);
@@ -127,128 +116,4 @@ public class DeepShortcutView extends FrameLayout implements ValueAnimator.Anima
public View getIconView() {
return mIconView;
}
-
- /**
- * Creates an animator to play when the shortcut container is being opened.
- */
- public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
- Point center = getIconCenter();
- ValueAnimator openAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
- mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
- .createRevealAnimator(this, false);
- mOpenAnimationProgress = 0f;
- openAnimator.addUpdateListener(this);
- return openAnimator;
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mOpenAnimationProgress = valueAnimator.getAnimatedFraction();
- }
-
- public boolean isOpenOrOpening() {
- return mOpenAnimationProgress > 0;
- }
-
- /**
- * Creates an animator to play when the shortcut container is being closed.
- */
- public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
- long duration) {
- Point center = getIconCenter();
- ValueAnimator closeAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
- mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
- .createRevealAnimator(this, true);
- // Scale down the duration and interpolator according to the progress
- // that the open animation was at when the close started.
- closeAnimator.setDuration((long) (duration * mOpenAnimationProgress));
- closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress));
- return closeAnimator;
- }
-
- /**
- * Creates an animator which clips the container to form a circle around the icon.
- */
- public Animator collapseToIcon() {
- int halfHeight = getMeasuredHeight() / 2;
- int iconCenterX = getIconCenter().x;
- return new PillWidthRevealOutlineProvider(mPillRect,
- iconCenterX - halfHeight, iconCenterX + halfHeight)
- .createRevealAnimator(this, true);
- }
-
- /**
- * Returns the position of the center of the icon relative to the container.
- */
- public Point getIconCenter() {
- sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
- if (Utilities.isRtl(getResources())) {
- sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
- }
- return sTempPoint;
- }
-
- /**
- * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
- */
- private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
-
- private final View mTranslateView;
- private final View mZoomView;
-
- private final float mFullHeight;
- private final float mTranslateYMultiplier;
-
- private final boolean mPivotLeft;
- private final float mTranslateX;
-
- public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
- View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
- super(x, y, pillRect);
- mTranslateView = translateView;
- mZoomView = zoomView;
- mFullHeight = pillRect.height();
-
- mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
-
- mPivotLeft = pivotLeft;
- mTranslateX = pivotLeft ? pillRect.height() / 2 : pillRect.right - pillRect.height() / 2;
- }
-
- @Override
- public void setProgress(float progress) {
- super.setProgress(progress);
-
- mZoomView.setScaleX(progress);
- mZoomView.setScaleY(progress);
-
- float height = mOutline.height();
- mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
-
- float pivotX = mPivotLeft ? (mOutline.left + height / 2) : (mOutline.right - height / 2);
- mTranslateView.setTranslationX(mTranslateX - pivotX);
- }
- }
-
- /**
- * An interpolator that reverses the current open animation progress.
- */
- private static class CloseInterpolator extends LogAccelerateInterpolator {
- private float mStartProgress;
- private float mRemainingProgress;
-
- /**
- * @param openAnimationProgress The progress that the open interpolator ended at.
- */
- public CloseInterpolator(float openAnimationProgress) {
- super(100, 0);
- mStartProgress = 1f - openAnimationProgress;
- mRemainingProgress = openAnimationProgress;
- }
-
- @Override
- public float getInterpolation(float v) {
- return mStartProgress + super.getInterpolation(v) * mRemainingProgress;
- }
- }
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutFilter.java b/src/com/android/launcher3/shortcuts/ShortcutFilter.java
deleted file mode 100644
index ec6881730..000000000
--- a/src/com/android/launcher3/shortcuts/ShortcutFilter.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.shortcuts;
-
-import android.support.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Sorts and filters shortcuts.
- */
-public class ShortcutFilter {
-
- public static final int MAX_SHORTCUTS = 4;
- @VisibleForTesting static final int NUM_DYNAMIC = 2;
-
- /**
- * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
- */
- private static final Comparator<ShortcutInfoCompat> RANK_COMPARATOR
- = new Comparator<ShortcutInfoCompat>() {
- @Override
- public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
- if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
- return -1;
- }
- if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
- return 1;
- }
- return Integer.compare(a.getRank(), b.getRank());
- }
- };
-
- /**
- * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
- * We want the filter to include both static and dynamic shortcuts, so we always
- * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
- *
- * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
- */
- public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
- List<ShortcutInfoCompat> shortcuts) {
- Collections.sort(shortcuts, RANK_COMPARATOR);
- if (shortcuts.size() <= MAX_SHORTCUTS) {
- return shortcuts;
- }
-
- // The list of shortcuts is now sorted with static shortcuts followed by dynamic
- // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
- List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
- int numDynamic = 0;
- int size = shortcuts.size();
- for (int i = 0; i < size; i++) {
- ShortcutInfoCompat shortcut = shortcuts.get(i);
- int filteredSize = filteredShortcuts.size();
- if (filteredSize < MAX_SHORTCUTS) {
- // Always add the first MAX_SHORTCUTS to the filtered list.
- filteredShortcuts.add(shortcut);
- if (shortcut.isDynamic()) {
- numDynamic++;
- }
- continue;
- }
- // At this point, we have MAX_SHORTCUTS already, but they may all be static.
- // If there are dynamic shortcuts, remove static shortcuts to add them.
- if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
- numDynamic++;
- int lastStaticIndex = filteredSize - numDynamic;
- filteredShortcuts.remove(lastStaticIndex);
- filteredShortcuts.add(shortcut);
- }
- }
- return filteredShortcuts;
- }
-}
diff --git a/tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 05d0ffb8c..0843d9b59 100644
--- a/tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.launcher3.shortcuts;
+package com.android.launcher3.popup;
import android.content.pm.ShortcutInfo;
import android.support.test.runner.AndroidJUnit4;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,40 +28,41 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import static com.android.launcher3.shortcuts.ShortcutFilter.MAX_SHORTCUTS;
-import static com.android.launcher3.shortcuts.ShortcutFilter.NUM_DYNAMIC;
+import static com.android.launcher3.popup.PopupPopulator.MAX_ITEMS;
+import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
- * Tests the sorting and filtering of shortcuts in {@link ShortcutFilter}.
+ * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
*/
@RunWith(AndroidJUnit4.class)
-public class ShortcutFilterTest {
+public class PopupPopulatorTest {
@Test
public void testSortAndFilterShortcuts() {
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_ITEMS, 0);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_ITEMS);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
- MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
- MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
+ MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_ITEMS - 1, 1);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_ITEMS - 1);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
- MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
- MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
}
private void filterShortcutsAndAssertNumStaticAndDynamic(
List<ShortcutInfoCompat> shortcuts, int expectedStatic, int expectedDynamic) {
Collections.shuffle(shortcuts);
- List<ShortcutInfoCompat> filteredShortcuts = ShortcutFilter.sortAndFilterShortcuts(shortcuts);
+ List<ShortcutInfoCompat> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
+ shortcuts);
assertIsSorted(filteredShortcuts);
int numStatic = 0;