diff options
Diffstat (limited to 'src')
15 files changed, 543 insertions, 393 deletions
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; - } -} |