Converting PopupContainerWithArrow into a base class so that it is easier
to create other types of popup Bug: 67585158 Change-Id: I966ae7bb90f941951b26feaf71b3ea30c3f3c0cc
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
@Override
|
||||
protected void onWidgetsBound() {
|
||||
if (mShortcutsItemView != null) {
|
||||
mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
super.closeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user