summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Wickham <twickham@google.com>2016-07-15 17:23:07 -0700
committerTony Wickham <twickham@google.com>2016-07-19 13:58:51 -0700
commit8f58e61d02fcb0ca90a2803e76a8792ec2c1f99a (patch)
tree2cba823be7962a2100c62acf086784f69fcae0fc
parent1e3d490e08b06750d467e144e5d74ea420c59613 (diff)
downloadandroid_packages_apps_Trebuchet-8f58e61d02fcb0ca90a2803e76a8792ec2c1f99a.tar.gz
android_packages_apps_Trebuchet-8f58e61d02fcb0ca90a2803e76a8792ec2c1f99a.tar.bz2
android_packages_apps_Trebuchet-8f58e61d02fcb0ca90a2803e76a8792ec2c1f99a.zip
Update shortcut animations.
- Open animation: shortcuts reveal using modified circular reveal (so that it reveals in the pill shape instead of a circle); slight translation away from the original icon; scale icon and text. - Hover animation: scale the shortcut pill and translate others away. Bug: 28980830 Bug: 30127368 Change-Id: I8ed05c7a082f2c2a3f6c663da7259f6cd33e394f
-rw-r--r--res/drawable/bg_white_pill.xml3
-rw-r--r--res/layout/deep_shortcut.xml12
-rw-r--r--res/values/config.xml5
-rw-r--r--res/values/dimens.xml6
-rw-r--r--res/values/styles.xml3
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java10
-rw-r--r--src/com/android/launcher3/folder/Folder.java6
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutView.java109
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java80
-rw-r--r--src/com/android/launcher3/util/CircleRevealOutlineProvider.java57
-rw-r--r--src/com/android/launcher3/util/PillRevealOutlineProvider.java66
-rw-r--r--src/com/android/launcher3/util/RevealOutlineAnimation.java72
-rw-r--r--src/com/android/launcher3/util/RevealOutlineProvider.java49
-rw-r--r--src/com/android/launcher3/util/UiThreadCircularReveal.java57
14 files changed, 378 insertions, 157 deletions
diff --git a/res/drawable/bg_white_pill.xml b/res/drawable/bg_white_pill.xml
index 29c3145cc..186b06b67 100644
--- a/res/drawable/bg_white_pill.xml
+++ b/res/drawable/bg_white_pill.xml
@@ -17,6 +17,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/quantum_panel_bg_color" />
- <size android:height="48dp" />
- <corners android:radius="24dp" />
+ <corners android:radius="@dimen/bg_pill_radius" />
</shape> \ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index b87923564..089546033 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -16,5 +16,13 @@
<com.android.launcher3.shortcuts.DeepShortcutView
xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/Icon.DeepShortcut"
- android:focusable="true"/>
+ android:layout_width="@dimen/bg_pill_width"
+ android:layout_height="@dimen/bg_pill_height"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:background="@drawable/bg_white_pill">
+
+ <com.android.launcher3.BubbleTextView
+ android:id="@+id/deep_shortcut"
+ style="@style/Icon.DeepShortcut"
+ android:focusable="true"/>
+</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/values/config.xml b/res/values/config.xml
index a678958bb..bdedff0df 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -86,6 +86,11 @@
<!-- View ID used by cell layout to jail its content -->
<item type="id" name="cell_layout_jail_id" />
+<!-- Deep shortcuts -->
+ <integer name="config_deepShortcutOpenDuration">300</integer>
+ <integer name="config_deepShortcutOpenStagger">20</integer>
+ <integer name="config_deepShortcutHoverDuration">120</integer>
+
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
<item type="id" name="action_uninstall" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 38775f14f..8b02e5aac 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -152,12 +152,16 @@
<!-- Deep shortcuts -->
<dimen name="deep_shortcuts_elevation">9dp</dimen>
- <dimen name="deep_shortcuts_width">180dp</dimen>
+ <dimen name="bg_pill_width">180dp</dimen>
+ <dimen name="bg_pill_height">48dp</dimen>
+ <dimen name="bg_pill_radius">24dp</dimen>
<dimen name="deep_shortcuts_spacing">4dp</dimen>
<dimen name="deep_shortcuts_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this much distance away from the nearest edge
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>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 3e827a643..532b701d0 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -78,10 +78,7 @@
</style>
<style name="Icon.DeepShortcut">
- <item name="android:background">@drawable/bg_white_pill</item>
<item name="android:gravity">start|center_vertical</item>
- <item name="android:minWidth">@dimen/deep_shortcuts_width</item>
- <item name="android:maxWidth">@dimen/deep_shortcuts_width</item>
<item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
<item name="android:paddingLeft">7dp</item>
<item name="android:drawablePadding">12dp</item>
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index c3b7fe760..5c7e670a9 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -35,8 +35,8 @@ import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.UiThreadCircularReveal;
import com.android.launcher3.widget.WidgetsContainerView;
import java.util.HashMap;
@@ -345,8 +345,8 @@ public class LauncherStateTransitionAnimation {
float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
revealView, buttonView);
- Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
- height / 2, startRadius, revealRadius);
+ Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
+ startRadius, revealRadius).createRevealAnimator(revealView);
reveal.setDuration(revealDuration);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
if (listener != null) {
@@ -789,8 +789,8 @@ public class LauncherStateTransitionAnimation {
float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
AnimatorListenerAdapter listener =
pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
- Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
- height / 2, revealRadius, finalRadius);
+ Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
+ revealRadius, finalRadius).createRevealAnimator(revealView);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
reveal.setDuration(revealDuration);
reveal.setStartDelay(itemsAlphaStagger);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 556be0c28..2fbbad56d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -80,8 +80,8 @@ import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.UiThreadCircularReveal;
import java.util.ArrayList;
import java.util.Collections;
@@ -554,8 +554,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
float radius = (float) Math.hypot(rx, ry);
- Animator reveal = UiThreadCircularReveal.createCircularReveal(this, (int) getPivotX(),
- (int) getPivotY(), 0, radius);
+ Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(),
+ (int) getPivotY(), 0, radius).createRevealAnimator(this);
reveal.setDuration(mMaterialExpandDuration);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 7997d1e2e..f9dd336a2 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -16,19 +16,32 @@
package com.android.launcher3.shortcuts;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherViewPropertyAnimator;
import com.android.launcher3.R;
+import com.android.launcher3.util.PillRevealOutlineProvider;
/**
- * A {@link BubbleTextView} that represents a deep shortcut within an app.
+ * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
+ * This lets us animate the DeepShortcutView (icon and text) separately from the background.
*/
-public class DeepShortcutView extends BubbleTextView {
+public class DeepShortcutView extends FrameLayout {
+
+ private static final float HOVER_SCALE = 1.05f;
- private static final float HOVER_SCALE = 1.1f;
// The direction this view should translate when animating the hover state.
// This allows hovered shortcuts to "push" other shortcuts away.
@IntDef({DIRECTION_UP, DIRECTION_NONE, DIRECTION_DOWN})
@@ -37,12 +50,18 @@ public class DeepShortcutView extends BubbleTextView {
public static final int DIRECTION_UP = -1;
public static final int DIRECTION_NONE = 0;
public static final int DIRECTION_DOWN = 1;
+
@TranslationDirection
private int mTranslationDirection = DIRECTION_NONE;
private int mSpacing;
+ private int mRadius;
+ private Rect mPillRect;
private int mTop;
private boolean mIsHoveringOver = false;
+ private LauncherViewPropertyAnimator mHoverAnimator;
+
+ private BubbleTextView mBubbleText;
public DeepShortcutView(Context context) {
this(context, null, 0);
@@ -56,10 +75,76 @@ public class DeepShortcutView extends BubbleTextView {
super(context, attrs, defStyle);
mSpacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+ mRadius = getResources().getDimensionPixelSize(R.dimen.bg_pill_radius);
+ mPillRect = new Rect();
+ mHoverAnimator = new LauncherViewPropertyAnimator(this);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mBubbleText = (BubbleTextView) findViewById(R.id.deep_shortcut);
}
- public int getSpacing() {
- return mSpacing;
+ public BubbleTextView getBubbleText() {
+ return mBubbleText;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ @Override
+ public void setPivotX(float pivotX) {
+ super.setPivotX(pivotX);
+ mBubbleText.setPivotX(pivotX);
+ }
+
+ @Override
+ public void setPivotY(float pivotY) {
+ super.setPivotY(pivotY);
+ mBubbleText.setPivotY(pivotY);
+ }
+
+ /**
+ * 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) {
+ final Resources res = getResources();
+ setVisibility(INVISIBLE);
+
+ AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
+
+ Animator reveal = new PillRevealOutlineProvider((int) getPivotX(), (int) getPivotY(),
+ mPillRect, mRadius).createRevealAnimator(this);
+ reveal.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ }
+ });
+
+ float transY = res.getDimensionPixelSize(R.dimen.deep_shortcut_anim_translation_y);
+ Animator translationY = ObjectAnimator.ofFloat(this, TRANSLATION_Y,
+ isContainerAboveIcon ? transY : -transY, 0);
+
+ // Only scale mBubbleText (the icon and text, not the background).
+ mBubbleText.setScaleX(0);
+ mBubbleText.setScaleY(0);
+ LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mBubbleText)
+ .scaleX(1).scaleY(1);
+
+ openAnimation.playTogether(reveal, translationY, scale);
+ openAnimation.setStartDelay(animationIndex * res.getInteger(
+ R.integer.config_deepShortcutOpenStagger));
+ openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration));
+ openAnimation.setInterpolator(new DecelerateInterpolator());
+ return openAnimation;
}
/**
@@ -95,16 +180,16 @@ public class DeepShortcutView extends BubbleTextView {
/**
* If this shortcut is being hovered over, we scale it up. If another shortcut is being hovered
* over, we translate this one away from it to account for its increased size.
- *
- * TODO: apply motion spec here
*/
private void animateHoverState() {
+ if (mHoverAnimator.isRunning()) {
+ return;
+ }
float scale = mIsHoveringOver ? HOVER_SCALE : 1f;
- setScaleX(scale);
- setScaleY(scale);
-
- float translation = (HOVER_SCALE - 1f) * getHeight();
- setTranslationY(translation * mTranslationDirection);
+ float translateY = (HOVER_SCALE - 1f) * getHeight() * mTranslationDirection;
+ mHoverAnimator.scaleX(scale).scaleY(scale).translationY(translateY)
+ .setDuration(getResources().getInteger(R.integer.config_deepShortcutHoverDuration))
+ .start();
}
public boolean isHoveringOver() {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 70082f365..912f00622 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -1,6 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.launcher3.shortcuts;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
@@ -12,6 +30,7 @@ import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -22,9 +41,9 @@ import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LogDecelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
@@ -36,7 +55,6 @@ import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.UiThreadCircularReveal;
import java.util.Collections;
import java.util.Comparator;
@@ -66,6 +84,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
private Point mIconLastTouchPos = new Point();
private boolean mIsLeftAligned;
private boolean mIsAboveIcon;
+ private boolean mIsAnimatingOpen;
/**
* Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
@@ -107,11 +126,12 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
public void populateAndShow(final BubbleTextView originalIcon, final List<String> ids) {
// Add dummy views first, and populate with real shortcut info when ready.
+ final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+ final LayoutInflater inflator = mLauncher.getLayoutInflater();
for (int i = 0; i < ids.size(); i++) {
- final DeepShortcutView shortcut = (DeepShortcutView)
- mLauncher.getLayoutInflater().inflate(R.layout.deep_shortcut, this, false);
+ final View shortcut = inflator.inflate(R.layout.deep_shortcut, this, false);
if (i < ids.size() - 1) {
- ((LayoutParams) shortcut.getLayoutParams()).bottomMargin = shortcut.getSpacing();
+ ((LayoutParams) shortcut.getLayoutParams()).bottomMargin = spacing;
}
addView(shortcut);
}
@@ -165,7 +185,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
@Override
public void run() {
- DeepShortcutView shortcutView = (DeepShortcutView) getChildAt(mShortcutChildIndex);
+ BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText();
shortcutView.applyFromShortcutInfo(mShortcutChildInfo,
LauncherAppState.getInstance().getIconCache());
shortcutView.setText(mLabel);
@@ -175,19 +195,36 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
}
}
- // TODO: update this animation
+ private DeepShortcutView getShortcutAt(int index) {
+ return (DeepShortcutView) getChildAt(index);
+ }
+
private void animateOpen(BubbleTextView originalIcon) {
orientAboutIcon(originalIcon);
setVisibility(View.VISIBLE);
- int rx = (int) Math.max(Math.max(getMeasuredWidth() - getPivotX(), 0), getPivotX());
- int ry = (int) Math.max(Math.max(getMeasuredHeight() - getPivotY(), 0), getPivotY());
- float radius = (float) Math.hypot(rx, ry);
- Animator reveal = UiThreadCircularReveal.createCircularReveal(this, (int) getPivotX(),
- (int) getPivotY(), 0, radius);
- reveal.setDuration(getResources().getInteger(R.integer.config_materialFolderExpandDuration));
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- reveal.start();
+
+ 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 pivotY = getShortcutAt(0).getMeasuredHeight() / 2;
+ for (int i = 0; i < numShortcuts; i++) {
+ DeepShortcutView deepShortcutView = getShortcutAt(i);
+ deepShortcutView.setPivotX(pivotX);
+ deepShortcutView.setPivotY(pivotY);
+ int animationIndex = mIsAboveIcon ? numShortcuts - i - 1 : i;
+ shortcutAnims.play(deepShortcutView.createOpenAnimation(animationIndex, mIsAboveIcon));
+ }
+ shortcutAnims.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingOpen = false;
+ }
+ });
+ mIsAnimatingOpen = true;
+ shortcutAnims.start();
}
/**
@@ -202,7 +239,7 @@ 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 pointer based on orientation.
+ * TODO: draw arrow based on orientation.
*/
private void orientAboutIcon(BubbleTextView icon) {
int width = getMeasuredWidth();
@@ -225,9 +262,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
y = mTempRect.bottom;
}
- setPivotX(width / 2);
- setPivotY(height / 2);
-
// Insets are added later, so subtract them now.
y -= insets.top;
@@ -308,8 +342,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
} else {
// Determine whether touch is over a shortcut.
boolean hoveringOverShortcut = false;
- for (int i = 0; i < childCount; i++) {
- DeepShortcutView shortcut = (DeepShortcutView) getChildAt(i);
+ for (int i = 0; i < childCount && !mIsAnimatingOpen; i++) {
+ DeepShortcutView shortcut = getShortcutAt(i);
if (shortcut.updateHoverState(containerContainsTouch, hoveringOverShortcut, y)) {
hoveringOverShortcut = true;
}
@@ -333,9 +367,9 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
cleanupDeferredDrag();
// Launch a shortcut if user was hovering over it.
for (int i = 0; i < childCount; i++) {
- DeepShortcutView shortcut = (DeepShortcutView) getChildAt(i);
+ DeepShortcutView shortcut = getShortcutAt(i);
if (shortcut.isHoveringOver()) {
- shortcut.performClick();
+ shortcut.getBubbleText().performClick();
break;
}
}
diff --git a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java b/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
new file mode 100644
index 000000000..c19212019
--- /dev/null
+++ b/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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.util;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
+
+ private int mCenterX;
+ private int mCenterY;
+ private float mRadius0;
+ private float mRadius1;
+
+ /**
+ * @param x reveal center x
+ * @param y reveal center y
+ * @param r0 initial radius
+ * @param r1 final radius
+ */
+ public CircleRevealOutlineProvider(int x, int y, float r0, float r1) {
+ mCenterX = x;
+ mCenterY = y;
+ mRadius0 = r0;
+ mRadius1 = r1;
+ }
+
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return true;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mOutlineRadius = (1 - progress) * mRadius0 + progress * mRadius1;
+
+ mOutline.left = (int) (mCenterX - mOutlineRadius);
+ mOutline.top = (int) (mCenterY - mOutlineRadius);
+ mOutline.right = (int) (mCenterX + mOutlineRadius);
+ mOutline.bottom = (int) (mCenterY + mOutlineRadius);
+ }
+}
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
new file mode 100644
index 000000000..09ff9bda4
--- /dev/null
+++ b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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.util;
+
+import android.annotation.TargetApi;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.ViewOutlineProvider;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+/**
+ * A {@link ViewOutlineProvider} that animates a reveal in a "pill" shape.
+ * A pill is simply a round rect, but we assume the width is greater than
+ * the height and that the radius is equal to half the height.
+ */
+public class PillRevealOutlineProvider extends RevealOutlineAnimation {
+
+ private int mCenterX;
+ private int mCenterY;
+ private Rect mPillRect;
+
+ /**
+ * @param x reveal center x
+ * @param y reveal center y
+ * @param pillRect round rect that represents the final pill shape
+ * @param pillRectRadius radius of the round rect
+ */
+ public PillRevealOutlineProvider(int x, int y, Rect pillRect, float pillRectRadius) {
+ mCenterX = x;
+ mCenterY = y;
+ mPillRect = pillRect;
+ mOutlineRadius = pillRectRadius;
+ }
+
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return false;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ // Assumes width is greater than height.
+ int centerToEdge = Math.max(mCenterX, mPillRect.width() - mCenterX);
+ int currentSize = (int) (progress * centerToEdge);
+
+ // Bound the outline to the final pill shape defined by mPillRect.
+ mOutline.left = Math.max(mPillRect.left, mCenterX - currentSize);
+ mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
+ mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
+ mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
+ }
+}
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/util/RevealOutlineAnimation.java
new file mode 100644
index 000000000..4447c3ba9
--- /dev/null
+++ b/src/com/android/launcher3/util/RevealOutlineAnimation.java
@@ -0,0 +1,72 @@
+package com.android.launcher3.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
+ * This class should be extended so that subclasses can define the reveal shape as the
+ * animation progresses from 0 to 1.
+ */
+public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
+ protected Rect mOutline;
+ protected float mOutlineRadius;
+
+ public RevealOutlineAnimation() {
+ mOutline = new Rect();
+ }
+
+ /** Returns whether elevation should be removed for the duration of the reveal animation. */
+ abstract boolean shouldRemoveElevationDuringAnimation();
+ /** Sets the progress, from 0 to 1, of the reveal animation. */
+ abstract void setProgress(float progress);
+
+ public ValueAnimator createRevealAnimator(final View revealView) {
+ ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ final float elevation = revealView.getElevation();
+
+ va.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ revealView.setOutlineProvider(RevealOutlineAnimation.this);
+ revealView.setClipToOutline(true);
+ if (shouldRemoveElevationDuringAnimation()) {
+ revealView.setTranslationZ(-elevation);
+ }
+ }
+
+ public void onAnimationEnd(Animator animation) {
+ revealView.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
+ revealView.setClipToOutline(false);
+ if (shouldRemoveElevationDuringAnimation()) {
+ revealView.setTranslationZ(0);
+ }
+ }
+
+ });
+
+ va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator arg0) {
+ float progress = arg0.getAnimatedFraction();
+ setProgress(progress);
+ revealView.invalidateOutline();
+ if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
+ revealView.invalidate();
+ }
+ }
+ });
+ return va;
+ }
+
+ @Override
+ public void getOutline(View v, Outline outline) {
+ outline.setRoundRect(mOutline, mOutlineRadius);
+ }
+}
diff --git a/src/com/android/launcher3/util/RevealOutlineProvider.java b/src/com/android/launcher3/util/RevealOutlineProvider.java
deleted file mode 100644
index 0db3984f8..000000000
--- a/src/com/android/launcher3/util/RevealOutlineProvider.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.launcher3.util;
-
-import android.annotation.TargetApi;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class RevealOutlineProvider extends ViewOutlineProvider {
-
- private int mCenterX;
- private int mCenterY;
- private float mRadius0;
- private float mRadius1;
- private int mCurrentRadius;
-
- private final Rect mOval;
-
- /**
- * @param x reveal center x
- * @param y reveal center y
- * @param r0 initial radius
- * @param r1 final radius
- */
- public RevealOutlineProvider(int x, int y, float r0, float r1) {
- mCenterX = x;
- mCenterY = y;
- mRadius0 = r0;
- mRadius1 = r1;
-
- mOval = new Rect();
- }
-
- public void setProgress(float progress) {
- mCurrentRadius = (int) ((1 - progress) * mRadius0 + progress * mRadius1);
-
- mOval.left = mCenterX - mCurrentRadius;
- mOval.top = mCenterY - mCurrentRadius;
- mOval.right = mCenterX + mCurrentRadius;
- mOval.bottom = mCenterY + mCurrentRadius;
- }
-
- @Override
- public void getOutline(View v, Outline outline) {
- outline.setRoundRect(mOval, mCurrentRadius);
- }
-}
diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java
deleted file mode 100644
index f2b5e5e15..000000000
--- a/src/com/android/launcher3/util/UiThreadCircularReveal.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.android.launcher3.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.Utilities;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class UiThreadCircularReveal {
-
- public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1) {
- return createCircularReveal(v, x, y, r0, r1, ViewOutlineProvider.BACKGROUND);
- }
-
- public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1,
- final ViewOutlineProvider originalProvider) {
- ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
-
- final View revealView = v;
- final RevealOutlineProvider outlineProvider = new RevealOutlineProvider(x, y, r0, r1);
- final float elevation = v.getElevation();
-
- va.addListener(new AnimatorListenerAdapter() {
- public void onAnimationStart(Animator animation) {
- revealView.setOutlineProvider(outlineProvider);
- revealView.setClipToOutline(true);
- revealView.setTranslationZ(-elevation);
- }
-
- public void onAnimationEnd(Animator animation) {
- revealView.setOutlineProvider(originalProvider);
- revealView.setClipToOutline(false);
- revealView.setTranslationZ(0);
- }
-
- });
-
- va.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator arg0) {
- float progress = arg0.getAnimatedFraction();
- outlineProvider.setProgress(progress);
- revealView.invalidateOutline();
- if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
- revealView.invalidate();
- }
- }
- });
- return va;
- }
-}