Updating gesture tutorial home animation
> Using a separate View as icon, instead of the taskView
> Updating swipe animation logic to abstract out FloatingIconView dependency
Change-Id: Ib466262afead11ebe4ca035d589f0382c37e3e97
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index fc7a119..1909f47 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -140,7 +140,6 @@
private final long mDuration;
FallbackHomeAnimationFactory(long duration) {
- super(null);
mDuration = duration;
if (mRunningOverHome) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index fa7d268..052d0a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.AnimatorSet;
import android.content.Context;
@@ -28,6 +29,7 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -72,36 +74,39 @@
mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive();
- homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
-
- @Override
- public RectF getWindowTargetRect() {
- if (canUseWorkspaceView) {
+ if (canUseWorkspaceView) {
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+ homeAnimFactory = new LauncherHomeAnimationFactory() {
+ @Override
+ public RectF getWindowTargetRect() {
return iconLocation;
- } else {
- return super.getWindowTargetRect();
}
- }
- @NonNull
- @Override
- public AnimatorPlaybackController createActivityAnimationToHome() {
- // Return an empty APC here since we have an non-user controlled animation
- // to home.
- long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
- return mActivity.getStateManager().createAnimationToNewWorkspace(
- NORMAL, accuracy, 0 /* animComponents */);
- }
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ anim.addAnimatorListener(floatingIconView);
+ floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ floatingIconView.setFastFinishRunnable(anim::end);
+ }
- @Override
- public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(mActivity, velocity,
- true /* animateOverviewScrim */).start();
- }
- };
+ @Override
+ public void update(RectF currentRect, float progress, float radius) {
+ floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
+ radius, false);
+ }
+ @Override
+ public void onCancel() {
+ floatingIconView.fastFinish();
+ }
+ };
+ } else {
+ homeAnimFactory = new LauncherHomeAnimationFactory();
+ }
} else {
- homeAnimFactory = new HomeAnimationFactory(null) {
+ homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -118,4 +123,22 @@
mRecentsAnimationController.finish(
true /* toRecents */, callback, true /* sendUserLeaveHint */);
}
+
+ private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ // Return an empty APC here since we have an non-user controlled animation
+ // to home.
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mActivity.getStateManager().createAnimationToNewWorkspace(
+ NORMAL, accuracy, 0 /* animComponents */);
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ new StaggeredWorkspaceAnim(mActivity, velocity,
+ true /* animateOverviewScrim */).start();
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index dc8f1c5..2962234 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
import android.content.Context;
@@ -28,7 +27,6 @@
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
@@ -37,7 +35,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -148,12 +145,6 @@
protected abstract class HomeAnimationFactory {
- public FloatingIconView mIconView;
-
- public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
- mIconView = iconView;
- }
-
public @NonNull RectF getWindowTargetRect() {
PagedOrientationHandler orientationHandler = getOrientationHandler();
DeviceProfile dp = mDp;
@@ -174,6 +165,12 @@
public void playAtomicAnimation(float velocity) {
// No-op
}
+
+ public void setAnimation(RectFSpringAnim anim) { }
+
+ public void update(RectF currentRect, float progress, float radius) { }
+
+ public void onCancel() { }
}
/**
@@ -184,8 +181,6 @@
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- final FloatingIconView fiv = homeAnimationFactory.mIconView;
- final boolean isFloatingIconView = fiv != null;
mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
@@ -203,11 +198,7 @@
windowToHomePositionMap.mapRect(startRect);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
- if (isFloatingIconView) {
- anim.addAnimatorListener(fiv);
- fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
- fiv.setFastFinishRunnable(anim::end);
- }
+ homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
@@ -242,32 +233,27 @@
final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap;
+ final HomeAnimationFactory mAnimationFactory;
- final FloatingIconView mFIV;
final AnimatorPlaybackController mHomeAnim;
final RectF mCropRectF;
final float mStartRadius;
final float mEndRadius;
- final float mWindowAlphaThreshold;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
Matrix homeToWindowPositionMap) {
+ mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap;
cropRectF.roundOut(mCropRect);
- mFIV = factory.mIconView;
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
mEndRadius = cropRectF.width() / 2f;
-
- // We want the window alpha to be 0 once this threshold is met, so that the
- // FolderIconView can be seen morphing into the icon shape.
- mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
}
@Override
@@ -282,10 +268,7 @@
.setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
- if (mFIV != null) {
- mFIV.update(currentRect, 1f, progress,
- mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
- }
+ mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
}
@Override
@@ -298,9 +281,7 @@
@Override
public void onCancel() {
- if (mFIV != null) {
- mFIV.fastFinish();
- }
+ mAnimationFactory.onCancel();
}
@Override
diff --git a/quickstep/res/drawable/bg_circle.xml b/quickstep/res/drawable/bg_circle.xml
new file mode 100644
index 0000000..506177b
--- /dev/null
+++ b/quickstep/res/drawable/bg_circle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="#FFFFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 459d65f..43bf0ea 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -24,6 +24,14 @@
android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/>
+ <com.android.launcher3.views.ClipIconView
+ android:id="@+id/gesture_tutorial_fake_icon_view"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:background="@drawable/bg_circle"
+ android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
+ android:visibility="invisible" />
+
<View
android:id="@+id/gesture_tutorial_fake_task_view"
android:layout_width="match_parent"
@@ -41,81 +49,81 @@
android:id="@+id/gesture_tutorial_fragment_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="18dp"
- android:layout_marginTop="30dp"
- android:layout_marginStart="4dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
- android:background="@android:color/transparent"
+ android:layout_marginStart="4dp"
+ android:layout_marginTop="30dp"
android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container"
+ android:background="@android:color/transparent"
android:contentDescription="@string/gesture_tutorial_close_button_content_description"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/gesture_tutorial_close_button"/>
+ android:padding="18dp"
+ android:src="@drawable/gesture_tutorial_close_button"
+ android:tint="?android:attr/textColorPrimary"/>
<LinearLayout
android:id="@+id/gesture_tutorial_fragment_titles_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="70dp"
android:layout_alignParentTop="true"
+ android:layout_marginTop="70dp"
android:focusable="true"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/gesture_tutorial_fragment_title_view"
+ style="@style/TextAppearance.GestureTutorial.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end"
- android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Title"/>
+ android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_subtitle_view"
+ style="@style/TextAppearance.GestureTutorial.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end"
- android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Subtitle"/>
+ android:layout_marginTop="10dp"
+ android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
</LinearLayout>
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_view"
+ style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="10dp"
- android:layout_centerHorizontal="true"
android:layout_above="@id/gesture_tutorial_fragment_action_button"
+ android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
- style="@style/TextAppearance.GestureTutorial.Feedback"/>
+ android:layout_marginBottom="10dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button
android:id="@+id/gesture_tutorial_fragment_action_button"
+ style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
- android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
- android:layout_marginBottom="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
- android:stateListAnimator="@null"
+ android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
+ android:layout_marginBottom="48dp"
android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
- style="@style/TextAppearance.GestureTutorial.ButtonLabel"/>
+ android:stateListAnimator="@null"/>
<Button
android:id="@+id/gesture_tutorial_fragment_action_text_button"
+ style="@style/TextAppearance.GestureTutorial.TextButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
- android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
- android:layout_marginBottom="48dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
- android:stateListAnimator="@null"
+ android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
+ android:layout_marginBottom="48dp"
android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
- style="@style/TextAppearance.GestureTutorial.TextButtonLabel"/>
+ android:stateListAnimator="@null"/>
</RelativeLayout>
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 14e00dc..4110b33 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
@@ -110,6 +111,7 @@
AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
+ mFakeIconView.setVisibility(View.INVISIBLE);
mFakeTaskView.setVisibility(View.INVISIBLE);
mFakeTaskView.setAlpha(1);
mRunningWindowAnim = null;
@@ -131,6 +133,7 @@
});
} else {
anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+ anim.setViewAlpha(mFakeIconView, 0, ACCEL);
anim.addListener(resetTaskView);
}
if (onEndRunnable != null) {
@@ -202,7 +205,7 @@
// derivative of the scroll interpolator at zero, ie. 2.
long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
- HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) {
+ HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -218,6 +221,24 @@
fakeHomeIconLeft + fakeHomeIconSizePx,
fakeHomeIconTop + fakeHomeIconSizePx);
}
+
+ @Override
+ public void update(RectF rect, float progress, float radius) {
+ mFakeIconView.setVisibility(View.VISIBLE);
+ mFakeIconView.update(rect, progress,
+ 1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
+ radius,
+ false, /* isOpening */
+ mFakeIconView, mDp,
+ false /* isVerticalBarLayout */);
+ mFakeIconView.setAlpha(1);
+ mFakeTaskView.setAlpha(getWindowAlpha(progress));
+ }
+
+ @Override
+ public void onCancel() {
+ mFakeIconView.setVisibility(View.INVISIBLE);
+ }
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
windowAnim.start(mContext, velocityPxPerMs);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index c1918c2..73f1f8c 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -28,6 +28,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -46,6 +47,7 @@
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final TextView mFeedbackView;
+ final ClipIconView mFakeIconView;
final View mFakeTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
@@ -66,6 +68,7 @@
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();