Updating the UI of the options popup to make it look similar to icon popup
Bug: 77327164 Change-Id: I3580df8bf8a43cb44123f203ffed9a85fa33aea7
This commit is contained in:
@@ -15,83 +15,11 @@
|
||||
-->
|
||||
<com.android.launcher3.views.OptionsPopupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:id="@+id/deep_shortcuts_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/popupColorPrimary"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:elevation="@dimen/deep_shortcuts_elevation"
|
||||
android:orientation="horizontal"
|
||||
launcher:layout_ignoreInsets="true">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/wallpaper_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTint="?android:attr/textColorPrimary"
|
||||
android:drawableTop="@drawable/ic_wallpaper"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:text="@string/wallpaper_button_text"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"/>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/widget_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTint="?android:attr/textColorPrimary"
|
||||
android:drawableTop="@drawable/ic_widget"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:text="@string/widget_button_text"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/settings_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTint="?android:attr/textColorPrimary"
|
||||
android:drawableTop="@drawable/ic_setting"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:text="@string/settings_button_text"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.launcher3.views.OptionsPopupView>
|
||||
android:orientation="vertical" />
|
||||
|
||||
@@ -222,4 +222,5 @@
|
||||
|
||||
<!-- Overview -->
|
||||
<dimen name="options_menu_icon_size">24dp</dimen>
|
||||
<dimen name="options_menu_thumb_size">32dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -47,7 +47,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
|
||||
private static final String TAG = "BaseDraggingActivity";
|
||||
|
||||
// The Intent extra that defines whether to ignore the launch animation
|
||||
protected static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
|
||||
public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
|
||||
"com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
|
||||
|
||||
// When starting an action mode, setting this tag will cause the action mode to be cancelled
|
||||
|
||||
@@ -1634,30 +1634,6 @@ public class Launcher extends BaseDraggingActivity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for the wallpaper picker button that appears after a long press
|
||||
* on the home screen.
|
||||
*/
|
||||
public void onClickWallpaperPicker(View v) {
|
||||
if (!Utilities.isWallpaperAllowed(this)) {
|
||||
Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
|
||||
float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
|
||||
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
|
||||
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
|
||||
|
||||
String pickerPackage = getString(R.string.wallpaper_picker_package);
|
||||
if (!TextUtils.isEmpty(pickerPackage)) {
|
||||
intent.setPackage(pickerPackage);
|
||||
} else {
|
||||
// If there is no target package, use the default intent chooser animation
|
||||
intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
|
||||
}
|
||||
startActivitySafely(v, intent, null);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
|
||||
@@ -2415,7 +2391,7 @@ public class Launcher extends BaseDraggingActivity
|
||||
|
||||
// Setting the touch point to (-1, -1) will show the options popup in the center of
|
||||
// the screen.
|
||||
OptionsPopupView.show(this, -1, -1);
|
||||
OptionsPopupView.showDefaultOptions(this, -1, -1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -349,6 +349,11 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
}
|
||||
|
||||
public float getWallpaperOffsetForCenterPage() {
|
||||
int pageScroll = getScrollForPage(getPageNearestToCenterOfScreen());
|
||||
return mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
|
||||
}
|
||||
|
||||
public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
|
||||
Rect r = new Rect();
|
||||
cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.MarginLayoutParams;
|
||||
@@ -83,7 +82,7 @@ public class NotificationItemView {
|
||||
|
||||
public void addGutter() {
|
||||
if (mGutter == null) {
|
||||
mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter);
|
||||
mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
|
||||
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.anim.RevealOutlineAnimation;
|
||||
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.graphics.TriangleShape;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
||||
|
||||
/**
|
||||
* A container for shortcuts to deep links and notifications associated with an app.
|
||||
*/
|
||||
public abstract class ArrowPopup extends AbstractFloatingView {
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
protected final LayoutInflater mInflater;
|
||||
private final float mOutlineRadius;
|
||||
protected final Launcher mLauncher;
|
||||
protected final boolean mIsRtl;
|
||||
|
||||
private final int mArrayOffset;
|
||||
private final View mArrow;
|
||||
|
||||
protected boolean mIsLeftAligned;
|
||||
protected boolean mIsAboveIcon;
|
||||
private int mGravity;
|
||||
|
||||
protected Animator mOpenCloseAnimator;
|
||||
protected boolean mDeferContainerRemoval;
|
||||
private final Rect mStartRect = new Rect();
|
||||
private final Rect mEndRect = new Rect();
|
||||
|
||||
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
|
||||
setClipToOutline(true);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize arrow view
|
||||
final Resources resources = getResources();
|
||||
final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
|
||||
final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
|
||||
mArrow = new View(context);
|
||||
mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
|
||||
mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
|
||||
}
|
||||
|
||||
public ArrowPopup(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ArrowPopup(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (animate) {
|
||||
animateClose();
|
||||
} else {
|
||||
closeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends View> T inflateAndAdd(int resId, ViewGroup container) {
|
||||
View view = mInflater.inflate(resId, container, false);
|
||||
container.addView(view);
|
||||
return (T) view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all view inflation and reordering in complete.
|
||||
*/
|
||||
protected void onInflationComplete(boolean isReversed) { }
|
||||
|
||||
/**
|
||||
* Shows the popup at the desired location, optionally reversing the children.
|
||||
* @param viewsToFlip number of views from the top to to flip in case of reverse order
|
||||
*/
|
||||
protected void reorderAndShow(int viewsToFlip) {
|
||||
setVisibility(View.INVISIBLE);
|
||||
mIsOpen = true;
|
||||
mLauncher.getDragLayer().addView(this);
|
||||
orientAboutObject();
|
||||
|
||||
boolean reverseOrder = mIsAboveIcon;
|
||||
if (reverseOrder) {
|
||||
int count = getChildCount();
|
||||
ArrayList<View> allViews = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i == viewsToFlip) {
|
||||
Collections.reverse(allViews);
|
||||
}
|
||||
allViews.add(getChildAt(i));
|
||||
}
|
||||
Collections.reverse(allViews);
|
||||
removeAllViews();
|
||||
for (int i = 0; i < count; i++) {
|
||||
addView(allViews.get(i));
|
||||
}
|
||||
|
||||
orientAboutObject();
|
||||
}
|
||||
onInflationComplete(reverseOrder);
|
||||
|
||||
// Add the arrow.
|
||||
final Resources res = getResources();
|
||||
final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
|
||||
? R.dimen.popup_arrow_horizontal_center_start
|
||||
: R.dimen.popup_arrow_horizontal_center_end);
|
||||
final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
|
||||
mLauncher.getDragLayer().addView(mArrow);
|
||||
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
|
||||
if (mIsLeftAligned) {
|
||||
mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
|
||||
} else {
|
||||
mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
|
||||
}
|
||||
|
||||
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 showDefaultOptions the arrow.
|
||||
mArrow.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
|
||||
arrowLp.width, arrowLp.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));
|
||||
mArrow.setBackground(arrowDrawable);
|
||||
mArrow.setElevation(getElevation());
|
||||
}
|
||||
|
||||
mArrow.setPivotX(arrowLp.width / 2);
|
||||
mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
|
||||
|
||||
animateOpen();
|
||||
}
|
||||
|
||||
protected boolean isAlignedWithStart() {
|
||||
return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the location of the target object relative to the dragLayer.
|
||||
*/
|
||||
protected abstract void getTargetObjectLocation(Rect outPos);
|
||||
|
||||
/**
|
||||
* 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 orientAboutObject() {
|
||||
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
||||
int width = getMeasuredWidth();
|
||||
int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
|
||||
+ getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
|
||||
int height = getMeasuredHeight() + extraVerticalSpace;
|
||||
|
||||
getTargetObjectLocation(mTempRect);
|
||||
DragLayer dragLayer = mLauncher.getDragLayer();
|
||||
Rect insets = dragLayer.getInsets();
|
||||
|
||||
// Align left (right in RTL) if there is room.
|
||||
int leftAlignedX = mTempRect.left;
|
||||
int rightAlignedX = mTempRect.right - width;
|
||||
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;
|
||||
|
||||
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
|
||||
int iconWidth = mTempRect.width();
|
||||
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 = mTempRect.height();
|
||||
int y = mTempRect.top - height;
|
||||
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
|
||||
if (!mIsAboveIcon) {
|
||||
y = mTempRect.top + iconHeight + extraVerticalSpace;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (Gravity.isVertical(mGravity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
|
||||
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
|
||||
if (mIsAboveIcon) {
|
||||
arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
|
||||
lp.bottomMargin =
|
||||
mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
|
||||
arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
|
||||
} else {
|
||||
arrowLp.gravity = lp.gravity = Gravity.TOP;
|
||||
lp.topMargin = y + insets.top;
|
||||
arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
// enforce contained is within screen
|
||||
DragLayer dragLayer = mLauncher.getDragLayer();
|
||||
if (getTranslationX() + l < 0 || getTranslationX() + r > 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);
|
||||
mArrow.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (Gravity.isVertical(mGravity)) {
|
||||
setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
private void animateOpen() {
|
||||
setVisibility(View.VISIBLE);
|
||||
|
||||
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.
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.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 = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
|
||||
.setDuration(res.getInteger(R.integer.config_popupArrowOpenDuration));
|
||||
|
||||
openAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
sendCustomAccessibilityEvent(
|
||||
ArrowPopup.this,
|
||||
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
|
||||
getContext().getString(R.string.action_deep_shortcut));
|
||||
}
|
||||
});
|
||||
|
||||
mOpenCloseAnimator = openAnim;
|
||||
openAnim.playSequentially(revealAnim, arrowScale);
|
||||
openAnim.start();
|
||||
}
|
||||
|
||||
protected void animateClose() {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
mEndRect.setEmpty();
|
||||
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
|
||||
((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
|
||||
}
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
}
|
||||
mIsOpen = false;
|
||||
|
||||
final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
|
||||
// Hide the arrow
|
||||
closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
|
||||
closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
|
||||
|
||||
final Resources res = getResources();
|
||||
final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
|
||||
|
||||
// Rectangular reveal (reversed).
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.createRevealAnimator(this, true);
|
||||
revealAnim.setInterpolator(revealInterpolator);
|
||||
closeAnim.play(revealAnim);
|
||||
|
||||
Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
|
||||
fadeOut.setInterpolator(revealInterpolator);
|
||||
closeAnim.play(fadeOut);
|
||||
|
||||
onCreateCloseAnimation(closeAnim);
|
||||
closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
|
||||
closeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
if (mDeferContainerRemoval) {
|
||||
setVisibility(INVISIBLE);
|
||||
} else {
|
||||
closeComplete();
|
||||
}
|
||||
}
|
||||
});
|
||||
mOpenCloseAnimator = closeAnim;
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when creating the close transition allowing subclass can add additional animations.
|
||||
*/
|
||||
protected void onCreateCloseAnimation(AnimatorSet anim) { }
|
||||
|
||||
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
|
||||
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 arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
|
||||
|
||||
mStartRect.set(arrowCenterX, arrowCenterY, arrowCenterX, arrowCenterY);
|
||||
if (mEndRect.isEmpty()) {
|
||||
mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
|
||||
return new RoundedRectRevealOutlineProvider
|
||||
(mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the popup without animation.
|
||||
*/
|
||||
protected void closeComplete() {
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
mOpenCloseAnimator = null;
|
||||
}
|
||||
mIsOpen = false;
|
||||
mDeferContainerRemoval = false;
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
mLauncher.getDragLayer().removeView(mArrow);
|
||||
}
|
||||
}
|
||||
@@ -16,36 +16,28 @@
|
||||
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
|
||||
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
|
||||
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.LayoutTransition;
|
||||
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.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.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
@@ -56,20 +48,15 @@ import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.ItemInfoWithIcon;
|
||||
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.RevealOutlineAnimation;
|
||||
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.dragndrop.DragView;
|
||||
import com.android.launcher3.graphics.TriangleShape;
|
||||
import com.android.launcher3.logging.LoggerUtils;
|
||||
import com.android.launcher3.notification.NotificationInfo;
|
||||
import com.android.launcher3.notification.NotificationItemView;
|
||||
@@ -79,85 +66,38 @@ import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
||||
import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
|
||||
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
|
||||
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
|
||||
/**
|
||||
* 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 ArrowPopup implements DragSource,
|
||||
DragController.DragListener, View.OnLongClickListener,
|
||||
View.OnTouchListener {
|
||||
|
||||
private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
|
||||
private final PointF mInterceptTouchDown = new PointF();
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final Point mIconLastTouchPos = new Point();
|
||||
|
||||
private final int mStartDragThreshold;
|
||||
private final LayoutInflater mInflater;
|
||||
private final float mOutlineRadius;
|
||||
private final Launcher mLauncher;
|
||||
private final LauncherAccessibilityDelegate mAccessibilityDelegate;
|
||||
private final boolean mIsRtl;
|
||||
|
||||
private final int mArrayOffset;
|
||||
private final View mArrow;
|
||||
|
||||
private BubbleTextView mOriginalIcon;
|
||||
private NotificationItemView mNotificationItemView;
|
||||
private int mNumNotifications;
|
||||
|
||||
private ViewGroup mSystemShortcutContainer;
|
||||
|
||||
private boolean mIsLeftAligned;
|
||||
protected boolean mIsAboveIcon;
|
||||
private int mNumNotifications;
|
||||
private int mGravity;
|
||||
|
||||
protected Animator mOpenCloseAnimator;
|
||||
protected boolean mDeferContainerRemoval;
|
||||
private final Rect mStartRect = new Rect();
|
||||
private final Rect mEndRect = new Rect();
|
||||
|
||||
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mStartDragThreshold = getResources().getDimensionPixelSize(
|
||||
R.dimen.deep_shortcuts_start_drag_threshold);
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
|
||||
setClipToOutline(true);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize arrow view
|
||||
final Resources resources = getResources();
|
||||
final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
|
||||
final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
|
||||
mArrow = new View(context);
|
||||
mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
|
||||
mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
|
||||
}
|
||||
|
||||
public PopupContainerWithArrow(Context context, AttributeSet attrs) {
|
||||
@@ -222,21 +162,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (animate) {
|
||||
animateClose();
|
||||
} else {
|
||||
closeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends View> T inflateAndAdd(int resId) {
|
||||
View view = mInflater.inflate(resId, this, false);
|
||||
addView(view);
|
||||
return (T) view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the notifications and deep shortcuts associated with {@param icon}.
|
||||
* @return the container if shown or null.
|
||||
@@ -267,13 +192,30 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInflationComplete(boolean isReversed) {
|
||||
if (isReversed && mNotificationItemView != null) {
|
||||
mNotificationItemView.inverseGutterMargin();
|
||||
}
|
||||
|
||||
// Update dividers
|
||||
int count = getChildCount();
|
||||
DeepShortcutView lastView = null;
|
||||
for (int i = 0; i < count; i++) {
|
||||
View view = getChildAt(i);
|
||||
if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
|
||||
if (lastView != null) {
|
||||
lastView.setDividerVisibility(VISIBLE);
|
||||
}
|
||||
lastView = (DeepShortcutView) view;
|
||||
lastView.setDividerVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
|
||||
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
|
||||
mNumNotifications = notificationKeys.size();
|
||||
|
||||
setVisibility(View.INVISIBLE);
|
||||
mLauncher.getDragLayer().addView(this);
|
||||
|
||||
mOriginalIcon = originalIcon;
|
||||
|
||||
// Add views
|
||||
@@ -295,17 +237,15 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
}
|
||||
|
||||
for (int i = shortcutIds.size(); i > 0; i--) {
|
||||
mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut));
|
||||
mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut, this));
|
||||
}
|
||||
updateHiddenShortcuts();
|
||||
|
||||
if (!systemShortcuts.isEmpty()) {
|
||||
mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons);
|
||||
mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
|
||||
for (SystemShortcut shortcut : systemShortcuts) {
|
||||
View view = mInflater.inflate(R.layout.system_shortcut_icon_only,
|
||||
mSystemShortcutContainer, false);
|
||||
mSystemShortcutContainer.addView(view);
|
||||
initializeSystemShortcut(view, shortcut);
|
||||
initializeSystemShortcut(
|
||||
R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
|
||||
}
|
||||
}
|
||||
} else if (!systemShortcuts.isEmpty()) {
|
||||
@@ -314,68 +254,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
}
|
||||
|
||||
for (SystemShortcut shortcut : systemShortcuts) {
|
||||
initializeSystemShortcut(inflateAndAdd(R.layout.system_shortcut), shortcut);
|
||||
initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
|
||||
}
|
||||
}
|
||||
orientAboutIcon();
|
||||
|
||||
boolean reverseOrder = mIsAboveIcon;
|
||||
if (reverseOrder) {
|
||||
int count = getChildCount();
|
||||
ArrayList<View> allViews = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i == viewsToFlip) {
|
||||
Collections.reverse(allViews);
|
||||
}
|
||||
allViews.add(getChildAt(i));
|
||||
}
|
||||
Collections.reverse(allViews);
|
||||
removeAllViews();
|
||||
for (int i = 0; i < count; i++) {
|
||||
addView(allViews.get(i));
|
||||
}
|
||||
if (mNotificationItemView != null) {
|
||||
mNotificationItemView.inverseGutterMargin();
|
||||
}
|
||||
|
||||
orientAboutIcon();
|
||||
}
|
||||
updateDividers();
|
||||
|
||||
// Add the arrow.
|
||||
final Resources res = getResources();
|
||||
final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
|
||||
? R.dimen.popup_arrow_horizontal_center_start
|
||||
: R.dimen.popup_arrow_horizontal_center_end);
|
||||
final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
|
||||
mLauncher.getDragLayer().addView(mArrow);
|
||||
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
|
||||
if (mIsLeftAligned) {
|
||||
mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
|
||||
} else {
|
||||
mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
|
||||
}
|
||||
|
||||
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.
|
||||
mArrow.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
|
||||
arrowLp.width, arrowLp.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));
|
||||
mArrow.setBackground(arrowDrawable);
|
||||
mArrow.setElevation(getElevation());
|
||||
}
|
||||
|
||||
mArrow.setPivotX(arrowLp.width / 2);
|
||||
mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
|
||||
|
||||
animateOpen();
|
||||
reorderAndShow(viewsToFlip);
|
||||
|
||||
ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
|
||||
int numShortcuts = mShortcuts.size() + systemShortcuts.size();
|
||||
@@ -401,189 +284,15 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
this, shortcutIds, mShortcuts, notificationKeys));
|
||||
}
|
||||
|
||||
protected boolean isAlignedWithStart() {
|
||||
return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
||||
int width = getMeasuredWidth();
|
||||
int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
|
||||
+ getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
|
||||
int height = getMeasuredHeight() + extraVerticalSpace;
|
||||
|
||||
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;
|
||||
|
||||
// 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 + extraVerticalSpace;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (Gravity.isVertical(mGravity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
|
||||
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
|
||||
if (mIsAboveIcon) {
|
||||
arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
|
||||
lp.bottomMargin =
|
||||
mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
|
||||
arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
|
||||
} else {
|
||||
arrowLp.gravity = lp.gravity = Gravity.TOP;
|
||||
lp.topMargin = y + insets.top;
|
||||
arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
// enforce contained is within screen
|
||||
DragLayer dragLayer = mLauncher.getDragLayer();
|
||||
if (getTranslationX() + l < 0 || getTranslationX() + r > 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);
|
||||
mArrow.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (Gravity.isVertical(mGravity)) {
|
||||
setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.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 = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
|
||||
.setDuration(res.getInteger(R.integer.config_popupArrowOpenDuration));
|
||||
|
||||
openAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
sendCustomAccessibilityEvent(
|
||||
PopupContainerWithArrow.this,
|
||||
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
|
||||
getContext().getString(R.string.action_deep_shortcut));
|
||||
}
|
||||
});
|
||||
|
||||
mOpenCloseAnimator = openAnim;
|
||||
openAnim.playSequentially(revealAnim, arrowScale);
|
||||
openAnim.start();
|
||||
protected void getTargetObjectLocation(Rect outPos) {
|
||||
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
|
||||
outPos.top += mOriginalIcon.getPaddingTop();
|
||||
outPos.left += mOriginalIcon.getPaddingLeft();
|
||||
outPos.right -= mOriginalIcon.getPaddingRight();
|
||||
outPos.bottom = outPos.top + (mOriginalIcon.getIcon() != null
|
||||
? mOriginalIcon.getIcon().getBounds().height()
|
||||
: mOriginalIcon.getHeight());
|
||||
}
|
||||
|
||||
public void applyNotificationInfos(List<NotificationInfo> notificationInfos) {
|
||||
@@ -642,10 +351,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
if (onClickListener != null && widgetsView == null) {
|
||||
// We didn't have any widgets cached but now there are some, so enable the shortcut.
|
||||
if (mSystemShortcutContainer != this) {
|
||||
View view = mInflater.inflate(R.layout.system_shortcut_icon_only,
|
||||
mSystemShortcutContainer, false);
|
||||
mSystemShortcutContainer.addView(view);
|
||||
initializeSystemShortcut(view, widgetInfo);
|
||||
initializeSystemShortcut(
|
||||
R.layout.system_shortcut_icon_only, mSystemShortcutContainer, widgetInfo);
|
||||
} else {
|
||||
// If using the expanded system shortcut (as opposed to just the icon), we need to
|
||||
// reopen the container to ensure measurements etc. all work out. While this could
|
||||
@@ -665,7 +372,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeSystemShortcut(View view, SystemShortcut info) {
|
||||
private void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
|
||||
View view = inflateAndAdd(resId, container);
|
||||
if (view instanceof DeepShortcutView) {
|
||||
// Expanded system shortcut, with both icon and text shown on white background.
|
||||
final DeepShortcutView shortcutView = (DeepShortcutView) view;
|
||||
@@ -682,12 +390,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
(ItemInfo) mOriginalIcon.getTag()));
|
||||
}
|
||||
|
||||
protected int getIconHeightForPopupPlacement() {
|
||||
return mOriginalIcon.getIcon() != null
|
||||
? mOriginalIcon.getIcon().getBounds().height()
|
||||
: mOriginalIcon.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines when the deferred drag should be started.
|
||||
*
|
||||
@@ -807,91 +509,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
targetParent.containerType = ContainerType.DEEPSHORTCUTS;
|
||||
}
|
||||
|
||||
protected void animateClose() {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
mEndRect.setEmpty();
|
||||
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
|
||||
((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
|
||||
}
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
}
|
||||
mIsOpen = false;
|
||||
|
||||
final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
|
||||
// Hide the arrow
|
||||
closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
|
||||
closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
|
||||
|
||||
@Override
|
||||
protected void onCreateCloseAnimation(AnimatorSet anim) {
|
||||
// Animate original icon's text back in.
|
||||
closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
|
||||
anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
|
||||
mOriginalIcon.forceHideBadge(false);
|
||||
|
||||
final Resources res = getResources();
|
||||
final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
|
||||
|
||||
// Rectangular reveal (reversed).
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.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));
|
||||
|
||||
closeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
if (mDeferContainerRemoval) {
|
||||
setVisibility(INVISIBLE);
|
||||
} else {
|
||||
closeComplete();
|
||||
}
|
||||
}
|
||||
});
|
||||
mOpenCloseAnimator = closeAnim;
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
|
||||
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 arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
|
||||
|
||||
mStartRect.set(arrowCenterX, arrowCenterY, arrowCenterX, arrowCenterY);
|
||||
if (mEndRect.isEmpty()) {
|
||||
mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
|
||||
return new RoundedRectRevealOutlineProvider
|
||||
(mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the popup without animation.
|
||||
*/
|
||||
private void closeComplete() {
|
||||
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
|
||||
mOriginalIcon.forceHideBadge(false);
|
||||
|
||||
mLauncher.getDragController().removeDragListener(this);
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
mOpenCloseAnimator = null;
|
||||
}
|
||||
mIsOpen = false;
|
||||
mDeferContainerRemoval = false;
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
mLauncher.getDragLayer().removeView(mArrow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -144,7 +144,7 @@ public class WorkspaceTouchListener implements OnTouchListener, Runnable {
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
|
||||
Action.Direction.NONE, ContainerType.WORKSPACE,
|
||||
mWorkspace.getCurrentPage());
|
||||
OptionsPopupView.show(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
|
||||
OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,15 +108,12 @@ public class LauncherDragIndicator extends ImageView implements Insettable, OnCl
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
if (action == WALLPAPERS) {
|
||||
launcher.onClickWallpaperPicker(this);
|
||||
return true;
|
||||
return OptionsPopupView.startWallpaperPicker(this);
|
||||
} else if (action == WIDGETS) {
|
||||
return OptionsPopupView.onWidgetsClicked(launcher);
|
||||
return OptionsPopupView.onWidgetsClicked(this);
|
||||
} else if (action == SETTINGS) {
|
||||
OptionsPopupView.startSettings(launcher);
|
||||
return true;
|
||||
return OptionsPopupView.startSettings(this);
|
||||
}
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
@@ -15,52 +15,42 @@
|
||||
*/
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import static com.android.launcher3.BaseDraggingActivity.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
|
||||
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.anim.Interpolators;
|
||||
import com.android.launcher3.anim.RevealOutlineAnimation;
|
||||
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.graphics.ColorScrim;
|
||||
import com.android.launcher3.popup.ArrowPopup;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Popup shown on long pressing an empty space in launcher
|
||||
*/
|
||||
public class OptionsPopupView extends AbstractFloatingView
|
||||
public class OptionsPopupView extends ArrowPopup
|
||||
implements OnClickListener, OnLongClickListener {
|
||||
|
||||
private final float mOutlineRadius;
|
||||
private final Launcher mLauncher;
|
||||
private final PointF mTouchPoint = new PointF();
|
||||
|
||||
private final ColorScrim mScrim;
|
||||
|
||||
protected Animator mOpenCloseAnimator;
|
||||
private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
|
||||
private RectF mTargetRect;
|
||||
|
||||
public OptionsPopupView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
@@ -68,31 +58,6 @@ public class OptionsPopupView extends AbstractFloatingView
|
||||
|
||||
public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
|
||||
setClipToOutline(true);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
|
||||
}
|
||||
});
|
||||
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mScrim = ColorScrim.createExtractedColorScrim(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
attachListeners(findViewById(R.id.wallpaper_button));
|
||||
attachListeners(findViewById(R.id.widget_button));
|
||||
attachListeners(findViewById(R.id.settings_button));
|
||||
}
|
||||
|
||||
private void attachListeners(View view) {
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,20 +71,14 @@ public class OptionsPopupView extends AbstractFloatingView
|
||||
}
|
||||
|
||||
private boolean handleViewClick(View view, int action) {
|
||||
if (view.getId() == R.id.wallpaper_button) {
|
||||
mLauncher.onClickWallpaperPicker(view);
|
||||
logTap(action, ControlType.WALLPAPER_BUTTON);
|
||||
close(true);
|
||||
return true;
|
||||
} else if (view.getId() == R.id.widget_button) {
|
||||
logTap(action, ControlType.WIDGETS_BUTTON);
|
||||
if (onWidgetsClicked(mLauncher)) {
|
||||
close(true);
|
||||
return true;
|
||||
}
|
||||
} else if (view.getId() == R.id.settings_button) {
|
||||
startSettings(mLauncher);
|
||||
logTap(action, ControlType.SETTINGS_BUTTON);
|
||||
OptionItem item = mItemMap.get(view);
|
||||
if (item == null) {
|
||||
return false;
|
||||
}
|
||||
if (item.mControlTypeForLog > 0) {
|
||||
logTap(action, item.mControlTypeForLog);
|
||||
}
|
||||
if (item.mClickListener.onLongClick(view)) {
|
||||
close(true);
|
||||
return true;
|
||||
}
|
||||
@@ -142,63 +101,6 @@ public class OptionsPopupView extends AbstractFloatingView
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (animate) {
|
||||
animateClose();
|
||||
} else {
|
||||
closeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void animateClose() {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
mIsOpen = false;
|
||||
|
||||
final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
|
||||
closeAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
|
||||
|
||||
// Rectangular reveal (reversed).
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.createRevealAnimator(this, true);
|
||||
closeAnim.play(revealAnim);
|
||||
|
||||
Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
|
||||
fadeOut.setInterpolator(Interpolators.DEACCEL);
|
||||
closeAnim.play(fadeOut);
|
||||
|
||||
Animator gradientAlpha = ObjectAnimator.ofFloat(mScrim, ColorScrim.PROGRESS, 0);
|
||||
gradientAlpha.setInterpolator(Interpolators.DEACCEL);
|
||||
closeAnim.play(gradientAlpha);
|
||||
|
||||
closeAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
closeComplete();
|
||||
}
|
||||
});
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
}
|
||||
mOpenCloseAnimator = closeAnim;
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the popup without animation.
|
||||
*/
|
||||
private void closeComplete() {
|
||||
if (mOpenCloseAnimator != null) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
mOpenCloseAnimator = null;
|
||||
}
|
||||
mIsOpen = false;
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
// TODO:
|
||||
@@ -209,90 +111,49 @@ public class OptionsPopupView extends AbstractFloatingView
|
||||
return (type & TYPE_OPTIONS_POPUP) != 0;
|
||||
}
|
||||
|
||||
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
|
||||
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
|
||||
Rect startRect = new Rect();
|
||||
startRect.offset((int) (mTouchPoint.x - lp.x), (int) (mTouchPoint.y - lp.y));
|
||||
|
||||
Rect endRect = new Rect(0, 0, lp.width, lp.height);
|
||||
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
|
||||
((RevealOutlineAnimation) getOutlineProvider()).getOutline(endRect);
|
||||
}
|
||||
|
||||
return new RoundedRectRevealOutlineProvider
|
||||
(mOutlineRadius, mOutlineRadius, startRect, endRect);
|
||||
@Override
|
||||
protected void getTargetObjectLocation(Rect outPos) {
|
||||
mTargetRect.roundOut(outPos);
|
||||
}
|
||||
|
||||
private void animateOpen() {
|
||||
mIsOpen = true;
|
||||
final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
|
||||
openAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
|
||||
public static void show(Launcher launcher, RectF targetRect, List<OptionItem> items) {
|
||||
OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater()
|
||||
.inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false);
|
||||
popup.mTargetRect = targetRect;
|
||||
|
||||
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
|
||||
.createRevealAnimator(this, false);
|
||||
openAnim.play(revealAnim);
|
||||
|
||||
Animator gradientAlpha = ObjectAnimator.ofFloat(mScrim, ColorScrim.PROGRESS, 1);
|
||||
gradientAlpha.setInterpolator(Interpolators.ACCEL);
|
||||
openAnim.play(gradientAlpha);
|
||||
|
||||
mOpenCloseAnimator = openAnim;
|
||||
|
||||
openAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mOpenCloseAnimator = null;
|
||||
}
|
||||
});
|
||||
openAnim.start();
|
||||
for (OptionItem item : items) {
|
||||
DeepShortcutView view = popup.inflateAndAdd(R.layout.system_shortcut, popup);
|
||||
view.getIconView().setBackgroundResource(item.mIconRes);
|
||||
view.getBubbleText().setText(item.mLabelRes);
|
||||
view.setDividerVisibility(View.INVISIBLE);
|
||||
view.setOnClickListener(popup);
|
||||
view.setOnLongClickListener(popup);
|
||||
popup.mItemMap.put(view, item);
|
||||
}
|
||||
popup.reorderAndShow(popup.getChildCount());
|
||||
}
|
||||
|
||||
public static void show(Launcher launcher, float x, float y) {
|
||||
DragLayer dl = launcher.getDragLayer();
|
||||
OptionsPopupView view = (OptionsPopupView) launcher.getLayoutInflater()
|
||||
.inflate(R.layout.longpress_options_menu, dl, false);
|
||||
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) view.getLayoutParams();
|
||||
|
||||
int maxWidth = dl.getWidth();
|
||||
int maxHeight = dl.getHeight();
|
||||
if (x <= 0 || y <= 0 || x >= maxWidth || y >= maxHeight) {
|
||||
x = maxWidth / 2;
|
||||
y = maxHeight / 2;
|
||||
public static void showDefaultOptions(Launcher launcher, float x, float y) {
|
||||
float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
|
||||
if (x < 0 || y < 0) {
|
||||
x = launcher.getDragLayer().getWidth() / 2;
|
||||
y = launcher.getDragLayer().getHeight() / 2;
|
||||
}
|
||||
view.mTouchPoint.set(x, y);
|
||||
RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
|
||||
|
||||
int height = lp.height;
|
||||
ArrayList<OptionItem> options = new ArrayList<>();
|
||||
options.add(new OptionItem(R.string.wallpaper_button_text, R.drawable.ic_wallpaper,
|
||||
ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
|
||||
options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
|
||||
ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
|
||||
options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
|
||||
ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings));
|
||||
|
||||
// Find a good width;
|
||||
int childCount = view.getChildCount();
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(maxWidth / childCount, MeasureSpec.AT_MOST);
|
||||
int maxChildWidth = 0;
|
||||
|
||||
for (int i = 0; i < childCount; i ++) {
|
||||
View child = ((ViewGroup) view.getChildAt(i)).getChildAt(0);
|
||||
child.measure(widthSpec, heightSpec);
|
||||
maxChildWidth = Math.max(maxChildWidth, child.getMeasuredWidth());
|
||||
}
|
||||
Rect insets = dl.getInsets();
|
||||
int margin = (int) (2 * view.getElevation());
|
||||
|
||||
int width = Math.min(maxWidth - insets.left - insets.right - 2 * margin,
|
||||
maxChildWidth * childCount);
|
||||
lp.width = width;
|
||||
|
||||
// Position is towards the finger
|
||||
lp.customPosition = true;
|
||||
lp.x = Utilities.boundToRange((int) (x - width / 2), insets.left + margin,
|
||||
maxWidth - insets.right - width - margin);
|
||||
lp.y = Utilities.boundToRange((int) (y - height / 2), insets.top + margin,
|
||||
maxHeight - insets.bottom - height - margin);
|
||||
|
||||
view.animateOpen();
|
||||
launcher.getDragLayer().addView(view);
|
||||
show(launcher, target, options);
|
||||
}
|
||||
|
||||
public static boolean onWidgetsClicked(Launcher launcher) {
|
||||
public static boolean onWidgetsClicked(View view) {
|
||||
Launcher launcher = Launcher.getLauncher(view.getContext());
|
||||
if (launcher.getPackageManager().isSafeMode()) {
|
||||
Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
@@ -302,9 +163,51 @@ public class OptionsPopupView extends AbstractFloatingView
|
||||
}
|
||||
}
|
||||
|
||||
public static void startSettings(Launcher launcher) {
|
||||
public static boolean startSettings(View view) {
|
||||
Launcher launcher = Launcher.getLauncher(view.getContext());
|
||||
launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
|
||||
.setPackage(launcher.getPackageName())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for the wallpaper picker button that appears after a long press
|
||||
* on the home screen.
|
||||
*/
|
||||
public static boolean startWallpaperPicker(View v) {
|
||||
Launcher launcher = Launcher.getLauncher(v.getContext());
|
||||
if (!Utilities.isWallpaperAllowed(launcher)) {
|
||||
Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
|
||||
.putExtra(EXTRA_WALLPAPER_OFFSET,
|
||||
launcher.getWorkspace().getWallpaperOffsetForCenterPage());
|
||||
|
||||
String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
|
||||
if (!TextUtils.isEmpty(pickerPackage)) {
|
||||
intent.setPackage(pickerPackage);
|
||||
} else {
|
||||
// If there is no target package, use the default intent chooser animation
|
||||
intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
|
||||
}
|
||||
return launcher.startActivitySafely(v, intent, null);
|
||||
}
|
||||
|
||||
public static class OptionItem {
|
||||
|
||||
private final int mLabelRes;
|
||||
private final int mIconRes;
|
||||
private final int mControlTypeForLog;
|
||||
private final OnLongClickListener mClickListener;
|
||||
|
||||
public OptionItem(int labelRes, int iconRes, int controlTypeForLog,
|
||||
OnLongClickListener clickListener) {
|
||||
mLabelRes = labelRes;
|
||||
mIconRes = iconRes;
|
||||
mControlTypeForLog = controlTypeForLog;
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user