summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2017-10-09 14:56:21 -0700
committerSunny Goyal <sunnygoyal@google.com>2017-10-10 10:28:05 -0700
commit10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd (patch)
treee7584efee3c997b6d4f127f910e06c00b303bd16 /src/com/android/launcher3
parent271e219ea3423ada57a55b54395beeef64fb401c (diff)
downloadandroid_packages_apps_Trebuchet-10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd.tar.gz
android_packages_apps_Trebuchet-10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd.tar.bz2
android_packages_apps_Trebuchet-10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd.zip
Converting PopupContainerWithArrow into a base class so that it is easier
to create other types of popup Bug: 67585158 Change-Id: I966ae7bb90f941951b26feaf71b3ea30c3f3c0cc
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r--src/com/android/launcher3/AbstractFloatingView.java6
-rw-r--r--src/com/android/launcher3/Launcher.java10
-rw-r--r--src/com/android/launcher3/keyboard/CustomActionsPopup.java4
-rw-r--r--src/com/android/launcher3/notification/NotificationFooterLayout.java10
-rw-r--r--src/com/android/launcher3/notification/NotificationInfo.java3
-rw-r--r--src/com/android/launcher3/popup/BaseActionPopup.java599
-rw-r--r--src/com/android/launcher3/popup/PopupContainerWithArrow.java564
-rw-r--r--src/com/android/launcher3/popup/PopupDataProvider.java15
-rw-r--r--src/com/android/launcher3/popup/PopupItemView.java2
9 files changed, 701 insertions, 512 deletions
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 49968189b..0fbad522e 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -38,14 +38,14 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
@IntDef(flag = true, value = {
TYPE_FOLDER,
- TYPE_POPUP_CONTAINER_WITH_ARROW,
+ TYPE_ACTION_POPUP,
TYPE_WIDGETS_BOTTOM_SHEET,
TYPE_WIDGET_RESIZE_FRAME
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
public static final int TYPE_FOLDER = 1 << 0;
- public static final int TYPE_POPUP_CONTAINER_WITH_ARROW = 1 << 1;
+ public static final int TYPE_ACTION_POPUP = 1 << 1;
public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
@@ -138,7 +138,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
}
public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_FOLDER | TYPE_POPUP_CONTAINER_WITH_ARROW
+ return getOpenView(launcher, TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME);
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2945b2294..1bb4807b5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -87,7 +87,6 @@ import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
@@ -110,6 +109,7 @@ import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.popup.BaseActionPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -1347,9 +1347,9 @@ public class Launcher extends BaseActivity
mWorkspace.updateIconBadges(updatedBadges);
mAppsView.updateIconBadges(updatedBadges);
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
- if (popup != null) {
- popup.updateNotificationHeader(updatedBadges);
+ BaseActionPopup popup = BaseActionPopup.getOpen(Launcher.this);
+ if (popup instanceof PopupContainerWithArrow) {
+ ((PopupContainerWithArrow) popup).updateNotificationHeader(updatedBadges);
}
}
};
@@ -3558,7 +3558,7 @@ public class Launcher extends BaseActivity
&& mAccessibilityDelegate.performAction(focusedView,
(ItemInfo) focusedView.getTag(),
LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
- PopupContainerWithArrow.getOpen(this).requestFocus();
+ BaseActionPopup.getOpen(this).requestFocus();
return true;
}
break;
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 938955ca2..150522ef6 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -27,7 +27,7 @@ import android.widget.PopupMenu.OnMenuItemClickListener;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.BaseActionPopup;
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;
- PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
+ BaseActionPopup container = BaseActionPopup.getOpen(launcher);
if (container != null) {
mDelegate = container.getAccessibilityDelegate();
} else {
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index ad07d37bd..3cf3ff62c 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -37,6 +37,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.popup.BaseActionPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import java.util.ArrayList;
@@ -193,16 +194,17 @@ public class NotificationFooterLayout extends FrameLayout {
private void removeViewFromIconRow(View child) {
mIconRow.removeView(child);
- mNotifications.remove((NotificationInfo) child.getTag());
+ mNotifications.remove(child.getTag());
updateOverflowEllipsisVisibility();
if (mIconRow.getChildCount() == 0) {
// There are no more icons in the footer, so hide it.
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
+ BaseActionPopup popup = BaseActionPopup.getOpen(
Launcher.getLauncher(getContext()));
- if (popup != null) {
+ if (popup instanceof PopupContainerWithArrow) {
final int newHeight = getResources().getDimensionPixelSize(
R.dimen.notification_empty_footer_height);
- Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight,
+ Animator collapseFooter = ((PopupContainerWithArrow) popup)
+ .reduceNotificationViewHeight(getHeight() - newHeight,
getResources().getInteger(R.integer.config_removeNotificationViewDuration));
collapseFooter.addListener(new AnimatorListenerAdapter() {
@Override
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 6e36f4f51..8ef10e356 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -31,7 +31,6 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.util.PackageUserKey;
/**
@@ -110,7 +109,7 @@ public class NotificationInfo implements View.OnClickListener {
launcher.getPopupDataProvider().cancelNotification(notificationKey);
}
AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView
- .TYPE_POPUP_CONTAINER_WITH_ARROW);
+ .TYPE_ACTION_POPUP);
}
public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/popup/BaseActionPopup.java b/src/com/android/launcher3/popup/BaseActionPopup.java
new file mode 100644
index 000000000..7ffe2ef8b
--- /dev/null
+++ b/src/com/android/launcher3/popup/BaseActionPopup.java
@@ -0,0 +1,599 @@
+/*
+ * 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 static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.notification.NotificationItemView;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutsItemView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Base popup container for showing shortcuts to deep links within apps.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class BaseActionPopup<V extends TextView> extends AbstractFloatingView {
+
+ public static final int ROUNDED_TOP_CORNERS = 1 << 0;
+ public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
+
+ @IntDef(flag = true, value = {
+ ROUNDED_TOP_CORNERS,
+ ROUNDED_BOTTOM_CORNERS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoundedCornerFlags {}
+
+ protected final Launcher mLauncher;
+ protected final LauncherAccessibilityDelegate mAccessibilityDelegate;
+ private final boolean mIsRtl;
+
+ public ShortcutsItemView mShortcutsItemView;
+
+ protected V mOriginalIcon;
+ private final Rect mTempRect = new Rect();
+ private PointF mInterceptTouchDown = new PointF();
+ private boolean mIsLeftAligned;
+ protected boolean mIsAboveIcon;
+ protected View mArrow;
+ private int mGravity;
+
+ protected Animator mOpenCloseAnimator;
+ protected boolean mDeferContainerRemoval;
+ private final Rect mStartRect = new Rect();
+ private final Rect mEndRect = new Rect();
+
+ public BaseActionPopup(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
+ mIsRtl = Utilities.isRtl(getResources());
+ }
+
+ public BaseActionPopup(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BaseActionPopup(Context context) {
+ this(context, null, 0);
+ }
+
+ public LauncherAccessibilityDelegate getAccessibilityDelegate() {
+ return mAccessibilityDelegate;
+ }
+
+ protected PopupItemView getItemViewAt(int index) {
+ if (!mIsAboveIcon) {
+ // Opening down, so arrow is the first view.
+ index++;
+ }
+ return (PopupItemView) getChildAt(index);
+ }
+
+ protected int getItemCount() {
+ // All children except the arrow are items.
+ return getChildCount() - 1;
+ }
+
+ protected void animateOpen() {
+ setVisibility(View.VISIBLE);
+ mIsOpen = true;
+
+ final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = getResources();
+ final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal.
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
+ }
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
+ (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
+ revealAnim.setDuration(revealDuration);
+ revealAnim.setInterpolator(revealInterpolator);
+
+ Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
+ fadeIn.setDuration(revealDuration);
+ fadeIn.setInterpolator(revealInterpolator);
+ openAnim.play(fadeIn);
+
+ // Animate the arrow.
+ mArrow.setScaleX(0);
+ mArrow.setScaleY(0);
+ Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
+ R.integer.config_popupArrowOpenDuration));
+
+ openAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ Utilities.sendCustomAccessibilityEvent(
+ BaseActionPopup.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.action_deep_shortcut));
+ }
+ });
+
+ mOpenCloseAnimator = openAnim;
+ openAnim.playSequentially(revealAnim, arrowScale);
+ openAnim.start();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ enforceContainedWithinScreen(l, r);
+ }
+
+ private void enforceContainedWithinScreen(int left, int right) {
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ if (getTranslationX() + left < 0 ||
+ getTranslationX() + right > dragLayer.getWidth()) {
+ // If we are still off screen, center horizontally too.
+ mGravity |= Gravity.CENTER_HORIZONTAL;
+ }
+
+ if (Gravity.isHorizontal(mGravity)) {
+ setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
+ }
+ if (Gravity.isVertical(mGravity)) {
+ setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
+ }
+ }
+
+ /**
+ * Returns the point at which the center of the arrow merges with the first popup item.
+ */
+ private Point computeAnimStartPoint(int itemsTotalHeight) {
+ int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+ R.dimen.popup_arrow_horizontal_center_start:
+ R.dimen.popup_arrow_horizontal_center_end);
+ if (!mIsLeftAligned) {
+ arrowCenterX = getMeasuredWidth() - arrowCenterX;
+ }
+ int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
+ - itemsTotalHeight;
+ // The y-coordinate of edge between the arrow and the first popup item.
+ int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
+ return new Point(arrowCenterX, arrowEdge);
+ }
+
+ /**
+ * Orients this container above or below the given icon, aligning with the left or right.
+ *
+ * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+ * - Above and left-aligned
+ * - Above and right-aligned
+ * - Below and left-aligned
+ * - Below and right-aligned
+ *
+ * So we always align left if there is enough horizontal space
+ * and align above if there is enough vertical space.
+ */
+ protected void orientAboutIcon(int arrowHeight) {
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight() + arrowHeight;
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ dragLayer.getDescendantRectRelativeToSelf(mOriginalIcon, mTempRect);
+ Rect insets = dragLayer.getInsets();
+
+ // Align left (right in RTL) if there is room.
+ int leftAlignedX = mTempRect.left + mOriginalIcon.getPaddingLeft();
+ int rightAlignedX = mTempRect.right - width - mOriginalIcon.getPaddingRight();
+ int x = leftAlignedX;
+ boolean canBeLeftAligned = leftAlignedX + width + insets.left
+ < dragLayer.getRight() - insets.right;
+ boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
+ if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
+ x = rightAlignedX;
+ }
+ mIsLeftAligned = x == leftAlignedX;
+ if (mIsRtl) {
+ x -= dragLayer.getWidth() - width;
+ }
+
+ // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
+ int iconWidth = mOriginalIcon.getWidth()
+ - mOriginalIcon.getTotalPaddingLeft() - mOriginalIcon.getTotalPaddingRight();
+ iconWidth *= mOriginalIcon.getScaleX();
+ Resources resources = getResources();
+ int xOffset;
+ if (isAlignedWithStart()) {
+ // Aligning with the shortcut icon.
+ int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
+ int shortcutPaddingStart = resources.getDimensionPixelSize(
+ R.dimen.popup_padding_start);
+ xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
+ } else {
+ // Aligning with the drag handle.
+ int shortcutDragHandleWidth = resources.getDimensionPixelSize(
+ R.dimen.deep_shortcut_drag_handle_size);
+ int shortcutPaddingEnd = resources.getDimensionPixelSize(
+ R.dimen.popup_padding_end);
+ xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
+ }
+ x += mIsLeftAligned ? xOffset : -xOffset;
+
+ // Open above icon if there is room.
+ int iconHeight = getIconHeightForPopupPlacement();
+ int y = mTempRect.top + mOriginalIcon.getPaddingTop() - height;
+ mIsAboveIcon = y > dragLayer.getTop() + insets.top;
+ if (!mIsAboveIcon) {
+ y = mTempRect.top + mOriginalIcon.getPaddingTop() + iconHeight;
+ }
+
+ // Insets are added later, so subtract them now.
+ if (mIsRtl) {
+ x += insets.right;
+ } else {
+ x -= insets.left;
+ }
+ y -= insets.top;
+
+ mGravity = 0;
+ if (y + height > dragLayer.getBottom() - insets.bottom) {
+ // The container is opening off the screen, so just center it in the drag layer instead.
+ mGravity = Gravity.CENTER_VERTICAL;
+ // Put the container next to the icon, preferring the right side in ltr (left in rtl).
+ int rightSide = leftAlignedX + iconWidth - insets.left;
+ int leftSide = rightAlignedX - iconWidth - insets.left;
+ if (!mIsRtl) {
+ if (rightSide + width < dragLayer.getRight()) {
+ x = rightSide;
+ mIsLeftAligned = true;
+ } else {
+ x = leftSide;
+ mIsLeftAligned = false;
+ }
+ } else {
+ if (leftSide > dragLayer.getLeft()) {
+ x = leftSide;
+ mIsLeftAligned = false;
+ } else {
+ x = rightSide;
+ mIsLeftAligned = true;
+ }
+ }
+ mIsAboveIcon = true;
+ }
+
+ setX(x);
+ setY(y);
+ }
+
+ protected int getIconHeightForPopupPlacement() {
+ return mOriginalIcon.getHeight();
+ }
+
+ protected boolean isAlignedWithStart() {
+ return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+ }
+
+ /**
+ * Adds an arrow view pointing at the original icon.
+ * @param horizontalOffset the horizontal offset of the arrow, so that it
+ * points at the center of the original icon
+ */
+ protected View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
+ LayoutParams layoutParams = new LayoutParams(width, height);
+ if (mIsLeftAligned) {
+ layoutParams.gravity = Gravity.LEFT;
+ layoutParams.leftMargin = horizontalOffset;
+ } else {
+ layoutParams.gravity = Gravity.RIGHT;
+ layoutParams.rightMargin = horizontalOffset;
+ }
+ if (mIsAboveIcon) {
+ layoutParams.topMargin = verticalOffset;
+ } else {
+ layoutParams.bottomMargin = verticalOffset;
+ }
+
+ View arrowView = new View(getContext());
+ if (Gravity.isVertical(mGravity)) {
+ // This is only true if there wasn't room for the container next to the icon,
+ // so we centered it instead. In that case we don't want to show the arrow.
+ arrowView.setVisibility(INVISIBLE);
+ } else {
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ width, height, !mIsAboveIcon));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
+ // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+ int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+ arrowPaint.setPathEffect(new CornerPathEffect(radius));
+ arrowView.setBackground(arrowDrawable);
+ arrowView.setElevation(getElevation());
+ }
+ addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
+ return arrowView;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mInterceptTouchDown.set(ev.getX(), ev.getY());
+ return false;
+ }
+ // Stop sending touch events to deep shortcut views if user moved beyond touch slop.
+ return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
+ > ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ protected ObjectAnimator createArrowScaleAnim(float scale) {
+ return LauncherAnimUtils.ofPropertyValuesHolder(
+ mArrow, new PropertyListBuilder().scale(scale).build());
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ protected void animateClose() {
+ if (!mIsOpen) {
+ return;
+ }
+ mEndRect.setEmpty();
+ if (mOpenCloseAnimator != null) {
+ Outline outline = new Outline();
+ getOutlineProvider().getOutline(this, outline);
+ outline.getRect(mEndRect);
+ mOpenCloseAnimator.cancel();
+ }
+ mIsOpen = false;
+
+ final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+ prepareCloseAnimator(closeAnim);
+
+ closeAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ if (mDeferContainerRemoval) {
+ setVisibility(INVISIBLE);
+ } else {
+ closeComplete();
+ }
+ }
+ });
+ mOpenCloseAnimator = closeAnim;
+ closeAnim.start();
+ }
+
+ protected void prepareCloseAnimator(AnimatorSet closeAnim) {
+ final Resources res = getResources();
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal (reversed).
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
+ }
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ if (mEndRect.isEmpty()) {
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+ }
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
+ radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
+ revealAnim.setInterpolator(revealInterpolator);
+ closeAnim.play(revealAnim);
+
+ Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ fadeOut.setInterpolator(revealInterpolator);
+ closeAnim.play(fadeOut);
+ closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+ }
+
+ /**
+ * Closes the folder without animation.
+ */
+ protected void closeComplete() {
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ mOpenCloseAnimator = null;
+ }
+ mIsOpen = false;
+ mDeferContainerRemoval = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ACTION_POPUP) != 0;
+ }
+
+ /**
+ * Returns a DeepShortcutsContainer which is already open or null
+ */
+ public static BaseActionPopup getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_ACTION_POPUP);
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ mLauncher.getUserEventDispatcher().logActionCommand(
+ command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ DragLayer dl = mLauncher.getDragLayer();
+ if (!dl.isEventOverView(this, ev)) {
+ mLauncher.getUserEventDispatcher().logActionTapOutside(
+ LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
+ close(true);
+
+ // We let touches on the original icon go through so that users can launch
+ // the app with one tap if they don't find a shortcut they want.
+ return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev);
+ }
+ }
+ return false;
+ }
+
+ public void populateAndShow(V originalIcon, PopupPopulator.Item[] itemsToPopulate) {
+ setVisibility(View.INVISIBLE);
+ mLauncher.getDragLayer().addView(this);
+
+ final Resources resources = getResources();
+ final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
+ final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
+ final int arrowVerticalOffset = resources.getDimensionPixelSize(
+ R.dimen.popup_arrow_vertical_offset);
+
+ mOriginalIcon = originalIcon;
+
+ // Add dummy views first, and populate with real info when ready.
+ addDummyViews(itemsToPopulate);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ orientAboutIcon(arrowHeight + arrowVerticalOffset);
+
+ boolean reverseOrder = mIsAboveIcon;
+ if (reverseOrder) {
+ removeAllViews();
+ mShortcutsItemView = null;
+ itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
+ addDummyViews(itemsToPopulate);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ orientAboutIcon(arrowHeight + arrowVerticalOffset);
+ }
+
+ // Add the arrow.
+ final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ?
+ R.dimen.popup_arrow_horizontal_offset_start :
+ R.dimen.popup_arrow_horizontal_offset_end);
+ mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
+ mArrow.setPivotX(arrowWidth / 2);
+ mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
+
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ animateOpen();
+ }
+
+ protected void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate) {
+ final LayoutInflater inflater = mLauncher.getLayoutInflater();
+ int shortcutsItemRoundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+ int numItems = itemTypesToPopulate.length;
+ for (int i = 0; i < numItems; i++) {
+ PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+ PopupPopulator.Item prevItemTypeToPopulate =
+ i > 0 ? itemTypesToPopulate[i - 1] : null;
+ PopupPopulator.Item nextItemTypeToPopulate =
+ i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
+ final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
+
+ boolean shouldUnroundTopCorners = prevItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ prevItemTypeToPopulate.isShortcut;
+ boolean shouldUnroundBottomCorners = nextItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
+
+ onViewInflated(item, itemTypeToPopulate,
+ shouldUnroundTopCorners, shouldUnroundBottomCorners);
+
+ if (itemTypeToPopulate.isShortcut) {
+ if (mShortcutsItemView == null) {
+ mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
+ R.layout.shortcuts_item, this, false);
+ addView(mShortcutsItemView);
+ if (shouldUnroundTopCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_TOP_CORNERS;
+ }
+ }
+ mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
+ if (shouldUnroundBottomCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+ }
+ } else {
+ addView(item);
+ }
+ }
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+ mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
+ }
+
+ protected void onViewInflated(View view, PopupPopulator.Item itemType,
+ boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) {
+
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 5c49b4bdf..68b547d88 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -25,32 +25,17 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.CornerPathEffect;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.support.annotation.IntDef;
import android.util.AttributeSet;
-import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -59,81 +44,42 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
-import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
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.graphics.IconPalette;
-import com.android.launcher3.graphics.TriangleShape;
-import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.popup.PopupPopulator.Item;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutsItemView;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Themes;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
- * A container for shortcuts to deep links within apps.
+ * A container for shortcuts to deep links and notifications associated with an app.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
+public class PopupContainerWithArrow extends BaseActionPopup<BubbleTextView> implements DragSource,
DragController.DragListener {
- public static final int ROUNDED_TOP_CORNERS = 1 << 0;
- public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
-
- @IntDef(flag = true, value = {
- ROUNDED_TOP_CORNERS,
- ROUNDED_BOTTOM_CORNERS
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RoundedCornerFlags {}
-
- protected final Launcher mLauncher;
private final int mStartDragThreshold;
- private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private final boolean mIsRtl;
- public ShortcutsItemView mShortcutsItemView;
private NotificationItemView mNotificationItemView;
-
- protected BubbleTextView mOriginalIcon;
- private final Rect mTempRect = new Rect();
- private PointF mInterceptTouchDown = new PointF();
- private boolean mIsLeftAligned;
- protected boolean mIsAboveIcon;
- private View mArrow;
- private int mGravity;
-
- protected Animator mOpenCloseAnimator;
- private boolean mDeferContainerRemoval;
private AnimatorSet mReduceHeightAnimatorSet;
- private final Rect mStartRect = new Rect();
- private final Rect mEndRect = new Rect();
+ private int mNumNotifications;
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
-
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
- mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
- mIsRtl = Utilities.isRtl(getResources());
}
public PopupContainerWithArrow(Context context, AttributeSet attrs) {
@@ -144,10 +90,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
this(context, null, 0);
}
- public LauncherAccessibilityDelegate getAccessibilityDelegate() {
- return mAccessibilityDelegate;
- }
-
/**
* Shows the notifications and deep shortcuts associated with {@param icon}.
* @return the container if shown or null.
@@ -174,49 +116,24 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
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, notificationKeys, systemShortcuts);
return container;
}
- public void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
+ private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
- final Resources resources = getResources();
- final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
- final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
- final int arrowVerticalOffset = resources.getDimensionPixelSize(
- R.dimen.popup_arrow_vertical_offset);
-
- mOriginalIcon = originalIcon;
-
- // Add dummy views first, and populate with real info when ready.
+ mNumNotifications = notificationKeys.size();
PopupPopulator.Item[] itemsToPopulate = PopupPopulator
.getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
- addDummyViews(itemsToPopulate, notificationKeys.size());
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
-
- boolean reverseOrder = mIsAboveIcon;
- if (reverseOrder) {
- removeAllViews();
- mNotificationItemView = null;
- mShortcutsItemView = null;
- itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
- addDummyViews(itemsToPopulate, notificationKeys.size());
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
- }
+ populateAndShow(originalIcon, itemsToPopulate);
ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
- : mShortcutsItemView.getDeepShortcutViews(reverseOrder);
+ : mShortcutsItemView.getDeepShortcutViews(mIsAboveIcon);
List<View> systemShortcutViews = mShortcutsItemView == null
? Collections.EMPTY_LIST
- : mShortcutsItemView.getSystemShortcutViews(reverseOrder);
+ : mShortcutsItemView.getSystemShortcutViews(mIsAboveIcon);
if (mNotificationItemView != null) {
updateNotificationHeader();
}
@@ -232,17 +149,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
numNotifications, originalIcon.getContentDescription().toString()));
}
- // Add the arrow.
- final int arrowHorizontalOffset = resources.getDimensionPixelSize(isAlignedWithStart() ?
- R.dimen.popup_arrow_horizontal_offset_start :
- R.dimen.popup_arrow_horizontal_offset_end);
- mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
- mArrow.setPivotX(arrowWidth / 2);
- mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
-
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- animateOpen();
-
mLauncher.getDragController().addDragListener(this);
mOriginalIcon.forceHideBadge(true);
@@ -254,6 +160,60 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
systemShortcuts, systemShortcutViews));
}
+ @Override
+ protected void addDummyViews(Item[] itemTypesToPopulate) {
+ mNotificationItemView = null;
+ super.addDummyViews(itemTypesToPopulate);
+ if (mNumNotifications > 0) {
+ mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
+ }
+ }
+
+ @Override
+ protected void onViewInflated(View view, Item itemType,
+ boolean shouldUnroundTopCorners, boolean shouldUnroundBottomCorners) {
+ if (itemType == PopupPopulator.Item.NOTIFICATION) {
+ mNotificationItemView = (NotificationItemView) view;
+ boolean notificationFooterHasIcons = mNumNotifications > 1;
+ int footerHeight = getResources().getDimensionPixelSize(
+ notificationFooterHasIcons ? R.dimen.notification_footer_height
+ : R.dimen.notification_empty_footer_height);
+ view.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
+ if (notificationFooterHasIcons) {
+ mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
+ }
+
+ int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+ if (shouldUnroundTopCorners) {
+ roundedCorners &= ~ROUNDED_TOP_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE);
+ }
+ if (shouldUnroundBottomCorners) {
+ roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE);
+ }
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
+ mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
+
+ mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate);
+ } else if (itemType == PopupPopulator.Item.SHORTCUT) {
+ view.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
+
+ if (itemType != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON && itemType.isShortcut
+ && mNumNotifications > 0) {
+ int prevHeight = view.getLayoutParams().height;
+ // Condense shortcuts height when there are notifications.
+ view.getLayoutParams().height = getResources().getDimensionPixelSize(
+ R.dimen.bg_popup_item_condensed_height);
+ if (view instanceof DeepShortcutView) {
+ float iconScale = (float) view.getLayoutParams().height / prevHeight;
+ ((DeepShortcutView) view).getIconView().setScaleX(iconScale);
+ ((DeepShortcutView) view).getIconView().setScaleY(iconScale);
+ }
+ }
+ }
+
private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) {
final Resources res = getResources();
final LayoutInflater inflater = mLauncher.getLayoutInflater();
@@ -337,261 +297,18 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
}
}
- protected PopupItemView getItemViewAt(int index) {
- if (!mIsAboveIcon) {
- // Opening down, so arrow is the first view.
- index++;
- }
- return (PopupItemView) getChildAt(index);
- }
-
- protected int getItemCount() {
- // All children except the arrow are items.
- return getChildCount() - 1;
- }
-
- private void animateOpen() {
- setVisibility(View.VISIBLE);
- mIsOpen = true;
-
- final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
- final Resources res = getResources();
- final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal.
- int itemsTotalHeight = 0;
- for (int i = 0; i < getItemCount(); i++) {
- itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
- }
- Point startPoint = computeAnimStartPoint(itemsTotalHeight);
- int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
- float radius = getItemViewAt(0).getBackgroundRadius();
- mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
- mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
- final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
- (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
- revealAnim.setDuration(revealDuration);
- revealAnim.setInterpolator(revealInterpolator);
-
- Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
- fadeIn.setDuration(revealDuration);
- fadeIn.setInterpolator(revealInterpolator);
- openAnim.play(fadeIn);
-
- // Animate the arrow.
- mArrow.setScaleX(0);
- mArrow.setScaleY(0);
- Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
- R.integer.config_popupArrowOpenDuration));
-
- openAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- Utilities.sendCustomAccessibilityEvent(
- PopupContainerWithArrow.this,
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- getContext().getString(R.string.action_deep_shortcut));
- }
- });
-
- mOpenCloseAnimator = openAnim;
- openAnim.playSequentially(revealAnim, arrowScale);
- openAnim.start();
- }
-
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- enforceContainedWithinScreen(l, r);
-
- }
-
- private void enforceContainedWithinScreen(int left, int right) {
- DragLayer dragLayer = mLauncher.getDragLayer();
- if (getTranslationX() + left < 0 ||
- getTranslationX() + right > dragLayer.getWidth()) {
- // If we are still off screen, center horizontally too.
- mGravity |= Gravity.CENTER_HORIZONTAL;
- }
-
- if (Gravity.isHorizontal(mGravity)) {
- setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
- }
- if (Gravity.isVertical(mGravity)) {
- setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
- }
- }
-
- /**
- * Returns the point at which the center of the arrow merges with the first popup item.
- */
- private Point computeAnimStartPoint(int itemsTotalHeight) {
- int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start:
- R.dimen.popup_arrow_horizontal_center_end);
- if (!mIsLeftAligned) {
- arrowCenterX = getMeasuredWidth() - arrowCenterX;
- }
- int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
- - itemsTotalHeight;
- // The y-coordinate of edge between the arrow and the first popup item.
- int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
- return new Point(arrowCenterX, arrowEdge);
- }
-
- /**
- * Orients this container above or below the given icon, aligning with the left or right.
- *
- * These are the preferred orientations, in order (RTL prefers right-aligned over left):
- * - Above and left-aligned
- * - Above and right-aligned
- * - Below and left-aligned
- * - Below and right-aligned
- *
- * So we always align left if there is enough horizontal space
- * and align above if there is enough vertical space.
- */
- private void orientAboutIcon(BubbleTextView icon, int arrowHeight) {
- int width = getMeasuredWidth();
- int height = getMeasuredHeight() + arrowHeight;
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.getDescendantRectRelativeToSelf(icon, mTempRect);
- Rect insets = dragLayer.getInsets();
-
- // Align left (right in RTL) if there is room.
- int leftAlignedX = mTempRect.left + icon.getPaddingLeft();
- int rightAlignedX = mTempRect.right - width - icon.getPaddingRight();
- int x = leftAlignedX;
- boolean canBeLeftAligned = leftAlignedX + width + insets.left
- < dragLayer.getRight() - insets.right;
- boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
- if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
- x = rightAlignedX;
- }
- mIsLeftAligned = x == leftAlignedX;
- if (mIsRtl) {
- x -= dragLayer.getWidth() - width;
- }
-
- // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
- int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight();
- iconWidth *= icon.getScaleX();
- Resources resources = getResources();
- int xOffset;
- if (isAlignedWithStart()) {
- // Aligning with the shortcut icon.
- int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
- int shortcutPaddingStart = resources.getDimensionPixelSize(
- R.dimen.popup_padding_start);
- xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
- } else {
- // Aligning with the drag handle.
- int shortcutDragHandleWidth = resources.getDimensionPixelSize(
- R.dimen.deep_shortcut_drag_handle_size);
- int shortcutPaddingEnd = resources.getDimensionPixelSize(
- R.dimen.popup_padding_end);
- xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
- }
- x += mIsLeftAligned ? xOffset : -xOffset;
-
- // Open above icon if there is room.
- int iconHeight = icon.getIcon() != null
- ? icon.getIcon().getBounds().height()
- : icon.getHeight();
- int y = mTempRect.top + icon.getPaddingTop() - height;
- mIsAboveIcon = y > dragLayer.getTop() + insets.top;
- if (!mIsAboveIcon) {
- y = mTempRect.top + icon.getPaddingTop() + iconHeight;
- }
-
- // Insets are added later, so subtract them now.
- if (mIsRtl) {
- x += insets.right;
- } else {
- x -= insets.left;
- }
- y -= insets.top;
-
- mGravity = 0;
- if (y + height > dragLayer.getBottom() - insets.bottom) {
- // The container is opening off the screen, so just center it in the drag layer instead.
- mGravity = Gravity.CENTER_VERTICAL;
- // Put the container next to the icon, preferring the right side in ltr (left in rtl).
- int rightSide = leftAlignedX + iconWidth - insets.left;
- int leftSide = rightAlignedX - iconWidth - insets.left;
- if (!mIsRtl) {
- if (rightSide + width < dragLayer.getRight()) {
- x = rightSide;
- mIsLeftAligned = true;
- } else {
- x = leftSide;
- mIsLeftAligned = false;
- }
- } else {
- if (leftSide > dragLayer.getLeft()) {
- x = leftSide;
- mIsLeftAligned = false;
- } else {
- x = rightSide;
- mIsLeftAligned = true;
- }
- }
- mIsAboveIcon = true;
+ protected void onWidgetsBound() {
+ if (mShortcutsItemView != null) {
+ mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon);
}
-
- setX(x);
- setY(y);
- }
-
- private boolean isAlignedWithStart() {
- return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
}
- /**
- * Adds an arrow view pointing at the original icon.
- * @param horizontalOffset the horizontal offset of the arrow, so that it
- * points at the center of the original icon
- */
- private View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
- LayoutParams layoutParams = new LayoutParams(width, height);
- if (mIsLeftAligned) {
- layoutParams.gravity = Gravity.LEFT;
- layoutParams.leftMargin = horizontalOffset;
- } else {
- layoutParams.gravity = Gravity.RIGHT;
- layoutParams.rightMargin = horizontalOffset;
- }
- if (mIsAboveIcon) {
- layoutParams.topMargin = verticalOffset;
- } else {
- layoutParams.bottomMargin = verticalOffset;
- }
-
- View arrowView = new View(getContext());
- if (Gravity.isVertical(mGravity)) {
- // This is only true if there wasn't room for the container next to the icon,
- // so we centered it instead. In that case we don't want to show the arrow.
- arrowView.setVisibility(INVISIBLE);
- } else {
- ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
- width, height, !mIsAboveIcon));
- Paint arrowPaint = arrowDrawable.getPaint();
- // Note that we have to use getChildAt() instead of getItemViewAt(),
- // since the latter expects the arrow which hasn't been added yet.
- PopupItemView itemAttachedToArrow = (PopupItemView)
- (getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0));
- arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
- // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
- int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
- arrowPaint.setPathEffect(new CornerPathEffect(radius));
- arrowView.setBackground(arrowDrawable);
- arrowView.setElevation(getElevation());
- }
- addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
- return arrowView;
+ @Override
+ protected int getIconHeightForPopupPlacement() {
+ return mOriginalIcon.getIcon() != null
+ ? mOriginalIcon.getIcon().getBounds().height()
+ : mOriginalIcon.getHeight();
}
/**
@@ -638,17 +355,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
};
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mInterceptTouchDown.set(ev.getX(), ev.getY());
- return false;
- }
- // Stop sending touch events to deep shortcut views if user moved beyond touch slop.
- return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
- > ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
/**
* Updates the notification header if the original icon's badge updated.
*/
@@ -719,18 +425,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
badgeInfo.getNotificationKeys()));
}
- @Override
- protected void onWidgetsBound() {
- if (mShortcutsItemView != null) {
- mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon);
- }
- }
-
- private ObjectAnimator createArrowScaleAnim(float scale) {
- return LauncherAnimUtils.ofPropertyValuesHolder(
- mArrow, new PropertyListBuilder().scale(scale).build());
- }
-
public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
return adjustItemHeights(heightToRemove, 0, duration);
}
@@ -832,124 +526,20 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
}
@Override
- protected void handleClose(boolean animate) {
- if (animate) {
- animateClose();
- } else {
- closeComplete();
- }
- }
-
- protected void animateClose() {
- if (!mIsOpen) {
- return;
- }
- mEndRect.setEmpty();
- if (mOpenCloseAnimator != null) {
- Outline outline = new Outline();
- getOutlineProvider().getOutline(this, outline);
- outline.getRect(mEndRect);
- mOpenCloseAnimator.cancel();
- }
- mIsOpen = false;
-
- final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
- final Resources res = getResources();
- final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal (reversed).
- int itemsTotalHeight = 0;
- for (int i = 0; i < getItemCount(); i++) {
- itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
- }
- Point startPoint = computeAnimStartPoint(itemsTotalHeight);
- int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
- float radius = getItemViewAt(0).getBackgroundRadius();
- mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
- if (mEndRect.isEmpty()) {
- mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
- }
- final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
- radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
- revealAnim.setDuration(revealDuration);
- revealAnim.setInterpolator(revealInterpolator);
- closeAnim.play(revealAnim);
-
- Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
- fadeOut.setDuration(revealDuration);
- fadeOut.setInterpolator(revealInterpolator);
- closeAnim.play(fadeOut);
-
+ protected void prepareCloseAnimator(AnimatorSet closeAnim) {
// Animate original icon's text back in.
- Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */);
- fadeText.setDuration(revealDuration);
- closeAnim.play(fadeText);
+ closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
- closeAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- if (mDeferContainerRemoval) {
- setVisibility(INVISIBLE);
- } else {
- closeComplete();
- }
- }
- });
- mOpenCloseAnimator = closeAnim;
- closeAnim.start();
mOriginalIcon.forceHideBadge(false);
+ super.prepareCloseAnimator(closeAnim);
}
- /**
- * Closes the folder without animation.
- */
+ @Override
protected void closeComplete() {
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- mOpenCloseAnimator = null;
- }
- mIsOpen = false;
- mDeferContainerRemoval = false;
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.forceHideBadge(false);
- mLauncher.getDragController().removeDragListener(this);
- mLauncher.getDragLayer().removeView(this);
- }
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_POPUP_CONTAINER_WITH_ARROW) != 0;
- }
-
- /**
- * Returns a DeepShortcutsContainer which is already open or null
- */
- public static PopupContainerWithArrow getOpen(Launcher launcher) {
- return getOpenView(launcher, TYPE_POPUP_CONTAINER_WITH_ARROW);
- }
-
- @Override
- public void logActionCommand(int command) {
- mLauncher.getUserEventDispatcher().logActionCommand(
- command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dl = mLauncher.getDragLayer();
- if (!dl.isEventOverView(this, ev)) {
- mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
- close(true);
-
- // We let touches on the original icon go through so that users can launch
- // the app with one tap if they don't find a shortcut they want.
- return mOriginalIcon == null || !dl.isEventOverView(mOriginalIcon, ev);
- }
- }
- return false;
+ mLauncher.getDragController().removeDragListener(this);
+ super.closeComplete();
}
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index c921b4b82..aeb713479 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -102,11 +102,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
mPackageUserToBadgeInfos.remove(removedPackageUserKey);
}
updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
-
- PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher);
- if (openContainer != null) {
- openContainer.trimNotifications(mPackageUserToBadgeInfos);
- }
+ trimNotifications(mPackageUserToBadgeInfos);
}
}
@@ -143,10 +139,13 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
if (!updatedBadges.isEmpty()) {
updateLauncherIconBadges(updatedBadges.keySet());
}
+ trimNotifications(updatedBadges);
+ }
- PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher);
- if (openContainer != null) {
- openContainer.trimNotifications(updatedBadges);
+ private void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
+ BaseActionPopup openContainer = BaseActionPopup.getOpen(mLauncher);
+ if (openContainer instanceof PopupContainerWithArrow) {
+ ((PopupContainerWithArrow) openContainer).trimNotifications(updatedBadges);
}
}
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 8ec051b30..75c3f26cf 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -32,7 +32,7 @@ import android.widget.FrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags;
+import com.android.launcher3.popup.BaseActionPopup.RoundedCornerFlags;
import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;