Merge "Add new app close implementation." into sc-dev

This commit is contained in:
Jonathan Miranda
2021-04-22 22:17:02 +00:00
committed by Android (Google) Code Review
17 changed files with 723 additions and 80 deletions
@@ -693,7 +693,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
matrix.postTranslate(windowTransX0, windowTransY0);
}
floatingView.update(floatingIconBounds, mIconAlpha.value, percent, 0f,
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
builder.withMatrix(matrix)
.withWindowCrop(crop)
@@ -1252,7 +1252,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
HomeAnimationFactory homeAnimationFactory) {
RectFSpringAnim anim =
super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
anim.addOnUpdateListener((r, p) -> {
anim.addOnUpdateListener((v, r, p) -> {
updateSysUiFlags(Math.max(p, mCurrentShift.value));
});
anim.addAnimatorListener(new AnimationSuccessListener() {
@@ -48,6 +48,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
@@ -56,6 +57,7 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
@@ -298,7 +300,8 @@ public class FallbackSwipeHandler extends
}
@Override
public void update(RectF currentRect, float progress, float radius) {
public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
float radius) {
if (mSurfaceControl != null) {
currentRect.roundOut(mTempRect);
Transaction t = new Transaction();
@@ -15,26 +15,45 @@
*/
package com.android.quickstep;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.dpToPx;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.RectF;
import android.os.UserHandle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.system.InputConsumerController;
/**
@@ -66,24 +85,41 @@ public class LauncherSwipeHandlerV2 extends
} else {
workspaceView = null;
}
final RectF iconLocation = new RectF();
boolean canUseWorkspaceView =
workspaceView != null && workspaceView.isAttachedToWindow();
FloatingIconView floatingIconView = canUseWorkspaceView
? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null;
mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive();
if (canUseWorkspaceView) {
final ResourceProvider rp = DynamicResource.provider(mActivity);
final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
float dpPerSecond = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp_per_s));
final float launcherAlphaMax =
rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress);
RectF iconLocation = new RectF();
FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */);
// 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() {
// There is a delay in loading the icon, so we need to keep the window
// opaque until it is ready.
private boolean mIsFloatingIconReady = false;
private @Nullable ValueAnimator mBounceBackAnimator;
@Override
public RectF getWindowTargetRect() {
if (PROTOTYPE_APP_CLOSE.get()) {
// We want the target rect to be at this offset position, so that all
// launcher content can spring back upwards.
floatingIconView.setPositionOffsetY(transY);
}
return iconLocation;
}
@@ -92,17 +128,92 @@ public class LauncherSwipeHandlerV2 extends
anim.addAnimatorListener(floatingIconView);
floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
floatingIconView.setFastFinishRunnable(anim::end);
if (PROTOTYPE_APP_CLOSE.get()) {
mBounceBackAnimator = bounceBackToRestingPosition();
// Use a spring to put drag layer translation back to 0.
anim.addAnimatorListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
floatingIconView.setPositionOffsetY(0);
mBounceBackAnimator.start();
}
});
Workspace workspace = mActivity.getWorkspace();
workspace.setPivotToScaleWithSelf(mActivity.getHotseat());
}
}
private ValueAnimator bounceBackToRestingPosition() {
DragLayer dl = mActivity.getDragLayer();
Workspace workspace = mActivity.getWorkspace();
Hotseat hotseat = mActivity.getHotseat();
final float startValue = transY;
final float endValue = 0;
// Ensures the velocity is always aligned with the direction.
float pixelPerSecond = Math.abs(dpPerSecond)
* Math.signum(endValue - transY);
ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
.setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
.setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
.setMinimumVisibleChange(1f)
.setStartValue(startValue)
.setEndValue(endValue)
.setStartVelocity(pixelPerSecond)
.build(dl, VIEW_TRANSLATE_Y);
springTransY.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dl.setTranslationY(0f);
dl.setAlpha(1f);
SCALE_PROPERTY.set(workspace, 1f);
SCALE_PROPERTY.set(hotseat, 1f);
}
});
return springTransY;
}
@Override
public void update(RectF currentRect, float progress, float radius) {
floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
radius, false);
public boolean keepWindowOpaque() {
if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) {
mIsFloatingIconReady = true;
return false;
}
return true;
}
@Override
public void update(@Nullable AppCloseConfig config, RectF currentRect,
float progress, float radius) {
int fgAlpha = 255;
if (config != null && PROTOTYPE_APP_CLOSE.get()) {
DragLayer dl = mActivity.getDragLayer();
float translationY = config.getWorkspaceTransY();
dl.setTranslationY(translationY);
float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR);
dl.setAlpha(Math.min(alpha, 1f));
float scale = Math.min(1f, config.getWorkspaceScale());
SCALE_PROPERTY.set(mActivity.getWorkspace(), scale);
SCALE_PROPERTY.set(mActivity.getHotseat(), scale);
SCALE_PROPERTY.set(mActivity.getAppsView(), scale);
progress = config.getInterpolatedProgress();
fgAlpha = config.getFgAlpha();
}
floatingIconView.update(1f, fgAlpha, currentRect, progress,
windowAlphaThreshold, radius, false);
}
@Override
public void onCancel() {
floatingIconView.fastFinish();
if (mBounceBackAnimator != null) {
mBounceBackAnimator.cancel();
}
}
};
} else {
@@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
import android.animation.Animator;
import android.content.Context;
@@ -26,6 +27,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
@@ -35,7 +37,9 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RectFSpringAnim2;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
@@ -149,7 +153,10 @@ public abstract class SwipeUpAnimationLogic {
public void setAnimation(RectFSpringAnim anim) { }
public void update(RectF currentRect, float progress, float radius) { }
public boolean keepWindowOpaque() { return false; }
public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
float radius) { }
public void onCancel() { }
@@ -199,7 +206,14 @@ public abstract class SwipeUpAnimationLogic {
homeToWindowPositionMap.invert(windowToHomePositionMap);
windowToHomePositionMap.mapRect(startRect);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
RectFSpringAnim anim;
if (PROTOTYPE_APP_CLOSE.get()) {
anim = new RectFSpringAnim2(startRect, targetRect, mContext,
mTaskViewSimulator.getCurrentCornerRadius(),
cropRectF.width() / 2f);
} else {
anim = new RectFSpringAnim(startRect, targetRect, mContext);
}
homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
@@ -259,18 +273,26 @@ public abstract class SwipeUpAnimationLogic {
}
@Override
public void onUpdate(RectF currentRect, float progress) {
public void onUpdate(@Nullable AppCloseConfig config, RectF currentRect, float progress) {
mHomeAnim.setPlayFraction(progress);
mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
float alpha = getWindowAlpha(progress);
if (config != null && PROTOTYPE_APP_CLOSE.get()) {
alpha = config.getWindowAlpha();
cornerRadius = config.getCornerRadius();
}
if (mAnimationFactory.keepWindowOpaque()) {
alpha = 1f;
}
mTransformParams
.setTargetAlpha(getWindowAlpha(progress))
.setTargetAlpha(alpha)
.setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
mAnimationFactory.update(config, currentRect, progress,
mMatrix.mapRadius(cornerRadius));
}
@Override
@@ -52,6 +52,7 @@ import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -306,11 +307,12 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
}
@Override
public void update(RectF rect, float progress, float radius) {
public void update(@Nullable AppCloseConfig config, RectF rect, float progress,
float radius) {
mFakeIconView.setVisibility(View.VISIBLE);
mFakeIconView.update(rect, progress,
1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
radius,
radius, 255,
false, /* isOpening */
mFakeIconView, mDp,
false /* isVerticalBarLayout */);
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 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.quickstep.util;
import android.annotation.FloatRange;
import android.annotation.IntRange;
/*
* Adds getter methods to {@link MultiValueUpdateListener} specific to app close animation,
* so that the entire animation can be defined in one place.
*/
public abstract class AppCloseConfig extends MultiValueUpdateListener {
/**
* Returns the translation y of the workspace contents.
*/
public abstract float getWorkspaceTransY();
/*
* Returns the scale of the workspace contents.
*/
public abstract float getWorkspaceScale();
/*
* Returns the alpha of the window.
*/
public abstract @FloatRange(from = 0, to = 1) float getWindowAlpha();
/*
* Returns the alpha of the foreground layer of an adaptive icon.
*/
public abstract @IntRange(from = 0, to = 255) int getFgAlpha();
/*
* Returns the corner radius of the window and icon.
*/
public abstract float getCornerRadius();
/*
* Returns the interpolated progress of the animation.
*/
public abstract float getInterpolatedProgress();
}
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -241,7 +242,7 @@ public class RectFSpringAnim extends ReleaseCheck {
mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight);
}
for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
onUpdateListener.onUpdate(mCurrentRect, mCurrentScaleProgress);
onUpdateListener.onUpdate(null, mCurrentRect, mCurrentScaleProgress);
}
}
}
@@ -266,7 +267,7 @@ public class RectFSpringAnim extends ReleaseCheck {
}
public interface OnUpdateListener {
void onUpdate(RectF currentRect, float progress);
void onUpdate(@Nullable AppCloseConfig values, RectF currentRect, float progress);
default void onCancel() { }
}
@@ -0,0 +1,390 @@
/*
* Copyright (C) 2021 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.quickstep.util;
import static com.android.launcher3.Utilities.dpToPx;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.PathParser;
import android.util.Property;
import android.view.animation.Interpolator;
import androidx.core.view.animation.PathInterpolatorCompat;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DynamicResource;
import com.android.systemui.plugins.ResourceProvider;
import java.util.ArrayList;
import java.util.List;
/**
* Applies spring forces to animate from a starting rect to a target rect,
* while providing update callbacks to the caller.
*/
public class RectFSpringAnim2 extends RectFSpringAnim {
private static final FloatPropertyCompat<RectFSpringAnim2> RECT_CENTER_X =
new FloatPropertyCompat<RectFSpringAnim2>("rectCenterXSpring") {
@Override
public float getValue(RectFSpringAnim2 anim) {
return anim.mCurrentCenterX;
}
@Override
public void setValue(RectFSpringAnim2 anim, float currentCenterX) {
anim.mCurrentCenterX = currentCenterX;
anim.onUpdate();
}
};
private static final FloatPropertyCompat<RectFSpringAnim2> RECT_Y =
new FloatPropertyCompat<RectFSpringAnim2>("rectYSpring") {
@Override
public float getValue(RectFSpringAnim2 anim) {
return anim.mCurrentY;
}
@Override
public void setValue(RectFSpringAnim2 anim, float y) {
anim.mCurrentY = y;
anim.onUpdate();
}
};
private static final Property<RectFSpringAnim2, Float> PROGRESS =
new Property<RectFSpringAnim2, Float>(Float.class, "rectFProgress") {
@Override
public Float get(RectFSpringAnim2 rectFSpringAnim) {
return rectFSpringAnim.mProgress;
}
@Override
public void set(RectFSpringAnim2 rectFSpringAnim, Float progress) {
rectFSpringAnim.mProgress = progress;
rectFSpringAnim.onUpdate();
}
};
private final RectF mStartRect;
private final RectF mTargetRect;
private final RectF mCurrentRect = new RectF();
private final List<OnUpdateListener> mOnUpdateListeners = new ArrayList<>();
private final List<Animator.AnimatorListener> mAnimatorListeners = new ArrayList<>();
private float mCurrentCenterX;
private float mCurrentY;
private float mTargetX;
private float mTargetY;
// If true, tracking the bottom of the rects, else tracking the top.
private final boolean mTrackingBottomY;
private float mProgress;
private SpringAnimation mRectXAnim;
private SpringAnimation mRectYAnim;
private ValueAnimator mRectScaleAnim;
private boolean mAnimsStarted;
private boolean mRectXAnimEnded;
private boolean mRectYAnimEnded;
private boolean mRectScaleAnimEnded;
private final float mXDamping;
private final float mXStiffness;
private final float mYDamping;
private float mYStiffness;
private long mDuration;
private final Interpolator mCloseInterpolator;
private AppCloseConfig mValues;
final float mStartRadius;
final float mEndRadius;
final float mHomeTransYEnd;
final float mScaleStart;
public RectFSpringAnim2(RectF startRect, RectF targetRect, Context context, float startRadius,
float endRadius) {
super(startRect, targetRect, context);
mStartRect = startRect;
mTargetRect = targetRect;
mTrackingBottomY = startRect.bottom < targetRect.bottom;
mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top;
mCurrentCenterX = mStartRect.centerX();
mTargetY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top;
mTargetX = mTargetRect.centerX();
ResourceProvider rp = DynamicResource.provider(context);
mXDamping = rp.getFloat(R.dimen.swipe_up_rect_2_x_damping_ratio);
mXStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_x_stiffness);
mYDamping = rp.getFloat(R.dimen.swipe_up_rect_2_y_damping_ratio);
mYStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_y_stiffness);
mDuration = Math.round(rp.getFloat(R.dimen.swipe_up_duration));
mHomeTransYEnd = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
mScaleStart = rp.getFloat(R.dimen.swipe_up_scale_start);
if (!mTrackingBottomY) {
mYStiffness *= rp.getFloat(R.dimen.swipe_up_rect_2_y_stiffness_low_swipe_multiplier);
mDuration *= rp.getFloat(R.dimen.swipe_up_low_swipe_duration_multiplier);
}
mCloseInterpolator = getAppCloseInterpolator(context);
// 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 = startRadius;
mEndRadius = endRadius;
setCanRelease(true);
}
public void onTargetPositionChanged() {
if (mRectXAnim != null && mTargetX != mTargetRect.centerX()) {
mTargetX = mTargetRect.centerX();
mRectXAnim.animateToFinalPosition(mTargetX);
}
if (mRectYAnim != null) {
if (mTrackingBottomY && mTargetY != mTargetRect.bottom) {
mTargetY = mTargetRect.bottom;
mRectYAnim.animateToFinalPosition(mTargetY);
} else if (!mTrackingBottomY && mTargetY != mTargetRect.top) {
mTargetY = mTargetRect.top;
mRectYAnim.animateToFinalPosition(mTargetY);
}
}
}
public void addOnUpdateListener(OnUpdateListener onUpdateListener) {
mOnUpdateListeners.add(onUpdateListener);
}
public void addAnimatorListener(Animator.AnimatorListener animatorListener) {
mAnimatorListeners.add(animatorListener);
}
/**
* Starts the fling/spring animation.
* @param context The activity context.
* @param velocityPxPerMs Velocity of swipe in px/ms.
*/
public void start(Context context, PointF velocityPxPerMs) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context);
mRectXAnim = new SpringAnimation(this, RECT_CENTER_X)
.setStartValue(mCurrentCenterX)
.setMinValue(Math.min(0, mCurrentCenterX))
.setMaxValue(Math.max(dp.widthPx, mCurrentCenterX))
.setStartVelocity(velocityPxPerMs.x * 1000)
.setSpring(new SpringForce(mTargetX)
.setStiffness(mXStiffness)
.setDampingRatio(mXDamping));
mRectXAnim.addEndListener(((animation, canceled, centerX, velocityX) -> {
mRectXAnimEnded = true;
maybeOnEnd();
}));
mRectYAnim = new SpringAnimation(this, RECT_Y)
.setStartValue(mCurrentY)
.setMinValue(Math.min(0, mCurrentY))
.setMaxValue(Math.max(dp.heightPx, mCurrentY))
.setStartVelocity(velocityPxPerMs.y * 1000)
.setSpring(new SpringForce(mTargetY)
.setStiffness(mYStiffness)
.setDampingRatio(mYDamping));
mRectYAnim.addEndListener(((animation, canceled, centerY, velocityY) -> {
mRectYAnimEnded = true;
maybeOnEnd();
}));
mRectScaleAnim = ObjectAnimator.ofFloat(this, PROGRESS, 0, 1f)
.setDuration(mDuration);
mRectScaleAnim.setInterpolator(mCloseInterpolator);
mRectScaleAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mRectScaleAnimEnded = true;
maybeOnEnd();
}
});
mValues = buildConfig();
mRectScaleAnim.addUpdateListener(mValues);
setCanRelease(false);
mAnimsStarted = true;
mRectXAnim.start();
mRectYAnim.start();
mRectScaleAnim.start();
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationStart(null);
}
}
private AppCloseConfig buildConfig() {
return new AppCloseConfig() {
FloatProp mHomeTransY = new FloatProp(0, mHomeTransYEnd, 0, mDuration, LINEAR);
FloatProp mHomeScale = new FloatProp(mScaleStart, 1f, 0, mDuration, LINEAR);
FloatProp mWindowFadeOut = new FloatProp(1f, 0f, 0, 116, LINEAR);
// There should be a slight overlap b/w window fading out and fg fading in.
// (fg startDelay < window fade out duration)
FloatProp mFgFadeIn = new FloatProp(0, 255f, 100, mDuration - 100, LINEAR);
FloatProp mRadius = new FloatProp(mStartRadius, mEndRadius, 0, mDuration, LINEAR);
FloatProp mThreePointInterpolation = new FloatProp(0, 1, 0, mDuration, LINEAR);
@Override
public float getWorkspaceTransY() {
return mHomeTransY.value;
}
@Override
public float getWorkspaceScale() {
return mHomeScale.value;
}
@Override
public float getWindowAlpha() {
return mWindowFadeOut.value;
}
@Override
public int getFgAlpha() {
return (int) mFgFadeIn.value;
}
@Override
public float getCornerRadius() {
return mRadius.value;
}
@Override
public float getInterpolatedProgress() {
return mThreePointInterpolation.value;
}
@Override
public void onUpdate(float percent) {}
};
}
public void end() {
if (mAnimsStarted) {
if (mRectXAnim.canSkipToEnd()) {
mRectXAnim.skipToEnd();
}
if (mRectYAnim.canSkipToEnd()) {
mRectYAnim.skipToEnd();
}
mRectScaleAnim.end();
}
mRectXAnimEnded = true;
mRectYAnimEnded = true;
mRectScaleAnimEnded = true;
maybeOnEnd();
}
private boolean isEnded() {
return mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded;
}
private void onUpdate() {
if (isEnded()) {
// Prevent further updates from being called. This can happen between callbacks for
// ending the x/y/scale animations.
return;
}
if (!mOnUpdateListeners.isEmpty()) {
float rectProgress = mProgress;
float currentWidth = Utilities.mapRange(rectProgress, mStartRect.width(),
mTargetRect.width());
float currentHeight = Utilities.mapRange(rectProgress, mStartRect.height(),
mTargetRect.height());
if (mTrackingBottomY) {
mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY - currentHeight,
mCurrentCenterX + currentWidth / 2, mCurrentY);
} else {
mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY,
mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight);
}
float currentPlayTime = mRectScaleAnimEnded ? mRectScaleAnim.getDuration()
: mRectScaleAnim.getCurrentPlayTime();
float linearProgress = Math.min(1f, currentPlayTime / mRectScaleAnim.getDuration());
for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
onUpdateListener.onUpdate(mValues, mCurrentRect, linearProgress);
}
}
}
private void maybeOnEnd() {
if (mAnimsStarted && isEnded()) {
mAnimsStarted = false;
setCanRelease(true);
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationEnd(null);
}
}
}
public void cancel() {
if (mAnimsStarted) {
for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
onUpdateListener.onCancel();
}
}
end();
}
private Interpolator getAppCloseInterpolator(Context context) {
ResourceProvider rp = DynamicResource.provider(context);
String path = String.format("M 0,0 C %f, %f, %f, %f, %f, %f C %f, %f, %f, %f, 1, 1",
rp.getFloat(R.dimen.c1_a),
rp.getFloat(R.dimen.c1_b),
rp.getFloat(R.dimen.c1_c),
rp.getFloat(R.dimen.c1_d),
rp.getFloat(R.dimen.mp_x),
rp.getFloat(R.dimen.mp_y),
rp.getFloat(R.dimen.c2_a),
rp.getFloat(R.dimen.c2_b),
rp.getFloat(R.dimen.c2_c),
rp.getFloat(R.dimen.c2_d));
return PathInterpolatorCompat.create(PathParser.createPathFromPathData(path));
}
}
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
@@ -220,6 +221,9 @@ public class StaggeredWorkspaceAnim {
* @param totalRows Total number of rows.
*/
private void addStaggeredAnimationForView(View v, int row, int totalRows) {
if (PROTOTYPE_APP_CLOSE.get()) {
return;
}
// Invert the rows, because we stagger starting from the bottom of the screen.
int invertedRow = totalRows - row;
// Add 1 to the inverted row so that the bottom most row has a start delay.
+61 -27
View File
@@ -134,8 +134,45 @@
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
<item name="swipe_up_scale_start" type="dimen" format="float">0.98</item>
<item name="swipe_up_duration" type="dimen" format="float">500</item>
<item name="swipe_up_trans_y_dp" type="dimen" format="float">3</item>
<item name="swipe_up_trans_y_dp_per_s" type="dimen" format="float">3</item>
<item name="swipe_up_trans_y_damping" type="dimen" format="float">0.4</item>
<item name="swipe_up_trans_y_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
<item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">100</item>
<item name="swipe_up_rect_2_x_damping_ratio" type="dimen" format="float">1</item>
<item name="swipe_up_rect_2_x_stiffness" type="dimen" format="float">350</item>
<item name="swipe_up_rect_2_y_damping_ratio" type="dimen" format="float">1</item>
<item name="swipe_up_rect_2_y_stiffness" type="dimen" format="float">700</item>
<item name="swipe_up_rect_2_y_stiffness_low_swipe_multiplier" type="dimen" format="float">1</item>
<item name="swipe_up_low_swipe_duration_multiplier" type="dimen" format="float">1</item>
<item name="swipe_up_launcher_alpha_max_progress" type="dimen" format="float">0.85</item>
<item name="c1_a" type="dimen" format="float">0.05</item>
<item name="c1_b" type="dimen" format="float">0</item>
<item name="c1_c" type="dimen" format="float">0.133333</item>
<item name="c1_d" type="dimen" format="float">0.06</item>
<item name="mp_x" type="dimen" format="float">0.166666</item>
<item name="mp_y" type="dimen" format="float">.4</item>
<item name="c2_a" type="dimen" format="float">0.208333</item>
<item name="c2_b" type="dimen" format="float">.82</item>
<item name="c2_c" type="dimen" format="float">.25</item>
<item name="c2_d" type="dimen" format="float">1</item>
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
@@ -151,35 +188,32 @@
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
<array name="dynamic_resources">
<item>@dimen/all_apps_spring_damping_ratio</item>
<item>@dimen/all_apps_spring_stiffness</item>
<item>@dimen/swipe_up_duration</item>
<item>@dimen/swipe_up_scale_start</item>
<item>@dimen/swipe_up_trans_y_dp</item>
<item>@dimen/swipe_up_trans_y_dp_per_s</item>
<item>@dimen/swipe_up_trans_y_damping</item>
<item>@dimen/swipe_up_trans_y_stiffness</item>
<item>@dimen/swipe_up_rect_2_x_damping_ratio</item>
<item>@dimen/swipe_up_rect_2_x_stiffness</item>
<item>@dimen/swipe_up_rect_2_y_damping_ratio</item>
<item>@dimen/swipe_up_rect_2_y_stiffness</item>
<item>@dimen/swipe_up_launcher_alpha_max_progress</item>
<item>@dimen/swipe_up_rect_2_y_stiffness_low_swipe_multiplier</item>
<item>@dimen/swipe_up_low_swipe_duration_multiplier</item>
<item>@dimen/dismiss_task_trans_y_damping_ratio</item>
<item>@dimen/dismiss_task_trans_y_stiffness</item>
<item>@dimen/c1_a</item>
<item>@dimen/c1_b</item>
<item>@dimen/c1_c</item>
<item>@dimen/c1_d</item>
<item>@dimen/dismiss_task_trans_x_damping_ratio</item>
<item>@dimen/dismiss_task_trans_x_stiffness</item>
<item>@dimen/mp_x</item>
<item>@dimen/mp_y</item>
<item>@dimen/horizontal_spring_damping_ratio</item>
<item>@dimen/horizontal_spring_stiffness</item>
<item>@dimen/swipe_up_rect_scale_damping_ratio</item>
<item>@dimen/swipe_up_rect_scale_stiffness</item>
<item>@dimen/swipe_up_rect_xy_fling_friction</item>
<item>@dimen/swipe_up_rect_xy_damping_ratio</item>
<item>@dimen/swipe_up_rect_xy_stiffness</item>
<item>@dimen/staggered_damping_ratio</item>
<item>@dimen/staggered_stiffness</item>
<item>@dimen/unlock_staggered_velocity_dp_per_s</item>
<item>@dimen/swipe_up_fling_min_visible_change</item>
<item>@dimen/swipe_up_y_overshoot</item>
<item>@dimen/hint_scale_damping_ratio</item>
<item>@dimen/hint_scale_stiffness</item>
<item>@dimen/hint_scale_velocity_dp_per_s</item>
<item>@dimen/c2_a</item>
<item>@dimen/c2_b</item>
<item>@dimen/c2_c</item>
<item>@dimen/c2_d</item>
</array>
<string-array name="live_wallpapers_remove_sysui_scrims">
+12
View File
@@ -3323,6 +3323,18 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
}
/**
* Set the given view's pivot point to match the workspace's, so that it scales together. Since
* both this view and workspace can move, transform the point manually instead of using
* dragLayer.getDescendantCoordRelativeToSelf and related methods.
*/
public void setPivotToScaleWithSelf(View sibling) {
sibling.setPivotY(getPivotY() + getTop()
- sibling.getTop() - sibling.getTranslationY());
sibling.setPivotX(getPivotX() + getLeft()
- sibling.getLeft() - sibling.getTranslationX());
}
@Override
public int getExpectedHeight() {
return getMeasuredHeight() <= 0 || !mIsLayoutValid
@@ -119,7 +119,7 @@ public class WorkspaceStateTransitionAnimation {
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
}
setPivotToScaleWithWorkspace(hotseat);
mWorkspace.setPivotToScaleWithSelf(hotseat);
float hotseatScale = hotseatScaleAndTranslation.scale;
if (shouldSpring) {
PendingAnimation pa = (PendingAnimation) propertySetter;
@@ -156,18 +156,6 @@ public class WorkspaceStateTransitionAnimation {
}
}
/**
* Set the given view's pivot point to match the workspace's, so that it scales together. Since
* both this view and workspace can move, transform the point manually instead of using
* dragLayer.getDescendantCoordRelativeToSelf and related methods.
*/
private void setPivotToScaleWithWorkspace(View sibling) {
sibling.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop()
- sibling.getTop() - sibling.getTranslationY());
sibling.setPivotX(mWorkspace.getPivotX() + mWorkspace.getLeft()
- sibling.getLeft() - sibling.getTranslationX());
}
public void setScrim(PropertySetter propertySetter, LauncherState state,
StateAnimationConfig config) {
Scrim workspaceDragScrim = mLauncher.getDragLayer().getWorkspaceDragScrim();
@@ -27,7 +27,6 @@ import android.view.animation.PathInterpolator;
import com.android.launcher3.Utilities;
/**
* Common interpolators used in Launcher
*/
@@ -233,6 +233,9 @@ public final class FeatureFlags {
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
"Sends a notification whenever launcher encounters an uncaught exception.");
public static final BooleanFlag PROTOTYPE_APP_CLOSE = getDebugFlag(
"PROTOTYPE_APP_CLOSE", false, "Enables new app close");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -15,10 +15,13 @@
*/
package com.android.launcher3.views;
import static com.android.launcher3.Utilities.boundToRange;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static java.lang.Math.max;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -143,10 +146,9 @@ public class ClipIconView extends View implements ClipPathView {
/**
* Update the icon UI to match the provided parameters during an animation frame
*/
public void update(RectF rect, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening, View container,
DeviceProfile dp, boolean isVerticalBarLayout) {
public void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp,
boolean isVerticalBarLayout) {
MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
float dX = mIsRtl
@@ -166,7 +168,7 @@ public class ClipIconView extends View implements ClipPathView {
return;
}
update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, isOpening, scale,
minSize, lp, isVerticalBarLayout, dp);
container.setPivotX(0);
@@ -178,8 +180,8 @@ public class ClipIconView extends View implements ClipPathView {
}
private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
boolean isOpening, float scale, float minSize, MarginLayoutParams parentLp,
boolean isVerticalBarLayout, DeviceProfile dp) {
int fgIconAlpha, boolean isOpening, float scale, float minSize,
MarginLayoutParams parentLp, boolean isVerticalBarLayout, DeviceProfile dp) {
float dX = mIsRtl
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
: rect.left - parentLp.getMarginStart();
@@ -187,9 +189,9 @@ public class ClipIconView extends View implements ClipPathView {
// shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
float shapeRevealProgress = Utilities.boundToRange(mapToRange(
Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
LINEAR), 0, 1);
float shapeRevealProgress = boundToRange(mapToRange(max(shapeProgressStart, progress),
shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1);
if (isVerticalBarLayout) {
mOutline.right = (int) (rect.width() / scale);
@@ -231,6 +233,8 @@ public class ClipIconView extends View implements ClipPathView {
sTmpRect.offset(diffX, diffY);
mForeground.setBounds(sTmpRect);
} else {
mForeground.setAlpha(fgIconAlpha);
// Spring the foreground relative to the icon's movement within the DragLayer.
int diffX = (int) (dX / dp.availableWidthPx * FG_TRANS_X_FACTOR);
int diffY = (int) (dY / dp.availableHeightPx * FG_TRANS_Y_FACTOR);
@@ -100,6 +100,8 @@ public class FloatingIconView extends FrameLayout implements
private ListenerView mListenerView;
private Runnable mFastFinishRunnable;
private float mIconOffsetY;
public FloatingIconView(Context context) {
this(context, null);
}
@@ -136,16 +138,18 @@ public class FloatingIconView extends FrameLayout implements
/**
* Positions this view to match the size and location of {@param rect}.
* @param alpha The alpha to set this view.
* @param alpha The alpha[0, 1] of the entire floating view.
* @param fgIconAlpha The alpha[0-255] of the foreground layer of the icon (if applicable).
* @param progress A value from [0, 1] that represents the animation progress.
* @param shapeProgressStart The progress value at which to start the shape reveal.
* @param cornerRadius The corner radius of {@param rect}.
* @param isOpening True if view is used for app open animation, false for app close animation.
*/
public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening) {
public void update(float alpha, int fgIconAlpha, RectF rect, float progress,
float shapeProgressStart, float cornerRadius, boolean isOpening) {
setAlpha(alpha);
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening,
this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha,
isOpening, this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
}
@Override
@@ -478,11 +482,19 @@ public class FloatingIconView extends FrameLayout implements
@Override
public void onAnimationRepeat(Animator animator) {}
/**
* Offsets and updates the position of this view by {@param y}.
*/
public void setPositionOffsetY(float y) {
mIconOffsetY = y;
onGlobalLayout();
}
@Override
public void onGlobalLayout() {
if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
sTmpRectF);
if (mOriginalIcon != null && mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, sTmpRectF);
sTmpRectF.offset(0, mIconOffsetY);
if (!sTmpRectF.equals(mPositionOut)) {
updatePosition(sTmpRectF, (InsettableFrameLayout.LayoutParams) getLayoutParams());
if (mOnTargetChangeRunnable != null) {
@@ -617,6 +629,7 @@ public class FloatingIconView extends FrameLayout implements
mClipIconView.recycle();
mBtvDrawable.setBackground(null);
mFastFinishRunnable = null;
mIconOffsetY = 0;
}
private static class IconLoadResult {