Merge changes from topic "polish-app-recents-anim" into ub-launcher3-qt-dev
* changes: Polish app => recents animation (5/5) Polish app => recents animation (4/5) Polish app => recents animation (3/N) Polish app => recents animation (2/N) Polish app => recents animation (1/N)
This commit is contained in:
committed by
Android (Google) Code Review
commit
f079b277cf
@@ -15,30 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.quickstep;
|
package com.android.quickstep;
|
||||||
|
|
||||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
|
||||||
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
|
|
||||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||||
import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
|
import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION;
|
||||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||||
|
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.android.launcher3.BaseDraggingActivity;
|
import com.android.launcher3.BaseDraggingActivity;
|
||||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
|
||||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||||
import com.android.quickstep.views.IconRecentsView;
|
import com.android.quickstep.views.IconRecentsView;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
|
||||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for the atomic remote window animation from the app to the overview.
|
* Provider for the atomic remote window animation from the app to the overview.
|
||||||
@@ -47,9 +37,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.
|
|||||||
*/
|
*/
|
||||||
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
|
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
|
||||||
RemoteAnimationProvider {
|
RemoteAnimationProvider {
|
||||||
|
|
||||||
private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50;
|
|
||||||
private static final long APP_SCALE_DOWN_DURATION = 400;
|
|
||||||
private static final String TAG = "AppToOverviewAnimationProvider";
|
private static final String TAG = "AppToOverviewAnimationProvider";
|
||||||
|
|
||||||
private final ActivityControlHelper<T> mHelper;
|
private final ActivityControlHelper<T> mHelper;
|
||||||
@@ -131,106 +118,17 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
|
|||||||
return anim;
|
return anim;
|
||||||
}
|
}
|
||||||
|
|
||||||
View thumbnailView = mRecentsView.getBottomThumbnailView();
|
mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget);
|
||||||
if (thumbnailView == null) {
|
|
||||||
// This can be null if there were previously 0 tasks and the recycler view has not had
|
|
||||||
// enough time to take in the data change, bind a new view, and lay out the new view.
|
|
||||||
// TODO: Have a fallback to animate to
|
|
||||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
|
||||||
Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
|
|
||||||
}
|
|
||||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView);
|
|
||||||
|
|
||||||
return anim;
|
return anim;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Animate a closing app to scale down to the location of the thumbnail view in recents.
|
|
||||||
*
|
|
||||||
* @param anim animator set
|
|
||||||
* @param appTarget the app surface thats closing
|
|
||||||
* @param recentsTarget the surface containing recents
|
|
||||||
* @param thumbnailView the thumbnail view to animate to
|
|
||||||
*/
|
|
||||||
private void playAppScaleDownAnim(@NonNull AnimatorSet anim,
|
|
||||||
@NonNull RemoteAnimationTargetCompat appTarget,
|
|
||||||
@NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) {
|
|
||||||
|
|
||||||
// Identify where the entering remote app should animate to.
|
|
||||||
Rect endRect = new Rect();
|
|
||||||
thumbnailView.getGlobalVisibleRect(endRect);
|
|
||||||
|
|
||||||
Rect appBounds = appTarget.sourceContainerBounds;
|
|
||||||
|
|
||||||
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1);
|
|
||||||
valueAnimator.setDuration(APP_SCALE_DOWN_DURATION);
|
|
||||||
|
|
||||||
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
|
|
||||||
new SyncRtSurfaceTransactionApplierCompat(thumbnailView);
|
|
||||||
|
|
||||||
// Keep recents visible throughout the animation.
|
|
||||||
SurfaceParams[] params = new SurfaceParams[2];
|
|
||||||
// Closing app should stay on top.
|
|
||||||
int boostedMode = MODE_CLOSING;
|
|
||||||
params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
|
|
||||||
null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */);
|
|
||||||
|
|
||||||
valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
|
|
||||||
private final FloatProp mScaleX;
|
|
||||||
private final FloatProp mScaleY;
|
|
||||||
private final FloatProp mTranslationX;
|
|
||||||
private final FloatProp mTranslationY;
|
|
||||||
private final FloatProp mAlpha;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Scale down and move to view location.
|
|
||||||
float endScaleX = ((float) endRect.width()) / appBounds.width();
|
|
||||||
mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION,
|
|
||||||
ACCEL_DEACCEL);
|
|
||||||
float endScaleY = ((float) endRect.height()) / appBounds.height();
|
|
||||||
mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION,
|
|
||||||
ACCEL_DEACCEL);
|
|
||||||
float endTranslationX = endRect.left -
|
|
||||||
(appBounds.width() - thumbnailView.getWidth()) / 2.0f;
|
|
||||||
mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION,
|
|
||||||
ACCEL_DEACCEL);
|
|
||||||
float endTranslationY = endRect.top -
|
|
||||||
(appBounds.height() - thumbnailView.getHeight()) / 2.0f;
|
|
||||||
mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION,
|
|
||||||
ACCEL_DEACCEL);
|
|
||||||
|
|
||||||
// Fade out quietly near the end to be replaced by the real view.
|
|
||||||
mAlpha = new FloatProp(1.0f, 0,
|
|
||||||
APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION,
|
|
||||||
APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpdate(float percent) {
|
|
||||||
Matrix m = new Matrix();
|
|
||||||
m.setScale(mScaleX.value, mScaleY.value,
|
|
||||||
appBounds.width() / 2.0f, appBounds.height() / 2.0f);
|
|
||||||
m.postTranslate(mTranslationX.value, mTranslationY.value);
|
|
||||||
|
|
||||||
params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
|
|
||||||
null /* windowCrop */, getLayer(appTarget, boostedMode),
|
|
||||||
0 /* cornerRadius */);
|
|
||||||
surfaceApplier.scheduleApply(params);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
anim.play(valueAnimator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get duration of animation from app to overview.
|
* Get duration of animation from app to overview.
|
||||||
*
|
*
|
||||||
* @return duration of animation
|
* @return duration of animation
|
||||||
*/
|
*/
|
||||||
long getRecentsLaunchDuration() {
|
long getRecentsLaunchDuration() {
|
||||||
return APP_SCALE_DOWN_DURATION;
|
return REMOTE_APP_TO_OVERVIEW_DURATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public final class FallbackActivityControllerHelper extends
|
|||||||
@Override
|
@Override
|
||||||
public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
|
public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
|
||||||
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
|
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
|
||||||
|
// TODO: Logic for setting remote animation
|
||||||
if (activityVisible) {
|
if (activityVisible) {
|
||||||
return (transitionLength) -> { };
|
return (transitionLength) -> { };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public final class LauncherActivityControllerHelper extends GoActivityControlHel
|
|||||||
boolean activityVisible, boolean animateActivity,
|
boolean activityVisible, boolean animateActivity,
|
||||||
Consumer<AnimatorPlaybackController> callback) {
|
Consumer<AnimatorPlaybackController> callback) {
|
||||||
LauncherState fromState = activity.getStateManager().getState();
|
LauncherState fromState = activity.getStateManager().getState();
|
||||||
|
activity.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
|
||||||
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
|
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
|
||||||
return new AnimationFactory() {
|
return new AnimationFactory() {
|
||||||
@Override
|
@Override
|
||||||
@@ -87,6 +88,7 @@ public final class LauncherActivityControllerHelper extends GoActivityControlHel
|
|||||||
if (launcher == null) {
|
if (launcher == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
launcher.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(false);
|
||||||
launcher.getUserEventDispatcher().logActionCommand(
|
launcher.getUserEventDispatcher().logActionCommand(
|
||||||
LauncherLogProto.Action.Command.RECENTS_BUTTON,
|
LauncherLogProto.Action.Command.RECENTS_BUTTON,
|
||||||
getContainerType(),
|
getContainerType(),
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
|||||||
|
|
||||||
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
|
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
|
||||||
|
|
||||||
|
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||||
import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
|
import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
|
||||||
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
|
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
|
||||||
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK;
|
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK;
|
||||||
|
import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY;
|
||||||
import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
|
import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
|
||||||
|
import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
|
||||||
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -32,6 +36,7 @@ import android.animation.PropertyValuesHolder;
|
|||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -40,10 +45,12 @@ import android.util.FloatProperty;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewDebug;
|
import android.view.ViewDebug;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
|
import android.view.animation.PathInterpolator;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@@ -64,7 +71,11 @@ import com.android.quickstep.TaskAdapter;
|
|||||||
import com.android.quickstep.TaskHolder;
|
import com.android.quickstep.TaskHolder;
|
||||||
import com.android.quickstep.TaskListLoader;
|
import com.android.quickstep.TaskListLoader;
|
||||||
import com.android.quickstep.TaskSwipeCallback;
|
import com.android.quickstep.TaskSwipeCallback;
|
||||||
|
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||||
|
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -102,6 +113,22 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
|
private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
|
||||||
private static final long CLEAR_ALL_FADE_DELAY = 120;
|
private static final long CLEAR_ALL_FADE_DELAY = 120;
|
||||||
|
|
||||||
|
private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300;
|
||||||
|
private static final long REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION = 400;
|
||||||
|
private static final long REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY = 200;
|
||||||
|
private static final long REMOTE_TO_RECENTS_ITEM_FADE_DURATION = 217;
|
||||||
|
private static final long REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY = 33;
|
||||||
|
|
||||||
|
private static final PathInterpolator FAST_OUT_SLOW_IN_1 =
|
||||||
|
new PathInterpolator(.4f, 0f, 0f, 1f);
|
||||||
|
private static final PathInterpolator FAST_OUT_SLOW_IN_2 =
|
||||||
|
new PathInterpolator(.5f, 0f, 0f, 1f);
|
||||||
|
private static final LinearOutSlowInInterpolator OUT_SLOW_IN =
|
||||||
|
new LinearOutSlowInInterpolator();
|
||||||
|
|
||||||
|
public static final long REMOTE_APP_TO_OVERVIEW_DURATION =
|
||||||
|
REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ratio representing the view's relative placement within its padded space. For example, 0
|
* A ratio representing the view's relative placement within its padded space. For example, 0
|
||||||
* is top aligned and 0.5 is centered vertically.
|
* is top aligned and 0.5 is centered vertically.
|
||||||
@@ -125,6 +152,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
private View mEmptyView;
|
private View mEmptyView;
|
||||||
private View mContentView;
|
private View mContentView;
|
||||||
private boolean mTransitionedFromApp;
|
private boolean mTransitionedFromApp;
|
||||||
|
private boolean mUsingRemoteAnimation;
|
||||||
private AnimatorSet mLayoutAnimation;
|
private AnimatorSet mLayoutAnimation;
|
||||||
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
|
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
|
||||||
private Rect mInsets;
|
private Rect mInsets;
|
||||||
@@ -276,7 +304,9 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
// not be scrollable.
|
// not be scrollable.
|
||||||
mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */);
|
mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */);
|
||||||
}
|
}
|
||||||
scheduleFadeInLayoutAnimation();
|
if (!mUsingRemoteAnimation) {
|
||||||
|
scheduleFadeInLayoutAnimation();
|
||||||
|
}
|
||||||
// Load any task changes
|
// Load any task changes
|
||||||
if (!mTaskLoader.needsToLoad()) {
|
if (!mTaskLoader.needsToLoad()) {
|
||||||
return;
|
return;
|
||||||
@@ -314,6 +344,17 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
mTransitionedFromApp = transitionedFromApp;
|
mTransitionedFromApp = transitionedFromApp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether we're using a custom remote animation. If so, we will not do the default layout
|
||||||
|
* animation when entering recents and instead wait for the remote app surface to be ready to
|
||||||
|
* use.
|
||||||
|
*
|
||||||
|
* @param usingRemoteAnimation true if doing a remote animation, false o/w
|
||||||
|
*/
|
||||||
|
public void setUsingRemoteAnimation(boolean usingRemoteAnimation) {
|
||||||
|
mUsingRemoteAnimation = usingRemoteAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles input from the overview button. Launch the most recent task unless we just came from
|
* Handles input from the overview button. Launch the most recent task unless we just came from
|
||||||
* the app. In that case, we launch the next most recent.
|
* the app. In that case, we launch the next most recent.
|
||||||
@@ -365,17 +406,19 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the bottom most thumbnail view to animate to.
|
* Get the bottom most task view to animate to.
|
||||||
*
|
*
|
||||||
* @return the thumbnail view if laid out
|
* @return the task view
|
||||||
*/
|
*/
|
||||||
public @Nullable View getBottomThumbnailView() {
|
private @Nullable TaskItemView getBottomTaskView() {
|
||||||
ArrayList<TaskItemView> taskViews = getTaskViews();
|
int childCount = mTaskRecyclerView.getChildCount();
|
||||||
if (taskViews.isEmpty()) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
return null;
|
View view = mTaskRecyclerView.getChildAt(i);
|
||||||
|
if (mTaskRecyclerView.getChildViewHolder(view).getItemViewType() == ITEM_TYPE_TASK) {
|
||||||
|
return (TaskItemView) view;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TaskItemView view = taskViews.get(0);
|
return null;
|
||||||
return view.getThumbnailView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -581,6 +624,205 @@ public final class IconRecentsView extends FrameLayout implements Insettable {
|
|||||||
mLayoutAnimation.start();
|
mLayoutAnimation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play remote animation from app to recents. This should scale the currently closing app down
|
||||||
|
* to the recents thumbnail.
|
||||||
|
*
|
||||||
|
* @param anim animator set
|
||||||
|
* @param appTarget the app surface thats closing
|
||||||
|
* @param recentsTarget the surface containing recents
|
||||||
|
*/
|
||||||
|
public void playRemoteAppToRecentsAnimation(@NonNull AnimatorSet anim,
|
||||||
|
@NonNull RemoteAnimationTargetCompat appTarget,
|
||||||
|
@NonNull RemoteAnimationTargetCompat recentsTarget) {
|
||||||
|
TaskItemView bottomView = getBottomTaskView();
|
||||||
|
if (bottomView == null) {
|
||||||
|
// This can be null if there were previously 0 tasks and the recycler view has not had
|
||||||
|
// enough time to take in the data change, bind a new view, and lay out the new view.
|
||||||
|
// TODO: Have a fallback to animate to
|
||||||
|
anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION));
|
||||||
|
}
|
||||||
|
final Matrix appMatrix = new Matrix();
|
||||||
|
playRemoteTransYAnim(anim, appMatrix);
|
||||||
|
playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget,
|
||||||
|
bottomView.getThumbnailView());
|
||||||
|
playRemoteTaskListFadeIn(anim, bottomView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play translation Y animation for the remote app to recents animation. Animates over all task
|
||||||
|
* views as well as the closing app, easing them into their final vertical positions.
|
||||||
|
*
|
||||||
|
* @param anim animator set to play on
|
||||||
|
* @param appMatrix transformation matrix for the closing app surface
|
||||||
|
*/
|
||||||
|
private void playRemoteTransYAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix) {
|
||||||
|
final ArrayList<TaskItemView> views = getTaskViews();
|
||||||
|
|
||||||
|
// Start Y translation from about halfway through the tasks list to the bottom thumbnail.
|
||||||
|
float taskHeight = getResources().getDimension(R.dimen.task_item_height);
|
||||||
|
float totalTransY = -(MAX_TASKS_TO_DISPLAY / 2.0f - 1) * taskHeight;
|
||||||
|
for (int i = 0, size = views.size(); i < size; i++) {
|
||||||
|
views.get(i).setTranslationY(totalTransY);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueAnimator transYAnim = ValueAnimator.ofFloat(totalTransY, 0);
|
||||||
|
transYAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
|
||||||
|
transYAnim.setInterpolator(FAST_OUT_SLOW_IN_2);
|
||||||
|
transYAnim.addUpdateListener(valueAnimator -> {
|
||||||
|
float transY = (float) valueAnimator.getAnimatedValue();
|
||||||
|
for (int i = 0, size = views.size(); i < size; i++) {
|
||||||
|
views.get(i).setTranslationY(transY);
|
||||||
|
}
|
||||||
|
appMatrix.postTranslate(0, transY - totalTransY);
|
||||||
|
});
|
||||||
|
transYAnim.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
for (int i = 0, size = views.size(); i < size; i++) {
|
||||||
|
views.get(i).setTranslationY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
anim.play(transYAnim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the scale down animation for the remote app to recents animation where the app surface
|
||||||
|
* scales down to where the thumbnail is.
|
||||||
|
*
|
||||||
|
* @param anim animator set to play on
|
||||||
|
* @param appMatrix transformation matrix for the app surface
|
||||||
|
* @param appTarget closing app target
|
||||||
|
* @param recentsTarget opening recents target
|
||||||
|
* @param thumbnailView thumbnail view to animate to
|
||||||
|
*/
|
||||||
|
private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix,
|
||||||
|
@NonNull RemoteAnimationTargetCompat appTarget,
|
||||||
|
@NonNull RemoteAnimationTargetCompat recentsTarget,
|
||||||
|
@NonNull View thumbnailView) {
|
||||||
|
// Identify where the entering remote app should animate to.
|
||||||
|
Rect endRect = new Rect();
|
||||||
|
thumbnailView.getGlobalVisibleRect(endRect);
|
||||||
|
Rect appBounds = appTarget.sourceContainerBounds;
|
||||||
|
|
||||||
|
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
|
||||||
|
new SyncRtSurfaceTransactionApplierCompat(this);
|
||||||
|
|
||||||
|
// Keep recents visible throughout the animation.
|
||||||
|
SurfaceParams[] params = new SurfaceParams[2];
|
||||||
|
// Closing app should stay on top.
|
||||||
|
int boostedMode = MODE_CLOSING;
|
||||||
|
params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
|
||||||
|
null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */);
|
||||||
|
|
||||||
|
ValueAnimator remoteAppAnim = ValueAnimator.ofInt(0, 1);
|
||||||
|
remoteAppAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
|
||||||
|
remoteAppAnim.addUpdateListener(new MultiValueUpdateListener() {
|
||||||
|
private final FloatProp mScaleX;
|
||||||
|
private final FloatProp mScaleY;
|
||||||
|
private final FloatProp mTranslationX;
|
||||||
|
private final FloatProp mTranslationY;
|
||||||
|
private final FloatProp mAlpha;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Scale down and move to view location.
|
||||||
|
float endScaleX = ((float) endRect.width()) / appBounds.width();
|
||||||
|
mScaleX = new FloatProp(1f, endScaleX, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||||
|
FAST_OUT_SLOW_IN_1);
|
||||||
|
float endScaleY = ((float) endRect.height()) / appBounds.height();
|
||||||
|
mScaleY = new FloatProp(1f, endScaleY, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||||
|
FAST_OUT_SLOW_IN_1);
|
||||||
|
float endTranslationX = endRect.left -
|
||||||
|
(appBounds.width() - thumbnailView.getWidth()) / 2.0f;
|
||||||
|
mTranslationX = new FloatProp(0, endTranslationX, 0,
|
||||||
|
REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_1);
|
||||||
|
float endTranslationY = endRect.top -
|
||||||
|
(appBounds.height() - thumbnailView.getHeight()) / 2.0f;
|
||||||
|
mTranslationY = new FloatProp(0, endTranslationY, 0,
|
||||||
|
REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_2);
|
||||||
|
mAlpha = new FloatProp(1.0f, 0, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
|
||||||
|
ACCEL_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(float percent) {
|
||||||
|
appMatrix.preScale(mScaleX.value, mScaleY.value,
|
||||||
|
appBounds.width() / 2.0f, appBounds.height() / 2.0f);
|
||||||
|
appMatrix.postTranslate(mTranslationX.value, mTranslationY.value);
|
||||||
|
|
||||||
|
params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, appMatrix,
|
||||||
|
null /* windowCrop */, getLayer(appTarget, boostedMode),
|
||||||
|
0 /* cornerRadius */);
|
||||||
|
surfaceApplier.scheduleApply(params);
|
||||||
|
appMatrix.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
anim.play(remoteAppAnim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play task list fade in animation as part of remote app to recents animation. This animation
|
||||||
|
* ensures that the task views in the recents list fade in from bottom to top.
|
||||||
|
*
|
||||||
|
* @param anim animator set to play on
|
||||||
|
* @param appTaskView the task view associated with the remote app closing
|
||||||
|
*/
|
||||||
|
private void playRemoteTaskListFadeIn(@NonNull AnimatorSet anim,
|
||||||
|
@NonNull TaskItemView appTaskView) {
|
||||||
|
long delay = REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY;
|
||||||
|
int childCount = mTaskRecyclerView.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
ValueAnimator fadeAnim = ValueAnimator.ofFloat(0, 1.0f);
|
||||||
|
fadeAnim.setDuration(REMOTE_TO_RECENTS_ITEM_FADE_DURATION).setInterpolator(OUT_SLOW_IN);
|
||||||
|
fadeAnim.setStartDelay(delay);
|
||||||
|
View view = mTaskRecyclerView.getChildAt(i);
|
||||||
|
if (Objects.equals(view, appTaskView)) {
|
||||||
|
// Only animate icon and text for the view with snapshot animating in
|
||||||
|
final View icon = appTaskView.getIconView();
|
||||||
|
final View label = appTaskView.getLabelView();
|
||||||
|
|
||||||
|
icon.setAlpha(0.0f);
|
||||||
|
label.setAlpha(0.0f);
|
||||||
|
|
||||||
|
fadeAnim.addUpdateListener(alphaVal -> {
|
||||||
|
float val = alphaVal.getAnimatedFraction();
|
||||||
|
|
||||||
|
icon.setAlpha(val);
|
||||||
|
label.setAlpha(val);
|
||||||
|
});
|
||||||
|
fadeAnim.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
icon.setAlpha(1.0f);
|
||||||
|
label.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Otherwise, fade in the entire view.
|
||||||
|
view.setAlpha(0.0f);
|
||||||
|
fadeAnim.addUpdateListener(alphaVal -> {
|
||||||
|
float val = alphaVal.getAnimatedFraction();
|
||||||
|
view.setAlpha(val);
|
||||||
|
});
|
||||||
|
fadeAnim.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
view.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
anim.play(fadeAnim);
|
||||||
|
|
||||||
|
int itemType = mTaskRecyclerView.getChildViewHolder(view).getItemViewType();
|
||||||
|
if (itemType == ITEM_TYPE_CLEAR_ALL) {
|
||||||
|
// Don't add delay. Clear all should animate at same time as next view.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
delay += REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInsets(Rect insets) {
|
public void setInsets(Rect insets) {
|
||||||
mInsets = insets;
|
mInsets = insets;
|
||||||
|
|||||||
@@ -137,6 +137,14 @@ public final class TaskItemView extends LinearLayout {
|
|||||||
return mThumbnailView;
|
return mThumbnailView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public View getIconView() {
|
||||||
|
return mIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getLabelView() {
|
||||||
|
return mLabelView;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a new animation from the current task content to the specified new content. The caller
|
* Start a new animation from the current task content to the specified new content. The caller
|
||||||
* is responsible for the actual animation control via the property
|
* is responsible for the actual animation control via the property
|
||||||
|
|||||||
Reference in New Issue
Block a user