Add arrow view to deep shortcuts container.
- We create a trangular path, which we use as the view's outline (for the shadow) and as its PathShape background. - We scale up the arrow as the container opens. Bug: 28980830 Change-Id: I71e566552f063dfdb69067bb7e3cee659988f934
This commit is contained in:
@@ -164,7 +164,9 @@
|
||||
of the shortcut container before the container is removed. -->
|
||||
<dimen name="deep_shortcuts_start_drag_threshold">35dp</dimen>
|
||||
<dimen name="deep_shortcut_icon_size">36dp</dimen>
|
||||
<dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
|
||||
<dimen name="deep_shortcut_anim_translation_y">5dp</dimen>
|
||||
<dimen name="deep_shortcuts_arrow_width">10dp</dimen>
|
||||
<dimen name="deep_shortcuts_arrow_height">8dp</dimen>
|
||||
<dimen name="deep_shortcuts_arrow_vertical_offset">-2dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.graphics;
|
||||
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Wrapper around {@link android.graphics.drawable.shapes.PathShape}
|
||||
* that creates a shape with a triangular path (pointing up or down).
|
||||
*/
|
||||
public class TriangleShape extends PathShape {
|
||||
private Path mTriangularPath;
|
||||
|
||||
public TriangleShape(Path path, float stdWidth, float stdHeight) {
|
||||
super(path, stdWidth, stdHeight);
|
||||
mTriangularPath = path;
|
||||
}
|
||||
|
||||
public static TriangleShape create(float width, float height, boolean isPointingUp) {
|
||||
Path triangularPath = new Path();
|
||||
if (isPointingUp) {
|
||||
triangularPath.moveTo(0, height);
|
||||
triangularPath.lineTo(width, height);
|
||||
triangularPath.lineTo(width / 2, 0);
|
||||
triangularPath.close();
|
||||
} else {
|
||||
triangularPath.moveTo(0, 0);
|
||||
triangularPath.lineTo(width / 2, height);
|
||||
triangularPath.lineTo(width, 0);
|
||||
triangularPath.close();
|
||||
}
|
||||
return new TriangleShape(triangularPath, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOutline(@NonNull Outline outline) {
|
||||
outline.setConvexPath(mTriangularPath);
|
||||
}
|
||||
}
|
||||
@@ -89,11 +89,8 @@ public class DeepShortcutView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Creates an animator to play when the shortcut container is being opened.
|
||||
*
|
||||
* @param animationIndex The index at which this animation will be started
|
||||
* relative to other DeepShortcutView open animations.
|
||||
*/
|
||||
public Animator createOpenAnimation(int animationIndex, boolean isContainerAboveIcon) {
|
||||
public Animator createOpenAnimation(long animationDelay, boolean isContainerAboveIcon) {
|
||||
final Resources res = getResources();
|
||||
setVisibility(INVISIBLE);
|
||||
|
||||
@@ -119,8 +116,7 @@ public class DeepShortcutView extends FrameLayout {
|
||||
.scaleX(1).scaleY(1);
|
||||
|
||||
openAnimation.playTogether(reveal, translationY, scale);
|
||||
openAnimation.setStartDelay(animationIndex * res.getInteger(
|
||||
R.integer.config_deepShortcutOpenStagger));
|
||||
openAnimation.setStartDelay(animationDelay);
|
||||
openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration));
|
||||
openAnimation.setInterpolator(new DecelerateInterpolator());
|
||||
return openAnimation;
|
||||
|
||||
@@ -16,19 +16,24 @@
|
||||
|
||||
package com.android.launcher3.shortcuts;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
@@ -44,6 +49,7 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAnimUtils;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.LauncherViewPropertyAnimator;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutInfo;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -54,6 +60,7 @@ import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.graphics.ScaledPreviewProvider;
|
||||
import com.android.launcher3.graphics.TriangleShape;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
@@ -88,6 +95,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
private Point mIconLastTouchPos = new Point();
|
||||
private boolean mIsLeftAligned;
|
||||
private boolean mIsAboveIcon;
|
||||
private View mArrow;
|
||||
|
||||
private boolean mSrcIconDragStarted;
|
||||
|
||||
@@ -210,27 +218,56 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
}
|
||||
|
||||
private DeepShortcutView getShortcutAt(int index) {
|
||||
if (!mIsAboveIcon) {
|
||||
// Opening down, so arrow is the first view.
|
||||
index++;
|
||||
}
|
||||
return (DeepShortcutView) getChildAt(index);
|
||||
}
|
||||
|
||||
private int getShortcutCount() {
|
||||
// All children except the arrow are shortcuts.
|
||||
return getChildCount() - 1;
|
||||
}
|
||||
|
||||
private void animateOpen(BubbleTextView originalIcon) {
|
||||
orientAboutIcon(originalIcon);
|
||||
final Resources resources = getResources();
|
||||
final int arrowWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_width);
|
||||
final int arrowHeight = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_height);
|
||||
int iconWidth = originalIcon.getWidth() - originalIcon.getTotalPaddingLeft()
|
||||
- originalIcon.getTotalPaddingRight();
|
||||
iconWidth *= originalIcon.getScaleX();
|
||||
final int arrowHorizontalOffset = iconWidth / 2 - arrowWidth / 2;
|
||||
final int arrowVerticalOffset = resources.getDimensionPixelSize(
|
||||
R.dimen.deep_shortcuts_arrow_vertical_offset);
|
||||
mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
|
||||
|
||||
setVisibility(View.VISIBLE);
|
||||
|
||||
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
|
||||
final int numShortcuts = getChildCount();
|
||||
final int arrowOffset = getResources().getDimensionPixelSize(
|
||||
R.dimen.deep_shortcuts_arrow_horizontal_offset);
|
||||
final int pivotX = mIsLeftAligned ? arrowOffset : getMeasuredWidth() - arrowOffset;
|
||||
final int shortcutCount = getShortcutCount();
|
||||
final int pivotX = mIsLeftAligned ? arrowHorizontalOffset
|
||||
: getMeasuredWidth() - arrowHorizontalOffset;
|
||||
final int pivotY = getShortcutAt(0).getMeasuredHeight() / 2;
|
||||
for (int i = 0; i < numShortcuts; i++) {
|
||||
for (int i = 0; i < shortcutCount; i++) {
|
||||
DeepShortcutView deepShortcutView = getShortcutAt(i);
|
||||
deepShortcutView.setPivotX(pivotX);
|
||||
deepShortcutView.setPivotY(pivotY);
|
||||
int animationIndex = mIsAboveIcon ? numShortcuts - i - 1 : i;
|
||||
shortcutAnims.play(deepShortcutView.createOpenAnimation(animationIndex, mIsAboveIcon));
|
||||
int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i;
|
||||
long animationDelay = animationIndex * getResources().getInteger(
|
||||
R.integer.config_deepShortcutOpenStagger);
|
||||
shortcutAnims.play(deepShortcutView.createOpenAnimation(animationDelay, mIsAboveIcon));
|
||||
}
|
||||
mArrow.setScaleX(0);
|
||||
mArrow.setScaleY(0);
|
||||
final long shortcutAnimDuration = shortcutAnims.getChildAnimations().get(0).getDuration();
|
||||
final long arrowScaleDelay = shortcutAnimDuration / 6;
|
||||
final long arrowScaleDuration = shortcutAnimDuration - arrowScaleDelay;
|
||||
Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1);
|
||||
arrowScale.setStartDelay(arrowScaleDelay);
|
||||
arrowScale.setDuration(arrowScaleDuration);
|
||||
shortcutAnims.play(arrowScale);
|
||||
shortcutAnims.start();
|
||||
}
|
||||
|
||||
@@ -245,8 +282,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
*
|
||||
* So we always align left if there is enough horizontal space
|
||||
* and align above if there is enough vertical space.
|
||||
*
|
||||
* TODO: draw arrow based on orientation.
|
||||
*/
|
||||
private void orientAboutIcon(BubbleTextView icon) {
|
||||
int width = getMeasuredWidth();
|
||||
@@ -285,6 +320,36 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
setY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
LinearLayout.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());
|
||||
ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
|
||||
width, height, !mIsAboveIcon));
|
||||
arrowDrawable.getPaint().setColor(Color.WHITE);
|
||||
arrowView.setBackground(arrowDrawable);
|
||||
arrowView.setElevation(getElevation());
|
||||
addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
|
||||
return arrowView;
|
||||
}
|
||||
|
||||
private void deferDrag(BubbleTextView originalIcon) {
|
||||
mDeferredDragIcon = originalIcon;
|
||||
showDragView(originalIcon);
|
||||
@@ -339,6 +404,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
Utilities.translateEventCoordinates(this, mLauncher.getDragLayer(), ev);
|
||||
final int dragLayerX = (int) ev.getX();
|
||||
final int dragLayerY = (int) ev.getY();
|
||||
int shortcutCount = getShortcutCount();
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
if (mLastX != 0 || mLastY != 0) {
|
||||
mDistanceDragged += Math.hypot(mLastX - x, mLastY - y);
|
||||
@@ -347,6 +413,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
|
||||
mLastY = y;
|
||||
|
||||
if (shouldStartDeferredDrag((int) x, (int) y)) {
|
||||
DeepShortcutView topShortcut = getShortcutAt(0);
|
||||
DeepShortcutView bottomShortcut = getShortcutAt(shortcutCount - 1);
|
||||
mSrcIconDragStarted = true;
|
||||
cleanupDeferredDrag(true);
|
||||
mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
|
||||
Reference in New Issue
Block a user