Merge "Launcher side implementation of gesture seekable back to home animation." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1b4d9b3b23
@@ -111,6 +111,7 @@ import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.quickstep.LauncherBackAnimationController;
|
||||
import com.android.quickstep.RemoteAnimationTargets;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.TaskViewUtils;
|
||||
@@ -216,6 +217,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
|
||||
private RemoteTransitionCompat mLauncherOpenTransition;
|
||||
|
||||
private LauncherBackAnimationController mBackAnimationController;
|
||||
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
@@ -241,6 +243,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mDeviceProfile = mLauncher.getDeviceProfile();
|
||||
mBackAnimationController = new LauncherBackAnimationController(
|
||||
mDeviceProfile, mLauncher, this);
|
||||
|
||||
Resources res = mLauncher.getResources();
|
||||
mContentScale = res.getFloat(R.dimen.content_scale);
|
||||
@@ -1159,6 +1163,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
|
||||
SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
|
||||
}
|
||||
if (mBackAnimationController != null) {
|
||||
mBackAnimationController.registerBackCallbacks(mHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void onActivityDestroyed() {
|
||||
@@ -1194,6 +1201,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
mLauncherOpenTransition = null;
|
||||
mWallpaperOpenTransitionRunner = null;
|
||||
}
|
||||
if (mBackAnimationController != null) {
|
||||
mBackAnimationController.unregisterBackCallbacks();
|
||||
mBackAnimationController = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
|
||||
@@ -1346,8 +1357,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
/**
|
||||
* Closing animator that animates the window into its final location on the workspace.
|
||||
*/
|
||||
private void getClosingWindowAnimators(AnimatorSet animation,
|
||||
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
|
||||
private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
|
||||
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
|
||||
RectF closingWindowStartRect) {
|
||||
FloatingIconView floatingIconView = null;
|
||||
FloatingWidgetView floatingWidget = null;
|
||||
RectF targetRect = new RectF();
|
||||
@@ -1379,8 +1391,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
targetRect.set(getDefaultWindowTargetRect());
|
||||
}
|
||||
|
||||
final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
|
||||
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher,
|
||||
RectFSpringAnim anim = new RectFSpringAnim(closingWindowStartRect, targetRect, mLauncher,
|
||||
mDeviceProfile);
|
||||
|
||||
// Hook up floating views to the closing window animators.
|
||||
@@ -1414,7 +1425,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
|
||||
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
|
||||
FloatingWidgetView finalFloatingWidget = floatingWidget;
|
||||
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
|
||||
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
|
||||
windowTargetBounds) {
|
||||
@Override
|
||||
public void onUpdate(RectF currentRectF, float progress) {
|
||||
@@ -1438,6 +1449,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
anim.start(mLauncher, velocityPxPerS);
|
||||
}
|
||||
});
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1561,6 +1573,97 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
|
||||
* the transition.
|
||||
*/
|
||||
public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets,
|
||||
boolean fromUnlock,
|
||||
RectF startRect) {
|
||||
AnimatorSet anim = null;
|
||||
RectFSpringAnim rectFSpringAnim = null;
|
||||
|
||||
RemoteAnimationProvider provider = mRemoteAnimationProvider;
|
||||
if (provider != null) {
|
||||
anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
|
||||
}
|
||||
|
||||
if (anim == null) {
|
||||
anim = new AnimatorSet();
|
||||
|
||||
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|
||||
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
|
||||
|
||||
View launcherView = findLauncherView(appTargets);
|
||||
boolean playFallBackAnimation = (launcherView == null
|
||||
&& launcherIsForceInvisibleOrOpening)
|
||||
|| mLauncher.getWorkspace().isOverlayShown()
|
||||
|| hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
|
||||
|
||||
boolean playWorkspaceReveal = true;
|
||||
boolean skipAllAppsScale = false;
|
||||
if (fromUnlock) {
|
||||
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
|
||||
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
|
||||
&& !playFallBackAnimation) {
|
||||
// Use a fixed velocity to start the animation.
|
||||
float velocityPxPerS = DynamicResource.provider(mLauncher)
|
||||
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
|
||||
PointF velocity = new PointF(0, -velocityPxPerS);
|
||||
rectFSpringAnim = getClosingWindowAnimators(
|
||||
anim, appTargets, launcherView, velocity, startRect);
|
||||
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
|
||||
true /* animateOverviewScrim */, launcherView).getAnimators());
|
||||
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
|
||||
playWorkspaceReveal = false;
|
||||
} else {
|
||||
// Skip scaling all apps, otherwise FloatingIconView will get wrong
|
||||
// layout bounds.
|
||||
skipAllAppsScale = true;
|
||||
}
|
||||
} else {
|
||||
anim.play(getFallbackClosingWindowAnimators(appTargets));
|
||||
}
|
||||
|
||||
// Normally, we run the launcher content animation when we are transitioning
|
||||
// home, but if home is already visible, then we don't want to animate the
|
||||
// contents of launcher unless we know that we are animating home as a result
|
||||
// of the home button press with quickstep, which will result in launcher being
|
||||
// started on touch down, prior to the animation home (and won't be in the
|
||||
// targets list because it is already visible). In that case, we force
|
||||
// invisibility on touch down, and only reset it after the animation to home
|
||||
// is initialized.
|
||||
if (launcherIsForceInvisibleOrOpening) {
|
||||
addCujInstrumentation(
|
||||
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
|
||||
// Only register the content animation for cancellation when state changes
|
||||
mLauncher.getStateManager().setCurrentAnimation(anim);
|
||||
|
||||
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
Pair<AnimatorSet, Runnable> contentAnimator =
|
||||
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
|
||||
skipAllAppsScale);
|
||||
anim.play(contentAnimator.first);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
contentAnimator.second.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (playWorkspaceReveal) {
|
||||
anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair(rectFSpringAnim, anim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote animation runner for animation from the app to Launcher, including recents.
|
||||
*/
|
||||
@@ -1601,84 +1704,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
mLauncher.getStateManager().moveToRestState();
|
||||
}
|
||||
|
||||
AnimatorSet anim = null;
|
||||
RemoteAnimationProvider provider = mRemoteAnimationProvider;
|
||||
if (provider != null) {
|
||||
anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
|
||||
}
|
||||
|
||||
if (anim == null) {
|
||||
anim = new AnimatorSet();
|
||||
|
||||
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|
||||
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
|
||||
|
||||
View launcherView = findLauncherView(appTargets);
|
||||
boolean playFallBackAnimation = (launcherView == null
|
||||
&& launcherIsForceInvisibleOrOpening)
|
||||
|| mLauncher.getWorkspace().isOverlayShown()
|
||||
|| hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
|
||||
|
||||
boolean playWorkspaceReveal = true;
|
||||
boolean skipAllAppsScale = false;
|
||||
if (mFromUnlock) {
|
||||
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
|
||||
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
|
||||
&& !playFallBackAnimation) {
|
||||
// Use a fixed velocity to start the animation.
|
||||
float velocityPxPerS = DynamicResource.provider(mLauncher)
|
||||
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
|
||||
PointF velocity = new PointF(0, -velocityPxPerS);
|
||||
getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
|
||||
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
|
||||
true /* animateOverviewScrim */, launcherView).getAnimators());
|
||||
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
|
||||
playWorkspaceReveal = false;
|
||||
} else {
|
||||
// Skip scaling all apps, otherwise FloatingIconView will get wrong
|
||||
// layout bounds.
|
||||
skipAllAppsScale = true;
|
||||
}
|
||||
} else {
|
||||
anim.play(getFallbackClosingWindowAnimators(appTargets));
|
||||
}
|
||||
|
||||
// Normally, we run the launcher content animation when we are transitioning
|
||||
// home, but if home is already visible, then we don't want to animate the
|
||||
// contents of launcher unless we know that we are animating home as a result
|
||||
// of the home button press with quickstep, which will result in launcher being
|
||||
// started on touch down, prior to the animation home (and won't be in the
|
||||
// targets list because it is already visible). In that case, we force
|
||||
// invisibility on touch down, and only reset it after the animation to home
|
||||
// is initialized.
|
||||
if (launcherIsForceInvisibleOrOpening) {
|
||||
addCujInstrumentation(
|
||||
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
|
||||
// Only register the content animation for cancellation when state changes
|
||||
mLauncher.getStateManager().setCurrentAnimation(anim);
|
||||
|
||||
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
Pair<AnimatorSet, Runnable> contentAnimator =
|
||||
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
|
||||
skipAllAppsScale);
|
||||
anim.play(contentAnimator.first);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
contentAnimator.second.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (playWorkspaceReveal) {
|
||||
anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
|
||||
appTargets, wallpaperTargets, mFromUnlock,
|
||||
new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx));
|
||||
|
||||
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
|
||||
result.setAnimation(anim, mLauncher);
|
||||
result.setAnimation(pair.second, mLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
|
||||
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
|
||||
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Pair;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.BackEvent;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.QuickstepTransitionManager;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
/**
|
||||
* Controls the animation of swiping back and returning to launcher.
|
||||
*
|
||||
* This is a two part animation. The first part is an animation that tracks gesture location to
|
||||
* scale and move the leaving app window. Once the gesture is committed, the second part takes over
|
||||
* the app window and plays the rest of app close transitions in one go.
|
||||
*
|
||||
* This animation is used only for apps that enable back dispatching via
|
||||
* {@link android.view.OnBackInvokedDispatcher}. The controller registers
|
||||
* an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
|
||||
* navigation to launcher starts.
|
||||
*
|
||||
* Apps using the legacy back dispatching will keep triggering the WALLPAPER_OPEN remote
|
||||
* transition registered in {@link QuickstepTransitionManager}.
|
||||
*
|
||||
*/
|
||||
public class LauncherBackAnimationController {
|
||||
private static final int CANCEL_TRANSITION_DURATION = 150;
|
||||
private static final String TAG = "LauncherBackAnimationController";
|
||||
private final DeviceProfile mDeviceProfile;
|
||||
private final QuickstepTransitionManager mQuickstepTransitionManager;
|
||||
private final Matrix mTransformMatrix = new Matrix();
|
||||
private final RectF mTargetRectF = new RectF();
|
||||
private final RectF mStartRectF = new RectF();
|
||||
private final RectF mCurrentRect = new RectF();
|
||||
private final BaseQuickstepLauncher mLauncher;
|
||||
private final int mWindowScaleMarginX;
|
||||
private final int mWindowScaleMarginY;
|
||||
private final float mWindowScaleEndCornerRadius;
|
||||
private final float mWindowScaleStartCornerRadius;
|
||||
|
||||
private RemoteAnimationTargetCompat mBackTarget;
|
||||
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
private boolean mSpringAnimationInProgress = false;
|
||||
private boolean mAnimatorSetInProgress = false;
|
||||
@BackEvent.SwipeEdge
|
||||
private int mSwipeEdge;
|
||||
private float mBackProgress = 0;
|
||||
private boolean mBackInProgress = false;
|
||||
|
||||
public LauncherBackAnimationController(
|
||||
DeviceProfile deviceProfile,
|
||||
BaseQuickstepLauncher launcher,
|
||||
QuickstepTransitionManager quickstepTransitionManager) {
|
||||
mDeviceProfile = deviceProfile;
|
||||
mLauncher = launcher;
|
||||
mQuickstepTransitionManager = quickstepTransitionManager;
|
||||
mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
|
||||
mLauncher.getResources())
|
||||
? mLauncher.getResources().getDimensionPixelSize(
|
||||
R.dimen.swipe_back_window_corner_radius)
|
||||
: 0;
|
||||
mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
|
||||
mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
|
||||
R.dimen.swipe_back_window_scale_x_margin);
|
||||
mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
|
||||
R.dimen.swipe_back_window_scale_y_margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@link IOnBackInvokedCallback} to receive back dispatches from shell.
|
||||
* @param handler Handler to the thread to run the animations on.
|
||||
*/
|
||||
public void registerBackCallbacks(Handler handler) {
|
||||
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
|
||||
if (systemUiProxy == null) {
|
||||
Log.e(TAG, "SystemUiProxy is null. Skip registering back invocation callbacks");
|
||||
return;
|
||||
}
|
||||
systemUiProxy.setBackToLauncherCallback(
|
||||
new IOnBackInvokedCallback.Stub() {
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
handler.post(() -> resetPositionAnimated());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackInvoked() {
|
||||
handler.post(() -> startTransition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackProgressed(BackEvent backEvent) {
|
||||
mBackProgress = backEvent.getProgress();
|
||||
// TODO: Update once the interpolation curve spec is finalized.
|
||||
mBackProgress =
|
||||
1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
|
||||
- mBackProgress);
|
||||
if (!mBackInProgress) {
|
||||
startBack(backEvent);
|
||||
} else {
|
||||
updateBackProgress(mBackProgress);
|
||||
}
|
||||
}
|
||||
|
||||
public void onBackStarted() { }
|
||||
});
|
||||
}
|
||||
|
||||
private void resetPositionAnimated() {
|
||||
ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
|
||||
cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
|
||||
cancelAnimator.addUpdateListener(
|
||||
animation -> {
|
||||
updateBackProgress((float) animation.getAnimatedValue());
|
||||
});
|
||||
cancelAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
finishAnimation();
|
||||
}
|
||||
});
|
||||
cancelAnimator.start();
|
||||
}
|
||||
|
||||
/** Unregisters the back to launcher callback in shell. */
|
||||
public void unregisterBackCallbacks() {
|
||||
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
|
||||
if (systemUiProxy != null) {
|
||||
systemUiProxy.clearBackToLauncherCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private void startBack(BackEvent backEvent) {
|
||||
mBackInProgress = true;
|
||||
RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
|
||||
|
||||
if (appTarget == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTransaction.show(appTarget.leash).apply();
|
||||
mTransaction.setAnimationTransaction();
|
||||
mBackTarget = new RemoteAnimationTargetCompat(appTarget);
|
||||
mSwipeEdge = backEvent.getSwipeEdge();
|
||||
float screenWidth = mDeviceProfile.widthPx;
|
||||
float screenHeight = mDeviceProfile.heightPx;
|
||||
float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
|
||||
float targetWidth = targetHeight * screenWidth / screenHeight;
|
||||
float left;
|
||||
if (mSwipeEdge == BackEvent.EDGE_LEFT) {
|
||||
left = screenWidth - targetWidth - mWindowScaleMarginX;
|
||||
} else {
|
||||
left = mWindowScaleMarginX;
|
||||
}
|
||||
float top = mWindowScaleMarginY;
|
||||
// TODO(b/218916755): Offset start rectangle in multiwindow mode.
|
||||
mStartRectF.set(0, 0, screenWidth, screenHeight);
|
||||
mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
|
||||
}
|
||||
|
||||
private void updateBackProgress(float progress) {
|
||||
if (mBackTarget == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentRect.set(
|
||||
MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
|
||||
MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
|
||||
MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
|
||||
MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
|
||||
SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
|
||||
new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
|
||||
|
||||
Rect currentRect = new Rect();
|
||||
mCurrentRect.round(currentRect);
|
||||
|
||||
// Scale the target window to match the currentRectF.
|
||||
final float scale = mCurrentRect.width() / mStartRectF.width();
|
||||
mTransformMatrix.reset();
|
||||
mTransformMatrix.setScale(scale, scale);
|
||||
mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
|
||||
Rect startRect = new Rect();
|
||||
mStartRectF.round(startRect);
|
||||
float cornerRadius = Utilities.mapRange(
|
||||
progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
|
||||
builder.withMatrix(mTransformMatrix)
|
||||
.withWindowCrop(startRect)
|
||||
.withCornerRadius(cornerRadius);
|
||||
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
|
||||
|
||||
if (surfaceParams.surface.isValid()) {
|
||||
surfaceParams.applyTo(mTransaction);
|
||||
}
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
private void startTransition() {
|
||||
if (mBackTarget == null) {
|
||||
// Trigger transition system instead of custom transition animation.
|
||||
finishAnimation();
|
||||
return;
|
||||
}
|
||||
if (mLauncher.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
// TODO: Catch the moment when launcher becomes visible after the top app un-occludes
|
||||
// launcher and start animating afterwards. Currently we occasionally get a flicker from
|
||||
// animating when launcher is still invisible.
|
||||
if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
|
||||
mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
|
||||
mLauncher.getStateManager().moveToRestState();
|
||||
}
|
||||
|
||||
Pair<RectFSpringAnim, AnimatorSet> pair =
|
||||
mQuickstepTransitionManager.createWallpaperOpenAnimations(
|
||||
new RemoteAnimationTargetCompat[]{mBackTarget},
|
||||
new RemoteAnimationTargetCompat[]{},
|
||||
false /* fromUnlock */,
|
||||
mCurrentRect);
|
||||
startTransitionAnimations(pair.first, pair.second);
|
||||
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
|
||||
}
|
||||
|
||||
private void finishAnimation() {
|
||||
mBackTarget = null;
|
||||
mBackInProgress = false;
|
||||
mBackProgress = 0;
|
||||
mSwipeEdge = BackEvent.EDGE_LEFT;
|
||||
mTransformMatrix.reset();
|
||||
mTargetRectF.setEmpty();
|
||||
mCurrentRect.setEmpty();
|
||||
mStartRectF.setEmpty();
|
||||
mAnimatorSetInProgress = false;
|
||||
mSpringAnimationInProgress = false;
|
||||
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
|
||||
if (systemUiProxy != null) {
|
||||
SystemUiProxy.INSTANCE.getNoCreate().onBackToLauncherAnimationFinished();
|
||||
}
|
||||
}
|
||||
|
||||
private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
|
||||
mAnimatorSetInProgress = anim != null;
|
||||
mSpringAnimationInProgress = springAnim != null;
|
||||
if (springAnim != null) {
|
||||
springAnim.addAnimatorListener(
|
||||
new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mSpringAnimationInProgress = false;
|
||||
tryFinishBackAnimation();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAnimatorSetInProgress = false;
|
||||
tryFinishBackAnimation();
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
}
|
||||
|
||||
private void tryFinishBackAnimation() {
|
||||
if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
|
||||
finishAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import android.view.MotionEvent;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
@@ -50,6 +51,7 @@ import com.android.systemui.shared.system.RemoteTransitionCompat;
|
||||
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
|
||||
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
|
||||
import com.android.systemui.shared.system.smartspace.SmartspaceState;
|
||||
import com.android.wm.shell.back.IBackAnimation;
|
||||
import com.android.wm.shell.onehanded.IOneHanded;
|
||||
import com.android.wm.shell.pip.IPip;
|
||||
import com.android.wm.shell.pip.IPipAnimationListener;
|
||||
@@ -82,6 +84,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
private IShellTransitions mShellTransitions;
|
||||
private IStartingWindow mStartingWindow;
|
||||
private IRecentTasks mRecentTasks;
|
||||
private IBackAnimation mBackAnimation;
|
||||
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
|
||||
MAIN_EXECUTOR.execute(() -> clearProxy());
|
||||
};
|
||||
@@ -96,6 +99,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
|
||||
private IRecentTasksListener mRecentTasksListener;
|
||||
private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
|
||||
private IOnBackInvokedCallback mBackToLaunchCallback;
|
||||
|
||||
// Used to dedupe calls to SystemUI
|
||||
private int mLastShelfHeight;
|
||||
@@ -162,8 +166,8 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
|
||||
IOneHanded oneHanded, IShellTransitions shellTransitions,
|
||||
IStartingWindow startingWindow, IRecentTasks recentTasks,
|
||||
ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
|
||||
|
||||
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
|
||||
IBackAnimation backAnimation) {
|
||||
unlinkToDeath();
|
||||
mSystemUiProxy = proxy;
|
||||
mPip = pip;
|
||||
@@ -173,6 +177,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
mStartingWindow = startingWindow;
|
||||
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
|
||||
mRecentTasks = recentTasks;
|
||||
mBackAnimation = backAnimation;
|
||||
linkToDeath();
|
||||
// re-attach the listeners once missing due to setProxy has not been initialized yet.
|
||||
if (mPipAnimationListener != null && mPip != null) {
|
||||
@@ -195,6 +200,9 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
if (mRecentTasksListener != null && mRecentTasks != null) {
|
||||
registerRecentTasksListener(mRecentTasksListener);
|
||||
}
|
||||
if (mBackAnimation != null && mBackToLaunchCallback != null) {
|
||||
setBackToLauncherCallback(mBackToLaunchCallback);
|
||||
}
|
||||
|
||||
if (mPendingSetNavButtonAlpha != null) {
|
||||
mPendingSetNavButtonAlpha.run();
|
||||
@@ -203,7 +211,7 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
}
|
||||
|
||||
public void clearProxy() {
|
||||
setProxy(null, null, null, null, null, null, null, null);
|
||||
setProxy(null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
// TODO(141886704): Find a way to remove this
|
||||
@@ -822,6 +830,49 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
|
||||
mRecentTasksListener = null;
|
||||
}
|
||||
|
||||
//
|
||||
// Back navigation transitions
|
||||
//
|
||||
|
||||
/** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
|
||||
public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
|
||||
mBackToLaunchCallback = callback;
|
||||
if (mBackAnimation == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mBackAnimation.setBackToLauncherCallback(callback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed call setBackToLauncherCallback", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears the previously registered {@link IOnBackInvokedCallback}. */
|
||||
public void clearBackToLauncherCallback() {
|
||||
if (mBackAnimation == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mBackAnimation.clearBackToLauncherCallback();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies shell that all back to launcher animations have finished (including the transition
|
||||
* that plays after the gesture is committed and before the app is closed.
|
||||
*/
|
||||
public void onBackToLauncherAnimationFinished() {
|
||||
if (mBackAnimation != null) {
|
||||
try {
|
||||
mBackAnimation.onBackToLauncherAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call onBackAnimationFinished", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
|
||||
if (mRecentTasks != null) {
|
||||
try {
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.quickstep.GestureState.DEFAULT_STATE;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
|
||||
@@ -106,6 +107,7 @@ import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
|
||||
import com.android.systemui.shared.tracing.ProtoTraceable;
|
||||
import com.android.wm.shell.back.IBackAnimation;
|
||||
import com.android.wm.shell.onehanded.IOneHanded;
|
||||
import com.android.wm.shell.pip.IPip;
|
||||
import com.android.wm.shell.recents.IRecentTasks;
|
||||
@@ -166,10 +168,12 @@ public class TouchInteractionService extends Service
|
||||
bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
|
||||
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
|
||||
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
|
||||
IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
|
||||
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
|
||||
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
|
||||
launcherUnlockAnimationController);
|
||||
launcherUnlockAnimationController, backAnimation);
|
||||
TouchInteractionService.this.initInputMonitor();
|
||||
preloadOverview(true /* fromInit */);
|
||||
});
|
||||
|
||||
@@ -166,4 +166,8 @@
|
||||
<!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
|
||||
<string name="color_generator_class" translatable="false"/>
|
||||
|
||||
<!-- Swipe back to home related -->
|
||||
<dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
|
||||
<dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
|
||||
<dimen name="swipe_back_window_corner_radius">40dp</dimen>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user