Merge "Migrate unfold animation to Shell transitions [Part 2]" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
37f9fa6afb
@@ -14,6 +14,7 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<!-- Should be the same as in packages/apps/Launcher3/res/color-v31/taskbar_background.xml -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_neutral1_500" android:lStar="35" />
|
||||
<item android:color="@android:color/system_neutral1_500" android:lStar="15" />
|
||||
</selector>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Matches taskbar color -->
|
||||
<item android:color="@android:color/system_neutral2_500" android:lStar="35" />
|
||||
</selector>
|
||||
@@ -319,26 +319,27 @@ public abstract class WMShellBaseModule {
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
|
||||
@DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController,
|
||||
@DynamicOverride Lazy<Optional<FullscreenUnfoldController>> fullscreenUnfoldController,
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider) {
|
||||
if (progressProvider.isPresent()
|
||||
&& progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
|
||||
return fullscreenUnfoldController;
|
||||
return fullscreenUnfoldController.get();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@BindsOptionalOf
|
||||
@DynamicOverride
|
||||
abstract UnfoldTransitionHandler optionalUnfoldTransitionHandler();
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<UnfoldTransitionHandler> provideUnfoldTransitionHandler(
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider,
|
||||
TransactionPool transactionPool,
|
||||
Transitions transitions,
|
||||
@ShellMainThread ShellExecutor executor) {
|
||||
if (progressProvider.isPresent()) {
|
||||
return Optional.of(
|
||||
new UnfoldTransitionHandler(progressProvider.get(), transactionPool, executor,
|
||||
transitions));
|
||||
@DynamicOverride Lazy<Optional<UnfoldTransitionHandler>> handler) {
|
||||
if (progressProvider.isPresent()
|
||||
&& progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
|
||||
return handler.get();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
|
||||
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -344,15 +346,37 @@ public class WMShellModule {
|
||||
@Provides
|
||||
@DynamicOverride
|
||||
static FullscreenUnfoldController provideFullscreenUnfoldController(
|
||||
Context context,
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider,
|
||||
Lazy<UnfoldBackgroundController> unfoldBackgroundController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
FullscreenUnfoldTaskAnimator fullscreenUnfoldTaskAnimator,
|
||||
UnfoldBackgroundController unfoldBackgroundController,
|
||||
@ShellMainThread ShellExecutor mainExecutor
|
||||
) {
|
||||
return new FullscreenUnfoldController(context, mainExecutor,
|
||||
unfoldBackgroundController.get(), progressProvider.get(),
|
||||
displayInsetsController);
|
||||
return new FullscreenUnfoldController(mainExecutor,
|
||||
unfoldBackgroundController, progressProvider.get(),
|
||||
unfoldTransitionHandler.get(), fullscreenUnfoldTaskAnimator);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator(
|
||||
Context context,
|
||||
DisplayInsetsController displayInsetsController
|
||||
) {
|
||||
return new FullscreenUnfoldTaskAnimator(context, displayInsetsController);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
@DynamicOverride
|
||||
static UnfoldTransitionHandler provideUnfoldTransitionHandler(
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider,
|
||||
FullscreenUnfoldTaskAnimator animator,
|
||||
UnfoldBackgroundController backgroundController,
|
||||
TransactionPool transactionPool,
|
||||
Transitions transitions,
|
||||
@ShellMainThread ShellExecutor executor) {
|
||||
return new UnfoldTransitionHandler(progressProvider.get(), animator,
|
||||
transactionPool, backgroundController, executor, transitions);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -76,6 +76,8 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
taskInfo.taskId);
|
||||
final Point positionInParent = taskInfo.positionInParent;
|
||||
mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
|
||||
mAnimatableTasksListener.onTaskAppeared(taskInfo);
|
||||
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
|
||||
mSyncQueue.runInSync(t -> {
|
||||
// Reset several properties back to fullscreen (PiP, for example, leaves all these
|
||||
@@ -87,15 +89,15 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
t.show(leash);
|
||||
});
|
||||
|
||||
mAnimatableTasksListener.onTaskAppeared(taskInfo);
|
||||
updateRecentsForVisibleFullscreenTask(taskInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
|
||||
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
|
||||
|
||||
mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
|
||||
updateRecentsForVisibleFullscreenTask(taskInfo);
|
||||
|
||||
final TaskData data = mDataByTaskId.get(taskInfo.taskId);
|
||||
|
||||
@@ -17,213 +17,119 @@
|
||||
package com.android.wm.shell.fullscreen;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.util.MathUtils.lerp;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.animation.RectEvaluator;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.internal.policy.ScreenDecorationsUtils;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
|
||||
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Controls full screen app unfold transition: animating cropping window and scaling when
|
||||
* folding or unfolding a foldable device.
|
||||
*
|
||||
* - When Shell transitions are disabled (legacy mode) this controller animates task surfaces
|
||||
* when doing both fold and unfold.
|
||||
*
|
||||
* - When Shell transitions are enabled this controller animates the surfaces only when
|
||||
* folding a foldable device. It's not done as a shell transition because we are not committed
|
||||
* to the display size WM changes yet.
|
||||
* In this case unfolding is handled by
|
||||
* {@link com.android.wm.shell.unfold.UnfoldTransitionHandler}.
|
||||
*/
|
||||
public final class FullscreenUnfoldController implements UnfoldListener,
|
||||
OnInsetsChangedListener {
|
||||
|
||||
private static final float[] FLOAT_9 = new float[9];
|
||||
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
|
||||
|
||||
private static final float HORIZONTAL_START_MARGIN = 0.08f;
|
||||
private static final float VERTICAL_START_MARGIN = 0.03f;
|
||||
private static final float END_SCALE = 1f;
|
||||
private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2;
|
||||
public final class FullscreenUnfoldController implements UnfoldListener {
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final ShellUnfoldProgressProvider mProgressProvider;
|
||||
private final DisplayInsetsController mDisplayInsetsController;
|
||||
|
||||
private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
|
||||
private final UnfoldBackgroundController mBackgroundController;
|
||||
|
||||
private InsetsSource mTaskbarInsetsSource;
|
||||
|
||||
private final float mWindowCornerRadiusPx;
|
||||
private final float mExpandedTaskBarHeight;
|
||||
|
||||
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
private final FullscreenUnfoldTaskAnimator mAnimator;
|
||||
private final UnfoldTransitionHandler mUnfoldTransitionHandler;
|
||||
|
||||
private boolean mShouldHandleAnimation = false;
|
||||
|
||||
public FullscreenUnfoldController(
|
||||
@NonNull Context context,
|
||||
@NonNull Executor executor,
|
||||
@NonNull UnfoldBackgroundController backgroundController,
|
||||
@NonNull ShellUnfoldProgressProvider progressProvider,
|
||||
@NonNull DisplayInsetsController displayInsetsController
|
||||
@NonNull UnfoldTransitionHandler unfoldTransitionHandler,
|
||||
@NonNull FullscreenUnfoldTaskAnimator animator
|
||||
) {
|
||||
mExecutor = executor;
|
||||
mProgressProvider = progressProvider;
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.taskbar_frame_height);
|
||||
mBackgroundController = backgroundController;
|
||||
mUnfoldTransitionHandler = unfoldTransitionHandler;
|
||||
mAnimator = animator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the controller
|
||||
*/
|
||||
public void init() {
|
||||
mAnimator.init();
|
||||
mProgressProvider.addListener(mExecutor, this);
|
||||
mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeStarted() {
|
||||
mShouldHandleAnimation = !mUnfoldTransitionHandler.willHandleTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeProgress(float progress) {
|
||||
if (mAnimationContextByTaskId.size() == 0) return;
|
||||
if (!mAnimator.hasActiveTasks() || !mShouldHandleAnimation) return;
|
||||
|
||||
mBackgroundController.ensureBackground(mTransaction);
|
||||
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
|
||||
context.mCurrentCropRect.set(RECT_EVALUATOR
|
||||
.evaluate(progress, context.mStartCropRect, context.mEndCropRect));
|
||||
|
||||
float scale = lerp(START_SCALE, END_SCALE, progress);
|
||||
context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(),
|
||||
context.mCurrentCropRect.exactCenterY());
|
||||
|
||||
mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
|
||||
.setMatrix(context.mLeash, context.mMatrix, FLOAT_9)
|
||||
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
|
||||
}
|
||||
|
||||
mAnimator.applyAnimationProgress(progress, mTransaction);
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeFinished() {
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
resetSurface(context);
|
||||
if (!mShouldHandleAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
mShouldHandleAnimation = false;
|
||||
mAnimator.resetAllSurfaces(mTransaction);
|
||||
mBackgroundController.removeBackground(mTransaction);
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) {
|
||||
mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
context.update(mTaskbarInsetsSource, context.mTaskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new matching task appeared
|
||||
*/
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
|
||||
taskInfo);
|
||||
mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
|
||||
mAnimator.addTask(taskInfo, leash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when matching task changed
|
||||
*/
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
|
||||
if (animationContext != null) {
|
||||
animationContext.update(mTaskbarInsetsSource, taskInfo);
|
||||
}
|
||||
mAnimator.onTaskInfoChanged(taskInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when matching task vanished
|
||||
*/
|
||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
|
||||
if (animationContext != null) {
|
||||
// PiP task has its own cleanup path, ignore surface reset to avoid conflict.
|
||||
if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) {
|
||||
resetSurface(animationContext);
|
||||
}
|
||||
mAnimationContextByTaskId.remove(taskInfo.taskId);
|
||||
// PiP task has its own cleanup path, ignore surface reset to avoid conflict.
|
||||
if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) {
|
||||
mAnimator.resetSurface(taskInfo, mTransaction);
|
||||
}
|
||||
mAnimator.removeTask(taskInfo);
|
||||
|
||||
if (mAnimationContextByTaskId.size() == 0) {
|
||||
if (!mAnimator.hasActiveTasks()) {
|
||||
mBackgroundController.removeBackground(mTransaction);
|
||||
}
|
||||
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
private void resetSurface(AnimationContext context) {
|
||||
mTransaction
|
||||
.setWindowCrop(context.mLeash, null)
|
||||
.setCornerRadius(context.mLeash, 0.0F)
|
||||
.setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F)
|
||||
.setPosition(context.mLeash,
|
||||
(float) context.mTaskInfo.positionInParent.x,
|
||||
(float) context.mTaskInfo.positionInParent.y);
|
||||
}
|
||||
|
||||
private class AnimationContext {
|
||||
final SurfaceControl mLeash;
|
||||
final Rect mStartCropRect = new Rect();
|
||||
final Rect mEndCropRect = new Rect();
|
||||
final Rect mCurrentCropRect = new Rect();
|
||||
final Matrix mMatrix = new Matrix();
|
||||
|
||||
TaskInfo mTaskInfo;
|
||||
|
||||
private AnimationContext(SurfaceControl leash,
|
||||
InsetsSource taskBarInsetsSource,
|
||||
TaskInfo taskInfo) {
|
||||
this.mLeash = leash;
|
||||
update(taskBarInsetsSource, taskInfo);
|
||||
}
|
||||
|
||||
private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) {
|
||||
mTaskInfo = taskInfo;
|
||||
mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds());
|
||||
|
||||
if (taskBarInsetsSource != null) {
|
||||
// Only insets the cropping window with task bar when it's expanded
|
||||
if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
|
||||
mStartCropRect.inset(taskBarInsetsSource
|
||||
.calculateVisibleInsets(mStartCropRect));
|
||||
}
|
||||
}
|
||||
|
||||
mEndCropRect.set(mStartCropRect);
|
||||
|
||||
int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN);
|
||||
mStartCropRect.left = mEndCropRect.left + horizontalMargin;
|
||||
mStartCropRect.right = mEndCropRect.right - horizontalMargin;
|
||||
int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN);
|
||||
mStartCropRect.top = mEndCropRect.top + verticalMargin;
|
||||
mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class UnfoldBackgroundController {
|
||||
}
|
||||
|
||||
private float[] getBackgroundColor(Context context) {
|
||||
int colorInt = context.getResources().getColor(R.color.unfold_transition_background);
|
||||
int colorInt = context.getResources().getColor(R.color.taskbar_background);
|
||||
return new float[]{
|
||||
(float) red(colorInt) / 255.0F,
|
||||
(float) green(colorInt) / 255.0F,
|
||||
|
||||
@@ -34,15 +34,20 @@ import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
|
||||
import com.android.wm.shell.transition.Transitions.TransitionHandler;
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
|
||||
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Transition handler that is responsible for animating app surfaces when unfolding of foldable
|
||||
* devices. It does not handle the folding animation, which is done in
|
||||
* {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}.
|
||||
*/
|
||||
public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
|
||||
|
||||
private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
|
||||
private final Transitions mTransitions;
|
||||
private final UnfoldBackgroundController mUnfoldBackgroundController;
|
||||
private final Executor mExecutor;
|
||||
private final TransactionPool mTransactionPool;
|
||||
|
||||
@@ -51,17 +56,22 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
@Nullable
|
||||
private IBinder mTransition;
|
||||
|
||||
private final List<TransitionInfo.Change> mAnimatedFullscreenTasks = new ArrayList<>();
|
||||
private final FullscreenUnfoldTaskAnimator mFullscreenAnimator;
|
||||
|
||||
public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider,
|
||||
TransactionPool transactionPool, Executor executor, Transitions transitions) {
|
||||
FullscreenUnfoldTaskAnimator animator, TransactionPool transactionPool,
|
||||
UnfoldBackgroundController unfoldBackgroundController,
|
||||
Executor executor, Transitions transitions) {
|
||||
mUnfoldProgressProvider = unfoldProgressProvider;
|
||||
mFullscreenAnimator = animator;
|
||||
mTransactionPool = transactionPool;
|
||||
mUnfoldBackgroundController = unfoldBackgroundController;
|
||||
mExecutor = executor;
|
||||
mTransitions = transitions;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
mFullscreenAnimator.init();
|
||||
mTransitions.addHandler(this);
|
||||
mUnfoldProgressProvider.addListener(mExecutor, this);
|
||||
}
|
||||
@@ -71,40 +81,36 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull TransitionFinishCallback finishCallback) {
|
||||
|
||||
if (transition != mTransition) return false;
|
||||
|
||||
mUnfoldBackgroundController.ensureBackground(startTransaction);
|
||||
startTransaction.apply();
|
||||
|
||||
mAnimatedFullscreenTasks.clear();
|
||||
mFullscreenAnimator.clearTasks();
|
||||
info.getChanges().forEach(change -> {
|
||||
final boolean allowedToAnimate = change.getTaskInfo() != null
|
||||
&& change.getTaskInfo().isVisible()
|
||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
|
||||
&& change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME
|
||||
&& change.getMode() == TRANSIT_CHANGE;
|
||||
|
||||
if (allowedToAnimate) {
|
||||
mAnimatedFullscreenTasks.add(change);
|
||||
mFullscreenAnimator.addTask(change.getTaskInfo(), change.getLeash());
|
||||
}
|
||||
});
|
||||
|
||||
mFullscreenAnimator.resetAllSurfaces(finishTransaction);
|
||||
mUnfoldBackgroundController.removeBackground(finishTransaction);
|
||||
mFinishCallback = finishCallback;
|
||||
mTransition = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeProgress(float progress) {
|
||||
mAnimatedFullscreenTasks.forEach(change -> {
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
|
||||
// TODO: this is a placeholder animation, replace with a spec version in the next CLs
|
||||
final float testScale = 0.8f + 0.2f * progress;
|
||||
transaction.setScale(change.getLeash(), testScale, testScale);
|
||||
|
||||
transaction.apply();
|
||||
mTransactionPool.release(transaction);
|
||||
});
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
mFullscreenAnimator.applyAnimationProgress(progress, transaction);
|
||||
transaction.apply();
|
||||
mTransactionPool.release(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,7 +118,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
if (mFinishCallback != null) {
|
||||
mFinishCallback.onTransitionFinished(null, null);
|
||||
mFinishCallback = null;
|
||||
mAnimatedFullscreenTasks.clear();
|
||||
mTransition = null;
|
||||
mFullscreenAnimator.clearTasks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +127,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
@Override
|
||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request) {
|
||||
if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null
|
||||
&& request.getDisplayChange().isPhysicalDisplayChanged()) {
|
||||
if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null) {
|
||||
mTransition = transition;
|
||||
return new WindowContainerTransaction();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean willHandleTransition() {
|
||||
return mTransition != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.wm.shell.unfold.animation;
|
||||
|
||||
import static android.util.MathUtils.lerp;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.animation.RectEvaluator;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
|
||||
import com.android.internal.policy.ScreenDecorationsUtils;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
|
||||
/**
|
||||
* This helper class contains logic that calculates scaling and cropping parameters
|
||||
* for the folding/unfolding animation. As an input it receives TaskInfo objects and
|
||||
* surfaces leashes and as an output it could fill surface transactions with required
|
||||
* transformations.
|
||||
*
|
||||
* This class is used by
|
||||
* {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and
|
||||
* {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. They use independent
|
||||
* instances of FullscreenUnfoldTaskAnimator.
|
||||
*/
|
||||
public class FullscreenUnfoldTaskAnimator implements
|
||||
DisplayInsetsController.OnInsetsChangedListener {
|
||||
|
||||
private static final float[] FLOAT_9 = new float[9];
|
||||
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
|
||||
|
||||
private static final float HORIZONTAL_START_MARGIN = 0.08f;
|
||||
private static final float VERTICAL_START_MARGIN = 0.03f;
|
||||
private static final float END_SCALE = 1f;
|
||||
private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2;
|
||||
|
||||
private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
|
||||
private final int mExpandedTaskBarHeight;
|
||||
private final float mWindowCornerRadiusPx;
|
||||
private final DisplayInsetsController mDisplayInsetsController;
|
||||
|
||||
private InsetsSource mTaskbarInsetsSource;
|
||||
|
||||
public FullscreenUnfoldTaskAnimator(Context context,
|
||||
DisplayInsetsController displayInsetsController) {
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.taskbar_frame_height);
|
||||
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) {
|
||||
mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
context.update(mTaskbarInsetsSource, context.mTaskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasActiveTasks() {
|
||||
return mAnimationContextByTaskId.size() > 0;
|
||||
}
|
||||
|
||||
public void addTask(TaskInfo taskInfo, SurfaceControl leash) {
|
||||
AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
|
||||
taskInfo);
|
||||
mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
|
||||
}
|
||||
|
||||
public void onTaskInfoChanged(TaskInfo taskInfo) {
|
||||
AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
|
||||
if (animationContext != null) {
|
||||
animationContext.update(mTaskbarInsetsSource, taskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTask(TaskInfo taskInfo) {
|
||||
mAnimationContextByTaskId.remove(taskInfo.taskId);
|
||||
}
|
||||
|
||||
public void clearTasks() {
|
||||
mAnimationContextByTaskId.clear();
|
||||
}
|
||||
|
||||
public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
|
||||
final AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
|
||||
if (context != null) {
|
||||
resetSurface(context, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyAnimationProgress(float progress, Transaction transaction) {
|
||||
if (mAnimationContextByTaskId.size() == 0) return;
|
||||
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
|
||||
context.mCurrentCropRect.set(RECT_EVALUATOR
|
||||
.evaluate(progress, context.mStartCropRect, context.mEndCropRect));
|
||||
|
||||
float scale = lerp(START_SCALE, END_SCALE, progress);
|
||||
context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(),
|
||||
context.mCurrentCropRect.exactCenterY());
|
||||
|
||||
transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
|
||||
.setMatrix(context.mLeash, context.mMatrix, FLOAT_9)
|
||||
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx)
|
||||
.show(context.mLeash)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAllSurfaces(Transaction transaction) {
|
||||
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
|
||||
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
|
||||
resetSurface(context, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetSurface(AnimationContext context, Transaction transaction) {
|
||||
transaction
|
||||
.setWindowCrop(context.mLeash, null)
|
||||
.setCornerRadius(context.mLeash, 0.0F)
|
||||
.setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F)
|
||||
.setPosition(context.mLeash,
|
||||
(float) context.mTaskInfo.positionInParent.x,
|
||||
(float) context.mTaskInfo.positionInParent.y);
|
||||
}
|
||||
|
||||
private class AnimationContext {
|
||||
final SurfaceControl mLeash;
|
||||
final Rect mStartCropRect = new Rect();
|
||||
final Rect mEndCropRect = new Rect();
|
||||
final Rect mCurrentCropRect = new Rect();
|
||||
final Matrix mMatrix = new Matrix();
|
||||
|
||||
TaskInfo mTaskInfo;
|
||||
|
||||
private AnimationContext(SurfaceControl leash, InsetsSource taskBarInsetsSource,
|
||||
TaskInfo taskInfo) {
|
||||
mLeash = leash;
|
||||
update(taskBarInsetsSource, taskInfo);
|
||||
}
|
||||
|
||||
private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) {
|
||||
mTaskInfo = taskInfo;
|
||||
mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds());
|
||||
|
||||
if (taskBarInsetsSource != null) {
|
||||
// Only insets the cropping window with task bar when it's expanded
|
||||
if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
|
||||
mStartCropRect.inset(taskBarInsetsSource
|
||||
.calculateVisibleInsets(mStartCropRect));
|
||||
}
|
||||
}
|
||||
|
||||
mEndCropRect.set(mStartCropRect);
|
||||
|
||||
int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN);
|
||||
mStartCropRect.left = mEndCropRect.left + horizontalMargin;
|
||||
mStartCropRect.right = mEndCropRect.right - horizontalMargin;
|
||||
int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN);
|
||||
mStartCropRect.top = mEndCropRect.top + verticalMargin;
|
||||
mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user