summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java135
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java74
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java219
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java98
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java)279
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java166
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java52
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java341
32 files changed, 1205 insertions, 741 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index f6a3e7fb54d9..38f7465f3f45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -27,7 +27,6 @@ import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
@@ -35,6 +34,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import java.util.Optional;
@@ -55,7 +55,7 @@ public class ShellInitImpl {
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
- private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
+ private final Optional<UnfoldAnimationController> mUnfoldController;
private final Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
private final ShellExecutor mMainExecutor;
@@ -76,7 +76,7 @@ public class ShellInitImpl {
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformTaskListener> freeformTaskListenerOptional,
Optional<RecentTasksController> recentTasks,
@@ -93,7 +93,7 @@ public class ShellInitImpl {
mSplitScreenOptional = splitScreenOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
- mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
+ mUnfoldController = unfoldAnimationController;
mUnfoldTransitionHandler = unfoldTransitionHandler;
mFreeformTaskListenerOptional = freeformTaskListenerOptional;
mRecentTasks = recentTasks;
@@ -146,7 +146,7 @@ public class ShellInitImpl {
mShellTaskOrganizer.addListenerForType(
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
- mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
+ mUnfoldController.ifPresent(UnfoldAnimationController::init);
mRecentTasks.ifPresent(RecentTasksController::init);
// Initialize kids mode task organizer
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 31f0ef0192ae..e9d24fbf4d0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -179,33 +180,41 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
private final Optional<RecentTasksController> mRecentTasks;
@Nullable
+ private final UnfoldAnimationController mUnfoldAnimationController;
+
+ @Nullable
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
+ Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
CompatUIController compatUI) {
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
+ Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
- recentTasks);
+ unfoldAnimationController, recentTasks);
}
@VisibleForTesting
protected ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
ShellExecutor mainExecutor, Context context, @Nullable CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
super(taskOrganizerController, mainExecutor);
mCompatUI = compatUI;
mRecentTasks = recentTasks;
+ mUnfoldAnimationController = unfoldAnimationController.orElse(null);
if (compatUI != null) {
compatUI.setCompatUICallback(this);
}
@@ -437,6 +446,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash());
+ }
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
notifyCompatUI(info.getTaskInfo(), listener);
}
@@ -458,6 +470,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
+
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+ }
+
final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final TaskListener oldListener = getTaskListener(data.getTaskInfo());
final TaskListener newListener = getTaskListener(taskInfo);
@@ -507,6 +524,10 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
public void onTaskVanished(RunningTaskInfo taskInfo) {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+ }
+
final int taskId = taskInfo.taskId;
final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
mTasks.remove(taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index c94455d9151a..ce3ae6e7bfc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -178,6 +178,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return outBounds;
}
+ /** Gets root bounds of the whole split layout */
+ public Rect getRootBounds() {
+ return new Rect(mRootBounds);
+ }
+
/** Gets bounds of divider window with screen based coordinate. */
public Rect getDividerBounds() {
return new Rect(mDividerBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 1d10bbe37438..a57b0e0ab241 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -64,7 +64,6 @@ import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
@@ -88,6 +87,7 @@ import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import java.util.Optional;
@@ -176,9 +176,11 @@ public abstract class WMShellBaseModule {
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
Context context,
CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional
) {
- return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
+ return new ShellTaskOrganizer(mainExecutor, context, compatUI, unfoldAnimationController,
+ recentTasksOptional);
}
@WMSingleton
@@ -190,10 +192,12 @@ public abstract class WMShellBaseModule {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional
) {
return new KidsModeTaskOrganizer(mainExecutor, mainHandler, context, syncTransactionQueue,
- displayController, displayInsetsController, recentTasksOptional);
+ displayController, displayInsetsController, unfoldAnimationController,
+ recentTasksOptional);
}
@WMSingleton
@@ -290,13 +294,11 @@ public abstract class WMShellBaseModule {
static FullscreenTaskListener provideFullscreenTaskListener(
@DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
Optional<RecentTasksController> recentTasksOptional) {
if (fullscreenTaskListener.isPresent()) {
return fullscreenTaskListener.get();
} else {
- return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
- recentTasksOptional);
+ return new FullscreenTaskListener(syncQueue, recentTasksOptional);
}
}
@@ -310,12 +312,13 @@ public abstract class WMShellBaseModule {
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
@DynamicOverride
- abstract FullscreenUnfoldController optionalFullscreenUnfoldController();
+ abstract UnfoldAnimationController optionalUnfoldController();
@WMSingleton
@Provides
- static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
- @DynamicOverride Lazy<Optional<FullscreenUnfoldController>> fullscreenUnfoldController,
+ static Optional<UnfoldAnimationController> provideUnfoldController(
+ @DynamicOverride Lazy<Optional<UnfoldAnimationController>>
+ fullscreenUnfoldController,
Optional<ShellUnfoldProgressProvider> progressProvider) {
if (progressProvider.isPresent()
&& progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
@@ -640,7 +643,7 @@ public abstract class WMShellBaseModule {
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> appUnfoldTransitionController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformTaskListener> freeformTaskListener,
Optional<RecentTasksController> recentTasksOptional,
@@ -657,7 +660,7 @@ public abstract class WMShellBaseModule {
splitScreenOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
- appUnfoldTransitionController,
+ unfoldAnimationController,
unfoldTransitionHandler,
freeformTaskListener,
recentTasksOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index c5453d4394b8..8d2b5f766f4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -45,7 +45,6 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -67,17 +66,22 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
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 com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+import com.android.wm.shell.unfold.qualifier.UnfoldTransition;
+import com.android.wm.shell.unfold.qualifier.UnfoldShellTransition;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
-import javax.inject.Provider;
-
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -91,7 +95,7 @@ import dagger.Provides;
* dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = WMShellBaseModule.class)
-public class WMShellModule {
+public abstract class WMShellModule {
//
// Bubbles
@@ -172,12 +176,11 @@ public class WMShellModule {
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- recentTasks, stageTaskUnfoldControllerProvider);
+ recentTasks);
}
//
@@ -324,62 +327,77 @@ public class WMShellModule {
//
// Unfold transition
//
-
@WMSingleton
@Provides
@DynamicOverride
- static FullscreenUnfoldController provideFullscreenUnfoldController(
+ static UnfoldAnimationController provideUnfoldAnimationController(
Optional<ShellUnfoldProgressProvider> progressProvider,
- Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
- FullscreenUnfoldTaskAnimator fullscreenUnfoldTaskAnimator,
- UnfoldBackgroundController unfoldBackgroundController,
+ TransactionPool transactionPool,
+ @UnfoldTransition SplitTaskUnfoldAnimator splitAnimator,
+ FullscreenUnfoldTaskAnimator fullscreenAnimator,
+ Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
@ShellMainThread ShellExecutor mainExecutor
) {
- return new FullscreenUnfoldController(mainExecutor,
- unfoldBackgroundController, progressProvider.get(),
- unfoldTransitionHandler.get(), fullscreenUnfoldTaskAnimator);
+ final List<UnfoldTaskAnimator> animators = new ArrayList<>();
+ animators.add(splitAnimator);
+ animators.add(fullscreenAnimator);
+
+ return new UnfoldAnimationController(
+ transactionPool,
+ progressProvider.get(),
+ animators,
+ unfoldTransitionHandler,
+ mainExecutor
+ );
}
+
@Provides
static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator(
Context context,
+ UnfoldBackgroundController unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController
+ ) {
+ return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
+ displayInsetsController);
+ }
+
+ @Provides
+ static SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimatorBase(
+ Context context,
+ UnfoldBackgroundController backgroundController,
+ @ShellMainThread ShellExecutor executor,
+ Lazy<Optional<SplitScreenController>> splitScreenOptional,
DisplayInsetsController displayInsetsController
) {
- return new FullscreenUnfoldTaskAnimator(context, displayInsetsController);
+ return new SplitTaskUnfoldAnimator(context, executor, splitScreenOptional,
+ backgroundController, displayInsetsController);
}
@WMSingleton
+ @UnfoldShellTransition
+ @Binds
+ abstract SplitTaskUnfoldAnimator provideShellSplitTaskUnfoldAnimator(
+ SplitTaskUnfoldAnimator splitTaskUnfoldAnimator);
+
+ @WMSingleton
+ @UnfoldTransition
+ @Binds
+ abstract SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimator(
+ SplitTaskUnfoldAnimator splitTaskUnfoldAnimator);
+
+ @WMSingleton
@Provides
@DynamicOverride
static UnfoldTransitionHandler provideUnfoldTransitionHandler(
Optional<ShellUnfoldProgressProvider> progressProvider,
FullscreenUnfoldTaskAnimator animator,
- UnfoldBackgroundController backgroundController,
+ @UnfoldShellTransition SplitTaskUnfoldAnimator unfoldAnimator,
TransactionPool transactionPool,
Transitions transitions,
@ShellMainThread ShellExecutor executor) {
return new UnfoldTransitionHandler(progressProvider.get(), animator,
- transactionPool, backgroundController, executor, transitions);
- }
-
- @Provides
- static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
- Optional<ShellUnfoldProgressProvider> progressProvider,
- Context context,
- TransactionPool transactionPool,
- Lazy<UnfoldBackgroundController> unfoldBackgroundController,
- DisplayInsetsController displayInsetsController,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
- return progressProvider.map(shellUnfoldTransitionProgressProvider ->
- new StageTaskUnfoldController(
- context,
- transactionPool,
- shellUnfoldTransitionProgressProvider,
- displayInsetsController,
- unfoldBackgroundController.get(),
- mainExecutor
- ));
+ unfoldAnimator, transactionPool, executor, transitions);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 1fc1215b6cea..79e363bcdb41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -16,17 +16,13 @@
package com.android.wm.shell.fullscreen;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.TaskInfo;
import android.graphics.Point;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -48,22 +44,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final SyncTransactionQueue mSyncQueue;
- private final FullscreenUnfoldController mFullscreenUnfoldController;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
- private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener();
- public FullscreenTaskListener(SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> unfoldController) {
- this(syncQueue, unfoldController, Optional.empty());
+ public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ this(syncQueue, Optional.empty());
}
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> unfoldController,
Optional<RecentTasksController> recentTasks) {
mSyncQueue = syncQueue;
- mFullscreenUnfoldController = unfoldController.orElse(null);
mRecentTasksOptional = recentTasks;
}
@@ -76,7 +67,6 @@ 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 -> {
@@ -94,8 +84,6 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
-
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo);
@@ -117,7 +105,6 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
return;
}
- mAnimatableTasksListener.onTaskVanished(taskInfo);
mDataByTaskId.remove(taskInfo.taskId);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
@@ -175,65 +162,4 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
this.positionInParent = positionInParent;
}
}
-
- class AnimatableTasksListener {
- private final SparseBooleanArray mTaskIds = new SparseBooleanArray();
-
- public void onTaskAppeared(RunningTaskInfo taskInfo) {
- final boolean isApplicable = isAnimatable(taskInfo);
- if (isApplicable) {
- mTaskIds.put(taskInfo.taskId, true);
-
- if (mFullscreenUnfoldController != null) {
- SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
- mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
- }
- }
- }
-
- public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
- final boolean isApplicable = isAnimatable(taskInfo);
-
- if (isCurrentlyApplicable) {
- if (isApplicable) {
- // Still applicable, send update
- if (mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskInfoChanged(taskInfo);
- }
- } else {
- // Became inapplicable
- if (mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskVanished(taskInfo);
- }
- mTaskIds.put(taskInfo.taskId, false);
- }
- } else {
- if (isApplicable) {
- // Became applicable
- mTaskIds.put(taskInfo.taskId, true);
-
- if (mFullscreenUnfoldController != null) {
- SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
- mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
- }
- }
- }
- }
-
- public void onTaskVanished(RunningTaskInfo taskInfo) {
- final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
- if (isCurrentlyApplicable && mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskVanished(taskInfo);
- }
- mTaskIds.put(taskInfo.taskId, false);
- }
-
- private boolean isAnimatable(TaskInfo taskInfo) {
- // Filter all visible tasks that are not launcher tasks
- // We do not animate launcher as it handles the animation by itself
- return taskInfo != null && taskInfo.isVisible && taskInfo.getConfiguration()
- .windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
deleted file mode 100644
index 99f15f65f74e..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.wm.shell.fullscreen;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-
-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 {
-
- private final Executor mExecutor;
- private final ShellUnfoldProgressProvider mProgressProvider;
- private final UnfoldBackgroundController mBackgroundController;
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
- private final FullscreenUnfoldTaskAnimator mAnimator;
- private final UnfoldTransitionHandler mUnfoldTransitionHandler;
-
- private boolean mShouldHandleAnimation = false;
-
- public FullscreenUnfoldController(
- @NonNull Executor executor,
- @NonNull UnfoldBackgroundController backgroundController,
- @NonNull ShellUnfoldProgressProvider progressProvider,
- @NonNull UnfoldTransitionHandler unfoldTransitionHandler,
- @NonNull FullscreenUnfoldTaskAnimator animator
- ) {
- mExecutor = executor;
- mProgressProvider = progressProvider;
- mBackgroundController = backgroundController;
- mUnfoldTransitionHandler = unfoldTransitionHandler;
- mAnimator = animator;
- }
-
- /**
- * Initializes the controller
- */
- public void init() {
- mAnimator.init();
- mProgressProvider.addListener(mExecutor, this);
- }
-
- @Override
- public void onStateChangeStarted() {
- mShouldHandleAnimation = !mUnfoldTransitionHandler.willHandleTransition();
- }
-
- @Override
- public void onStateChangeProgress(float progress) {
- if (!mAnimator.hasActiveTasks() || !mShouldHandleAnimation) return;
-
- mBackgroundController.ensureBackground(mTransaction);
- mAnimator.applyAnimationProgress(progress, mTransaction);
- mTransaction.apply();
- }
-
- @Override
- public void onStateChangeFinished() {
- if (!mShouldHandleAnimation) {
- return;
- }
-
- mShouldHandleAnimation = false;
- mAnimator.resetAllSurfaces(mTransaction);
- mBackgroundController.removeBackground(mTransaction);
- mTransaction.apply();
- }
-
- /**
- * Called when a new matching task appeared
- */
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- mAnimator.addTask(taskInfo, leash);
- }
-
- /**
- * Called when matching task changed
- */
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- mAnimator.onTaskInfoChanged(taskInfo);
- }
-
- /**
- * Called when matching task vanished
- */
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- // 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 (!mAnimator.hasActiveTasks()) {
- mBackgroundController.removeBackground(mTransaction);
- }
-
- mTransaction.apply();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index b4c87b6cbf95..2c8ba0970ccc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import java.io.PrintWriter;
import java.util.List;
@@ -146,9 +147,11 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
KidsModeSettingsObserver kidsModeSettingsObserver) {
- super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null, recentTasks);
+ super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null,
+ unfoldAnimationController, recentTasks);
mContext = context;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
@@ -164,8 +167,9 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
- super(mainExecutor, context, /* compatUI= */ null, recentTasks);
+ super(mainExecutor, context, /* compatUI= */ null, unfoldAnimationController, recentTasks);
mContext = context;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index ae5e075c4d3f..2bfa5db502ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.Nullable;
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -38,10 +37,9 @@ class MainStage extends StageTaskListener {
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
- stageTaskUnfoldController);
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ iconProvider);
}
boolean isActive() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index d55619f5e5ed..f92a0d3901b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.view.SurfaceSession;
@@ -38,10 +37,9 @@ class SideStage extends StageTaskListener {
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
- stageTaskUnfoldController);
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ iconProvider);
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 448773ae9ea2..29b6311e5041 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
@@ -58,6 +59,7 @@ public interface SplitScreen {
interface SplitScreenListener {
default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {}
default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {}
+ default void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {}
default void onSplitVisibilityChanged(boolean visible) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 0d976d4a81eb..e7958ee4d700 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -86,8 +86,6 @@ import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
-import javax.inject.Provider;
-
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
@@ -138,7 +136,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
- private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
@@ -152,8 +149,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
@@ -164,7 +160,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
mTransactionPool = transactionPool;
- mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
@@ -190,7 +185,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider);
+ mIconProvider, mMainExecutor, mRecentTasksOptional);
}
}
@@ -226,6 +221,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
new WindowContainerTransaction());
}
+ /**
+ * Update surfaces of the split screen layout based on the current state
+ * @param transaction to write the updates to
+ */
+ public void updateSplitScreenSurfaces(SurfaceControl.Transaction transaction) {
+ mStageCoordinator.updateSurfaces(transaction);
+ }
+
private boolean moveToStage(int taskId, @StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
@@ -539,6 +542,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
+ for (int i = 0; i < mExecutors.size(); i++) {
+ final int index = i;
+ mExecutors.valueAt(index).execute(() -> {
+ mExecutors.keyAt(index).onSplitBoundsChanged(rootBounds, mainBounds,
+ sideBounds);
+ });
+ }
+ }
+
+ @Override
public void onSplitVisibilityChanged(boolean visible) {
for (int i = 0; i < mExecutors.size(); i++) {
final int index = i;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 3cf8a45310ef..357a0e63386f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -117,8 +117,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
* {@link SideStage} stages.
@@ -145,10 +143,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final MainStage mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mMainUnfoldController;
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mSideUnfoldController;
private final DisplayLayout mDisplayLayout;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -209,8 +205,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
IconProvider iconProvider, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -218,8 +213,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
+
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
mMainStage = new MainStage(
@@ -229,8 +223,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider,
- mMainUnfoldController);
+ iconProvider);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
@@ -238,8 +231,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider,
- mSideUnfoldController);
+ iconProvider);
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -262,8 +254,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -277,8 +268,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
@@ -630,7 +619,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
onLayoutSizeChanged(mSplitLayout);
} else {
updateWindowBounds(mSplitLayout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
}
}
}
@@ -860,6 +849,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
listener.onSplitVisibilityChanged(isSplitScreenVisible());
+ if (mSplitLayout != null) {
+ listener.onSplitBoundsChanged(mSplitLayout.getRootBounds(), getMainStageBounds(),
+ getSideStageBounds());
+ }
mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
}
@@ -872,6 +865,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ private void sendOnBoundsChanged() {
+ if (mSplitLayout == null) return;
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onSplitBoundsChanged(mSplitLayout.getRootBounds(),
+ getMainStageBounds(), getSideStageBounds());
+ }
+ }
+
private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
boolean present, boolean visible) {
int stage;
@@ -935,12 +936,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
}
-
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- updateUnfoldBounds();
- }
+ sendOnBoundsChanged();
}
@Override
@@ -961,11 +957,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.init();
- mSideUnfoldController.init();
- }
-
onRootTaskAppeared();
}
@@ -979,13 +970,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
- && mMainStage.isActive()) {
- // TODO(b/204925795): With Shell transition, We are handling split bounds rotation at
- // onRotateDisplay. But still need to handle unfold case.
- if (ENABLE_SHELL_TRANSITIONS) {
- updateUnfoldBounds();
- return;
- }
+ && mMainStage.isActive()
+ && !ENABLE_SHELL_TRANSITIONS) {
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config.
mIsDividerRemoteAnimating = false;
@@ -1235,7 +1221,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onLayoutSizeChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateWindowBounds(layout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setResizingSplits(false /* resizing */);
@@ -1246,15 +1232,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
- private void updateUnfoldBounds() {
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onLayoutChanged(getMainStageBounds(), getMainStagePosition(),
- isLandscape());
- mSideUnfoldController.onLayoutChanged(getSideStageBounds(), getSideStagePosition(),
- isLandscape());
- }
- }
-
private boolean isLandscape() {
return mSplitLayout.isLandscape();
}
@@ -1334,6 +1311,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
}
+ void updateSurfaces(SurfaceControl.Transaction transaction) {
+ updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false);
+ mSplitLayout.update(transaction);
+ }
+
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
@@ -1346,7 +1328,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
}
updateWindowBounds(mSplitLayout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
}
private void onFoldedStateChanged(boolean folded) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index eb513384d822..86ffacc4eb50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -26,7 +26,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Point;
@@ -95,18 +94,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// TODO(b/204308910): Extracts SplitDecorManager related code to common package.
private SplitDecorManager mSplitDecorManager;
- private final StageTaskUnfoldController mStageTaskUnfoldController;
-
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
- mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -207,10 +202,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
- }
}
@Override
@@ -278,10 +269,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskVanished(taskInfo);
- }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
new file mode 100644
index 000000000000..530d47416665
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
@@ -0,0 +1,219 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import dagger.Lazy;
+
+/**
+ * Manages fold/unfold animations of tasks on foldable devices.
+ * When folding or unfolding a foldable device we play animations that
+ * transform task cropping/scaling/rounded corners.
+ *
+ * This controller manages:
+ * 1) Folding/unfolding when Shell transitions disabled
+ * 2) Folding when Shell transitions enabled, unfolding is managed by
+ * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler}
+ */
+public class UnfoldAnimationController implements UnfoldListener {
+
+ private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
+ private final Executor mExecutor;
+ private final TransactionPool mTransactionPool;
+ private final List<UnfoldTaskAnimator> mAnimators;
+ private final Lazy<Optional<UnfoldTransitionHandler>> mUnfoldTransitionHandler;
+
+ private final SparseArray<SurfaceControl> mTaskSurfaces = new SparseArray<>();
+ private final SparseArray<UnfoldTaskAnimator> mAnimatorsByTaskId = new SparseArray<>();
+
+ public UnfoldAnimationController(@NonNull TransactionPool transactionPool,
+ @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
+ @NonNull List<UnfoldTaskAnimator> animators,
+ @NonNull Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
+ @NonNull Executor executor) {
+ mUnfoldProgressProvider = unfoldProgressProvider;
+ mUnfoldTransitionHandler = unfoldTransitionHandler;
+ mTransactionPool = transactionPool;
+ mExecutor = executor;
+ mAnimators = animators;
+ }
+
+ /**
+ * Initializes the controller, starts listening for the external events
+ */
+ public void init() {
+ mUnfoldProgressProvider.addListener(mExecutor, this);
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.init();
+ animator.start();
+ }
+ }
+
+ /**
+ * Called when a task appeared
+ * @param taskInfo info for the appeared task
+ * @param leash surface leash for the appeared task
+ */
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ mTaskSurfaces.put(taskInfo.taskId, leash);
+
+ // Find the first matching animator
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.isApplicableTask(taskInfo)) {
+ mAnimatorsByTaskId.put(taskInfo.taskId, animator);
+ animator.onTaskAppeared(taskInfo, leash);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Called when task info changed
+ * @param taskInfo info for the changed task
+ */
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final UnfoldTaskAnimator animator = mAnimatorsByTaskId.get(taskInfo.taskId);
+ final boolean isCurrentlyApplicable = animator != null;
+
+ if (isCurrentlyApplicable) {
+ final boolean isApplicable = animator.isApplicableTask(taskInfo);
+ if (isApplicable) {
+ // Still applicable, send update
+ animator.onTaskChanged(taskInfo);
+ } else {
+ // Became inapplicable
+ resetTask(animator, taskInfo);
+ animator.onTaskVanished(taskInfo);
+ mAnimatorsByTaskId.remove(taskInfo.taskId);
+ }
+ } else {
+ // Find the first matching animator
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator currentAnimator = mAnimators.get(i);
+ if (currentAnimator.isApplicableTask(taskInfo)) {
+ // Became applicable
+ mAnimatorsByTaskId.put(taskInfo.taskId, currentAnimator);
+
+ SurfaceControl leash = mTaskSurfaces.get(taskInfo.taskId);
+ currentAnimator.onTaskAppeared(taskInfo, leash);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when a task vanished
+ * @param taskInfo info for the vanished task
+ */
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ mTaskSurfaces.remove(taskInfo.taskId);
+
+ final UnfoldTaskAnimator animator = mAnimatorsByTaskId.get(taskInfo.taskId);
+ final boolean isCurrentlyApplicable = animator != null;
+
+ if (isCurrentlyApplicable) {
+ resetTask(animator, taskInfo);
+ animator.onTaskVanished(taskInfo);
+ mAnimatorsByTaskId.remove(taskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void onStateChangeStarted() {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = null;
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) transaction = mTransactionPool.acquire();
+ animator.prepareStartTransaction(transaction);
+ }
+ }
+
+ if (transaction != null) {
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = null;
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) transaction = mTransactionPool.acquire();
+ animator.applyAnimationProgress(progress, transaction);
+ }
+ }
+
+ if (transaction != null) {
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.resetAllSurfaces(transaction);
+ animator.prepareFinishTransaction(transaction);
+ }
+
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ private void resetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ animator.resetSurface(taskInfo, transaction);
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 8e45e7d36b86..9bf32faa12bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.unfold;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.os.IBinder;
@@ -35,19 +33,22 @@ 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 com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+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}.
+ * {@link UnfoldAnimationController}.
*/
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;
@@ -56,22 +57,26 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Nullable
private IBinder mTransition;
- private final FullscreenUnfoldTaskAnimator mFullscreenAnimator;
+ private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider,
- FullscreenUnfoldTaskAnimator animator, TransactionPool transactionPool,
- UnfoldBackgroundController unfoldBackgroundController,
+ FullscreenUnfoldTaskAnimator fullscreenUnfoldAnimator,
+ SplitTaskUnfoldAnimator splitUnfoldTaskAnimator,
+ TransactionPool transactionPool,
Executor executor, Transitions transitions) {
mUnfoldProgressProvider = unfoldProgressProvider;
- mFullscreenAnimator = animator;
mTransactionPool = transactionPool;
- mUnfoldBackgroundController = unfoldBackgroundController;
mExecutor = executor;
mTransitions = transitions;
+
+ mAnimators.add(splitUnfoldTaskAnimator);
+ mAnimators.add(fullscreenUnfoldAnimator);
}
public void init() {
- mFullscreenAnimator.init();
+ for (int i = 0; i < mAnimators.size(); i++) {
+ mAnimators.get(i).init();
+ }
mTransitions.addHandler(this);
mUnfoldProgressProvider.addListener(mExecutor, this);
}
@@ -83,44 +88,67 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@NonNull TransitionFinishCallback finishCallback) {
if (transition != mTransition) return false;
- mUnfoldBackgroundController.ensureBackground(startTransaction);
- startTransaction.apply();
-
- 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) {
- mFullscreenAnimator.addTask(change.getTaskInfo(), change.getLeash());
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.clearTasks();
+
+ info.getChanges().forEach(change -> {
+ if (change.getTaskInfo() != null
+ && change.getMode() == TRANSIT_CHANGE
+ && animator.isApplicableTask(change.getTaskInfo())) {
+ animator.onTaskAppeared(change.getTaskInfo(), change.getLeash());
+ }
+ });
+
+ if (animator.hasActiveTasks()) {
+ animator.prepareStartTransaction(startTransaction);
+ animator.prepareFinishTransaction(finishTransaction);
+ animator.start();
}
- });
+ }
- mFullscreenAnimator.resetAllSurfaces(finishTransaction);
- mUnfoldBackgroundController.removeBackground(finishTransaction);
+ startTransaction.apply();
mFinishCallback = finishCallback;
return true;
}
@Override
public void onStateChangeProgress(float progress) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- mFullscreenAnimator.applyAnimationProgress(progress, transaction);
- transaction.apply();
- mTransactionPool.release(transaction);
+ if (mTransition == null) return;
+
+ SurfaceControl.Transaction transaction = null;
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) {
+ transaction = mTransactionPool.acquire();
+ }
+
+ animator.applyAnimationProgress(progress, transaction);
+ }
+ }
+
+ if (transaction != null) {
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
}
@Override
public void onStateChangeFinished() {
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null, null);
- mFinishCallback = null;
- mTransition = null;
- mFullscreenAnimator.clearTasks();
+ if (mFinishCallback == null) return;
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.clearTasks();
+ animator.stop();
}
+
+ mFinishCallback.onTransitionFinished(null, null);
+ mFinishCallback = null;
+ mTransition = null;
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
index 6ec5512c22ec..eab82f00e962 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.unfold.animation;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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.TaskInfo;
import android.content.Context;
import android.graphics.Matrix;
@@ -33,6 +36,8 @@ import android.view.SurfaceControl.Transaction;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
/**
* This helper class contains logic that calculates scaling and cropping parameters
@@ -42,10 +47,10 @@ import com.android.wm.shell.common.DisplayInsetsController;
*
* This class is used by
* {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and
- * {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. They use independent
+ * {@link UnfoldAnimationController}. They use independent
* instances of FullscreenUnfoldTaskAnimator.
*/
-public class FullscreenUnfoldTaskAnimator implements
+public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
DisplayInsetsController.OnInsetsChangedListener {
private static final float[] FLOAT_9 = new float[9];
@@ -60,12 +65,15 @@ public class FullscreenUnfoldTaskAnimator implements
private final int mExpandedTaskBarHeight;
private final float mWindowCornerRadiusPx;
private final DisplayInsetsController mDisplayInsetsController;
+ private final UnfoldBackgroundController mBackgroundController;
private InsetsSource mTaskbarInsetsSource;
public FullscreenUnfoldTaskAnimator(Context context,
+ @NonNull UnfoldBackgroundController backgroundController,
DisplayInsetsController displayInsetsController) {
mDisplayInsetsController = displayInsetsController;
+ mBackgroundController = backgroundController;
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.taskbar_frame_height);
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
@@ -88,27 +96,32 @@ public class FullscreenUnfoldTaskAnimator implements
return mAnimationContextByTaskId.size() > 0;
}
- public void addTask(TaskInfo taskInfo, SurfaceControl leash) {
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
taskInfo);
mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
}
- public void onTaskInfoChanged(TaskInfo taskInfo) {
+ @Override
+ public void onTaskChanged(TaskInfo taskInfo) {
AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
if (animationContext != null) {
animationContext.update(mTaskbarInsetsSource, taskInfo);
}
}
- public void removeTask(TaskInfo taskInfo) {
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
mAnimationContextByTaskId.remove(taskInfo.taskId);
}
+ @Override
public void clearTasks() {
mAnimationContextByTaskId.clear();
}
+ @Override
public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
final AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
@@ -116,6 +129,7 @@ public class FullscreenUnfoldTaskAnimator implements
}
}
+ @Override
public void applyAnimationProgress(float progress, Transaction transaction) {
if (mAnimationContextByTaskId.size() == 0) return;
@@ -132,11 +146,29 @@ public class FullscreenUnfoldTaskAnimator implements
transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
.setMatrix(context.mLeash, context.mMatrix, FLOAT_9)
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx)
- .show(context.mLeash)
- ;
+ .show(context.mLeash);
}
}
+ @Override
+ public void prepareStartTransaction(Transaction transaction) {
+ mBackgroundController.ensureBackground(transaction);
+ }
+
+ @Override
+ public void prepareFinishTransaction(Transaction transaction) {
+ mBackgroundController.removeBackground(transaction);
+ }
+
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return taskInfo != null && taskInfo.isVisible()
+ && taskInfo.realActivity != null // to filter out parents created by organizer
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+
+ @Override
public void resetAllSurfaces(Transaction transaction) {
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index 59eecb5db136..6e10ebe94c5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,17 +14,19 @@
* limitations under the License.
*/
-package com.android.wm.shell.splitscreen;
+package com.android.wm.shell.unfold.animation;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
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.Insets;
import android.graphics.Rect;
@@ -32,67 +34,130 @@ 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;
-import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
-import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import dagger.Lazy;
+
/**
- * Controls transformations of the split screen task surfaces in response
- * to the unfolding/folding action on foldable devices
+ * 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 UnfoldAnimationController}.
+ * They use independent instances of SplitTaskUnfoldAnimator.
*/
-public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
+public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
+ DisplayInsetsController.OnInsetsChangedListener, SplitScreenListener {
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
- private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
- private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
- private final DisplayInsetsController mDisplayInsetsController;
- private final UnfoldBackgroundController mBackgroundController;
private final Executor mExecutor;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
private final int mExpandedTaskBarHeight;
private final float mWindowCornerRadiusPx;
- private final Rect mStageBounds = new Rect();
- private final TransactionPool mTransactionPool;
+ private final Lazy<Optional<SplitScreenController>> mSplitScreenController;
+ private final UnfoldBackgroundController mUnfoldBackgroundController;
+
+ private final Rect mMainStageBounds = new Rect();
+ private final Rect mSideStageBounds = new Rect();
+ private final Rect mRootStageBounds = new Rect();
private InsetsSource mTaskbarInsetsSource;
- private boolean mBothStagesVisible;
-
- public StageTaskUnfoldController(@NonNull Context context,
- @NonNull TransactionPool transactionPool,
- @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
- @NonNull DisplayInsetsController displayInsetsController,
- @NonNull UnfoldBackgroundController backgroundController,
- @NonNull Executor executor) {
- mUnfoldProgressProvider = unfoldProgressProvider;
- mTransactionPool = transactionPool;
- mExecutor = executor;
- mBackgroundController = backgroundController;
+
+ @SplitPosition
+ private int mMainStagePosition = SPLIT_POSITION_UNDEFINED;
+ @SplitPosition
+ private int mSideStagePosition = SPLIT_POSITION_UNDEFINED;
+
+ public SplitTaskUnfoldAnimator(Context context, Executor executor,
+ Lazy<Optional<SplitScreenController>> splitScreenController,
+ UnfoldBackgroundController unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController) {
mDisplayInsetsController = displayInsetsController;
- mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mExecutor = executor;
+ mUnfoldBackgroundController = unfoldBackgroundController;
+ mSplitScreenController = splitScreenController;
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.taskbar_frame_height);
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
}
- /**
- * Initializes the controller, starts listening for the external events
- */
+ /** Initializes the animator, this should be called only once */
+ @Override
public void init() {
- mUnfoldProgressProvider.addListener(mExecutor, this);
mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
}
+ /**
+ * Starts listening for split-screen changes and gets initial split-screen
+ * layout information through the listener
+ */
+ @Override
+ public void start() {
+ mSplitScreenController.get().get().asSplitScreen()
+ .registerSplitScreenListener(this, mExecutor);
+ }
+
+ /**
+ * Stops listening for the split-screen layout changes
+ */
+ @Override
+ public void stop() {
+ mSplitScreenController.get().get().asSplitScreen()
+ .unregisterSplitScreenListener(this);
+ }
+
@Override
public void insetsChanged(InsetsState insetsState) {
mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ updateContexts();
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ final AnimationContext context = mAnimationContextByTaskId.get(taskId);
+ if (context != null) {
+ context.mStageType = stage;
+ context.update();
+ }
+ }
+
+ @Override
+ public void onStagePositionChanged(int stage, int position) {
+ if (stage == STAGE_TYPE_MAIN) {
+ mMainStagePosition = position;
+ } else {
+ mSideStagePosition = position;
+ }
+ updateContexts();
+ }
+
+ @Override
+ public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
+ mRootStageBounds.set(rootBounds);
+ mMainStageBounds.set(mainBounds);
+ mSideStageBounds.set(sideBounds);
+ updateContexts();
+ }
+
+ private void updateContexts() {
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
context.update();
@@ -100,44 +165,73 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
}
/**
- * Called when split screen task appeared
- * @param taskInfo info for the appeared task
- * @param leash surface leash for the appeared task
+ * Register a split task in the animator
+ * @param taskInfo info of the task
+ * @param leash the surface of the task
*/
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- // Only handle child task surface here.
- if (!taskInfo.hasParentTask()) return;
-
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
AnimationContext context = new AnimationContext(leash);
mAnimationContextByTaskId.put(taskInfo.taskId, context);
}
/**
- * Called when a split screen task vanished
- * @param taskInfo info for the vanished task
+ * Unregister the task from the unfold animation
+ * @param taskInfo info of the task
+ */
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return taskInfo.hasParentTask()
+ && taskInfo.isVisible
+ && taskInfo.realActivity != null // to filter out parents created by organizer
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW;
+ }
+
+ /**
+ * Clear all registered tasks
*/
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (!taskInfo.hasParentTask()) return;
+ @Override
+ public void clearTasks() {
+ mAnimationContextByTaskId.clear();
+ }
+ /**
+ * Reset transformations of the task that could have been applied by the animator
+ * @param taskInfo task to reset
+ * @param transaction a transaction to write the changes to
+ */
+ @Override
+ public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
resetSurface(transaction, context);
- transaction.apply();
- mTransactionPool.release(transaction);
}
- mAnimationContextByTaskId.remove(taskInfo.taskId);
}
+ /**
+ * Reset all surface transformation that could have been introduced by the animator
+ * @param transaction to write changes to
+ */
@Override
- public void onStateChangeProgress(float progress) {
- if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
-
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- mBackgroundController.ensureBackground(transaction);
+ public void resetAllSurfaces(Transaction transaction) {
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(transaction, context);
+ }
+ }
+ @Override
+ public void applyAnimationProgress(float progress, Transaction transaction) {
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ if (context.mStageType == STAGE_TYPE_UNDEFINED) {
+ continue;
+ }
context.mCurrentCropRect.set(RECT_EVALUATOR
.evaluate(progress, context.mStartCropRect, context.mEndCropRect));
@@ -145,53 +239,25 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
}
-
- transaction.apply();
-
- mTransactionPool.release(transaction);
}
@Override
- public void onStateChangeFinished() {
- resetTransformations();
+ public void prepareStartTransaction(Transaction transaction) {
+ mUnfoldBackgroundController.ensureBackground(transaction);
+ mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction);
}
- /**
- * Called when split screen visibility changes
- * @param bothStagesVisible true if both stages of the split screen are visible
- */
- public void onSplitVisibilityChanged(boolean bothStagesVisible) {
- mBothStagesVisible = bothStagesVisible;
- if (!bothStagesVisible) {
- resetTransformations();
- }
+ @Override
+ public void prepareFinishTransaction(Transaction transaction) {
+ mUnfoldBackgroundController.removeBackground(transaction);
}
/**
- * Called when split screen stage bounds changed
- * @param bounds new bounds for this stage
+ * @return true if there are tasks to animate
*/
- public void onLayoutChanged(Rect bounds, @SplitPosition int splitPosition,
- boolean isLandscape) {
- mStageBounds.set(bounds);
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update(splitPosition, isLandscape);
- }
- }
-
- private void resetTransformations() {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- resetSurface(transaction, context);
- }
- mBackgroundController.removeBackground(transaction);
- transaction.apply();
-
- mTransactionPool.release(transaction);
+ @Override
+ public boolean hasActiveTasks() {
+ return mAnimationContextByTaskId.size() > 0;
}
private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
@@ -202,26 +268,24 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
private class AnimationContext {
final SurfaceControl mLeash;
+
final Rect mStartCropRect = new Rect();
final Rect mEndCropRect = new Rect();
final Rect mCurrentCropRect = new Rect();
- private @SplitPosition int mSplitPosition = SPLIT_POSITION_UNDEFINED;
- private boolean mIsLandscape = false;
+ @SplitScreen.StageType
+ int mStageType = STAGE_TYPE_UNDEFINED;
private AnimationContext(SurfaceControl leash) {
- this.mLeash = leash;
- update();
- }
-
- private void update(@SplitPosition int splitPosition, boolean isLandscape) {
- this.mSplitPosition = splitPosition;
- this.mIsLandscape = isLandscape;
+ mLeash = leash;
update();
}
private void update() {
- mStartCropRect.set(mStageBounds);
+ final Rect stageBounds = mStageType == STAGE_TYPE_MAIN
+ ? mMainStageBounds : mSideStageBounds;
+
+ mStartCropRect.set(stageBounds);
boolean taskbarExpanded = isTaskbarExpanded();
if (taskbarExpanded) {
@@ -239,7 +303,8 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
// Sides adjacent to split bar or task bar are not be animated.
Insets margins;
- if (mIsLandscape) { // Left and right splits.
+ final boolean isLandscape = mRootStageBounds.width() > mRootStageBounds.height();
+ if (isLandscape) { // Left and right splits.
margins = getLandscapeMargins(margin, taskbarExpanded);
} else { // Top and bottom splits.
margins = getPortraitMargins(margin, taskbarExpanded);
@@ -251,7 +316,9 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
int left = margin;
int right = margin;
int bottom = taskbarExpanded ? 0 : margin; // Taskbar margin.
- if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ final int splitPosition = mStageType == STAGE_TYPE_MAIN
+ ? mMainStagePosition : mSideStagePosition;
+ if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
right = 0; // Divider margin.
} else {
left = 0; // Divider margin.
@@ -262,7 +329,9 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
private Insets getPortraitMargins(int margin, boolean taskbarExpanded) {
int bottom = margin;
int top = margin;
- if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ final int splitPosition = mStageType == STAGE_TYPE_MAIN
+ ? mMainStagePosition : mSideStagePosition;
+ if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
bottom = 0; // Divider margin.
} else { // Bottom split.
top = 0; // Divider margin.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java
new file mode 100644
index 000000000000..e1e366301b46
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java
@@ -0,0 +1,117 @@
+/*
+ * 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 android.app.TaskInfo;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Interface for classes that handle animations of tasks when folding or unfolding
+ * foldable devices.
+ */
+public interface UnfoldTaskAnimator {
+ /**
+ * Initializes the animator, this should be called once in the lifetime of the animator
+ */
+ default void init() {}
+
+ /**
+ * Starts the animator, it might start listening for some events from the system.
+ * Applying animation should be done only when animator is started.
+ * Animator could be started/stopped several times.
+ */
+ default void start() {}
+
+ /**
+ * Stops the animator, it could unsubscribe from system events.
+ */
+ default void stop() {}
+
+ /**
+ * If this method returns true then task updates will be propagated to
+ * the animator using the onTaskAppeared/Changed/Vanished callbacks.
+ * @return true if this task should be animated by this animator
+ */
+ default boolean isApplicableTask(TaskInfo taskInfo) {
+ return false;
+ }
+
+ /**
+ * Called whenever a task applicable to this animator appeared
+ * (isApplicableTask returns true for this task)
+ *
+ * @param taskInfo info of the appeared task
+ * @param leash surface of the task
+ */
+ default void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {}
+
+ /**
+ * Called whenever a task applicable to this animator changed
+ * @param taskInfo info of the changed task
+ */
+ default void onTaskChanged(TaskInfo taskInfo) {}
+
+ /**
+ * Called whenever a task applicable to this animator vanished
+ * @param taskInfo info of the vanished task
+ */
+ default void onTaskVanished(TaskInfo taskInfo) {}
+
+ /**
+ * @return true if there tasks that could be potentially animated
+ */
+ default boolean hasActiveTasks() {
+ return false;
+ }
+
+ /**
+ * Clears all registered tasks in the animator
+ */
+ default void clearTasks() {}
+
+ /**
+ * Apply task surfaces transformations based on the current unfold progress
+ * @param progress unfold transition progress
+ * @param transaction to write changes to
+ */
+ default void applyAnimationProgress(float progress, Transaction transaction) {}
+
+ /**
+ * Apply task surfaces transformations that should be set before starting the animation
+ * @param transaction to write changes to
+ */
+ default void prepareStartTransaction(Transaction transaction) {}
+
+ /**
+ * Apply task surfaces transformations that should be set after finishing the animation
+ * @param transaction to write changes to
+ */
+ default void prepareFinishTransaction(Transaction transaction) {}
+
+ /**
+ * Resets task surface to its initial transformation
+ * @param transaction to write changes to
+ */
+ default void resetSurface(TaskInfo taskInfo, Transaction transaction) {}
+
+ /**
+ * Resets all task surfaces to their initial transformations
+ * @param transaction to write changes to
+ */
+ default void resetAllSurfaces(Transaction transaction) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java
new file mode 100644
index 000000000000..4c868305dcdb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java
@@ -0,0 +1,29 @@
+/*
+ * 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.qualifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Indicates that this class is used for the shell unfold transition
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UnfoldShellTransition {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java
new file mode 100644
index 000000000000..4d2b3e6f899b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java
@@ -0,0 +1,30 @@
+/*
+ * 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.qualifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Indicates that this class is used for unfold transition implemented
+ * without using Shell transitions
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UnfoldTransition {}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index a6caefe6d3e7..0b53c4069c3f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -133,7 +133,7 @@ public class ShellTaskOrganizerTests {
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mCompatUI, Optional.empty()));
+ mCompatUI, Optional.empty(), Optional.empty()));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
deleted file mode 100644
index 4523e2c9cba5..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.wm.shell.fullscreen;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-
-import static org.junit.Assume.assumeFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.view.SurfaceControl;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.recents.RecentTasksController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-@SmallTest
-public class FullscreenTaskListenerTest {
- private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
-
- @Mock
- private SyncTransactionQueue mSyncQueue;
- @Mock
- private FullscreenUnfoldController mUnfoldController;
- @Mock
- private RecentTasksController mRecentTasksController;
- @Mock
- private SurfaceControl mSurfaceControl;
-
- private Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
-
- private FullscreenTaskListener mListener;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mFullscreenUnfoldController = Optional.of(mUnfoldController);
- mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController,
- Optional.empty());
- }
-
- @Test
- public void testAnimatableTaskAppeared_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0);
-
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- verify(mUnfoldController).onTaskAppeared(eq(info), any());
- }
-
- @Test
- public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0);
- RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1);
-
- mListener.onTaskAppeared(animatable1, mSurfaceControl);
- mListener.onTaskAppeared(animatable2, mSurfaceControl);
-
- InOrder order = inOrder(mUnfoldController);
- order.verify(mUnfoldController).onTaskAppeared(eq(animatable1), any());
- order.verify(mUnfoldController).onTaskAppeared(eq(animatable2), any());
- }
-
- @Test
- public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
-
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- mListener.onTaskInfoChanged(info);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- mListener.onTaskVanished(info);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testAnimatableTaskBecameInactive_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0);
- mListener.onTaskAppeared(animatableTask, mSurfaceControl);
- RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0);
-
- mListener.onTaskInfoChanged(notAnimatableTask);
-
- verify(mUnfoldController).onTaskVanished(eq(notAnimatableTask));
- }
-
- @Test
- public void testAnimatableTaskVanished_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0);
- mListener.onTaskAppeared(taskInfo, mSurfaceControl);
-
- mListener.onTaskVanished(taskInfo);
-
- verify(mUnfoldController).onTaskVanished(eq(taskInfo));
- }
-
- private RunningTaskInfo createTaskInfo(boolean visible, int taskId) {
- final RunningTaskInfo info = spy(new RunningTaskInfo());
- info.isVisible = visible;
- info.positionInParent = new Point();
- when(info.getWindowingMode()).thenReturn(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
- final Configuration configuration = new Configuration();
- configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- when(info.getConfiguration()).thenReturn(configuration);
- info.taskId = taskId;
- return info;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index 440a6f8fb59a..1eadeed7cd3b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -88,7 +88,7 @@ public class KidsModeTaskOrganizerTest {
// NOTE: KidsModeTaskOrganizer should have a null CompatUIController.
mOrganizer = spy(new KidsModeTaskOrganizer(mTaskOrganizerController, mTestExecutor,
mHandler, mContext, mSyncTransactionQueue, mDisplayController,
- mDisplayInsetsController, Optional.empty(), mObserver));
+ mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver));
mOrganizer.initialize(mStartingWindowController);
doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 50f6bd7b4927..9191b1564de2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -80,7 +80,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
- null /* sizeCompatUI */, Optional.of(mRecentTasksController));
+ null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 0639ad5d0a62..68cb57c14d8c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -61,7 +61,7 @@ public class MainStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, null);
+ mSyncQueue, mSurfaceSession, mIconProvider);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index a31aa58bdc26..3b42a48b5a40 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -66,7 +66,7 @@ public class SideStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, null);
+ mSyncQueue, mSurfaceSession, mIconProvider);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index eb9d3a11d285..a67853cfe745 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -40,8 +40,6 @@ import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
-import javax.inject.Provider;
-
public class SplitTestUtils {
static SplitLayout createMockSplitLayout() {
@@ -74,12 +72,10 @@ public class SplitTestUtils {
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldController) {
+ Optional<RecentTasksController> recentTasks) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, logger, mainExecutor, recentTasks,
- unfoldController);
+ transitions, transactionPool, logger, mainExecutor, recentTasks);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index b52690487944..304ca66dd3bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -118,16 +118,16 @@ public class SplitTransitionTests extends ShellTestCase {
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider, null);
+ mIconProvider);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider, null);
+ mIconProvider);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty);
+ mTransactionPool, mLogger, mMainExecutor, Optional.empty());
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 42d998f6b0ee..af2c495c85c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,6 +59,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -68,8 +70,6 @@ import org.mockito.MockitoAnnotations;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Tests for {@link StageCoordinator}
*/
@@ -85,10 +85,6 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private SideStage mSideStage;
@Mock
- private StageTaskUnfoldController mMainUnfoldController;
- @Mock
- private StageTaskUnfoldController mSideUnfoldController;
- @Mock
private SplitLayout mSplitLayout;
@Mock
private DisplayController mDisplayController;
@@ -107,6 +103,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
+ private final Rect mRootBounds = new Rect(0, 0, 45, 60);
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mRootLeash;
@@ -119,11 +116,12 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
- mMainExecutor, Optional.empty(), new UnfoldControllerProvider()));
+ mMainExecutor, Optional.empty()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
+ when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
when(mSplitLayout.isLandscape()).thenReturn(false);
mRootTask = new TestRunningTaskInfoBuilder().build();
@@ -168,13 +166,6 @@ public class StageCoordinatorTests extends ShellTestCase {
}
@Test
- public void testRootTaskAppeared_initializesUnfoldControllers() {
- verify(mMainUnfoldController).init();
- verify(mSideUnfoldController).init();
- verify(mStageCoordinator).onRootTaskAppeared();
- }
-
- @Test
public void testRootTaskInfoChanged_updatesSplitLayout() {
mStageCoordinator.onTaskInfoChanged(mRootTask);
@@ -184,26 +175,25 @@ public class StageCoordinatorTests extends ShellTestCase {
@Test
public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
- clearInvocations(mMainUnfoldController, mSideUnfoldController);
+ final SplitScreenListener listener = mock(SplitScreenListener.class);
+ mStageCoordinator.registerSplitScreenListener(listener);
+ clearInvocations(listener);
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- false);
- verify(mSideUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT, false);
+ verify(listener).onSplitBoundsChanged(mRootBounds, mBounds2, mBounds1);
}
@Test
public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
- clearInvocations(mMainUnfoldController, mSideUnfoldController);
+ final SplitScreenListener listener = mock(SplitScreenListener.class);
+ mStageCoordinator.registerSplitScreenListener(listener);
+ clearInvocations(listener);
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT,
- false);
- verify(mSideUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- false);
+ verify(listener).onSplitBoundsChanged(mRootBounds, mBounds1, mBounds2);
}
@Test
@@ -314,20 +304,4 @@ public class StageCoordinatorTests extends ShellTestCase {
verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
}
-
- private class UnfoldControllerProvider implements
- Provider<Optional<StageTaskUnfoldController>> {
-
- private boolean isMain = true;
-
- @Override
- public Optional<StageTaskUnfoldController> get() {
- if (isMain) {
- isMain = false;
- return Optional.of(mMainUnfoldController);
- } else {
- return Optional.of(mSideUnfoldController);
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 157c30bcb6c7..5ee8bf3006a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -72,8 +71,6 @@ public final class StageTaskListenerTests extends ShellTestCase {
private SyncTransactionQueue mSyncQueue;
@Mock
private IconProvider mIconProvider;
- @Mock
- private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -92,8 +89,7 @@ public final class StageTaskListenerTests extends ShellTestCase {
mCallbacks,
mSyncQueue,
mSurfaceSession,
- mIconProvider,
- mStageTaskUnfoldController);
+ mIconProvider);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
@@ -130,30 +126,6 @@ public final class StageTaskListenerTests extends ShellTestCase {
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
- @Test
- public void testTaskAppeared_notifiesUnfoldListener() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo task =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
-
- mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
-
- verify(mStageTaskUnfoldController).onTaskAppeared(eq(task), eq(mSurfaceControl));
- }
-
- @Test
- public void testTaskVanished_notifiesUnfoldListener() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo task =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
- mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
- clearInvocations(mStageTaskUnfoldController);
-
- mStageTaskListener.onTaskVanished(task);
-
- verify(mStageTaskUnfoldController).onTaskVanished(eq(task));
- }
-
@Test(expected = IllegalArgumentException.class)
public void testUnknownTaskVanished() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
new file mode 100644
index 000000000000..798208956180
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
@@ -0,0 +1,341 @@
+/*
+ * 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;
+
+import static com.android.wm.shell.unfold.UnfoldAnimationControllerTest.TestUnfoldTaskAnimator.UNSET;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Predicate;
+
+/**
+ * Tests for {@link UnfoldAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:UnfoldAnimationControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+public class UnfoldAnimationControllerTest extends ShellTestCase {
+
+ @Mock
+ private TransactionPool mTransactionPool;
+ @Mock
+ private UnfoldTransitionHandler mUnfoldTransitionHandler;
+ @Mock
+ private SurfaceControl mLeash;
+
+ private UnfoldAnimationController mUnfoldAnimationController;
+
+ private final TestShellUnfoldProgressProvider mProgressProvider =
+ new TestShellUnfoldProgressProvider();
+ private final TestShellExecutor mShellExecutor = new TestShellExecutor();
+
+ private final TestUnfoldTaskAnimator mTaskAnimator1 = new TestUnfoldTaskAnimator();
+ private final TestUnfoldTaskAnimator mTaskAnimator2 = new TestUnfoldTaskAnimator();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mTransactionPool.acquire()).thenReturn(mock(SurfaceControl.Transaction.class));
+
+ final List<UnfoldTaskAnimator> animators = new ArrayList<>();
+ animators.add(mTaskAnimator1);
+ animators.add(mTaskAnimator2);
+ mUnfoldAnimationController = new UnfoldAnimationController(
+ mTransactionPool,
+ mProgressProvider,
+ animators,
+ () -> Optional.of(mUnfoldTransitionHandler),
+ mShellExecutor
+ );
+ }
+
+ @Test
+ public void testAppearedMatchingTask_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedMatchingTaskTwoDifferentAnimators_appliesUnfoldProgressToBoth() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 1);
+ mTaskAnimator2.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(1).build();
+ RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo1, mLeash);
+ mUnfoldAnimationController.onTaskAppeared(taskInfo2, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ assertThat(mTaskAnimator2.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedNonMatchingTask_doesNotApplyUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testAppearedAndChangedToNonMatchingTask_doesNotApplyUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(0);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testAppearedAndChangedToNonMatchingTaskAndBack_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(0);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(2);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedNonMatchingTaskAndChangedToMatching_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(2);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedMatchingTaskAndChanged_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testShellTransitionRunning_doesNotApplyUnfoldProgress() {
+ when(mUnfoldTransitionHandler.willHandleTransition()).thenReturn(true);
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testApplicableTaskDisappeared_resetsSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+ assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
+ }
+
+ @Test
+ public void testNonApplicableTaskAppearedDisappeared_doesNotResetSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+ }
+
+ @Test
+ public void testInit_initsAndStartsAnimators() {
+ mUnfoldAnimationController.init();
+
+ assertThat(mTaskAnimator1.mInitialized).isTrue();
+ assertThat(mTaskAnimator1.mStarted).isTrue();
+ }
+
+ private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
+ ShellUnfoldProgressProvider.UnfoldListener {
+
+ private final List<UnfoldListener> mListeners = new ArrayList<>();
+
+ @Override
+ public void addListener(Executor executor, UnfoldListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void onStateChangeStarted() {
+ mListeners.forEach(UnfoldListener::onStateChangeStarted);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ mListeners.forEach(unfoldListener -> unfoldListener.onStateChangeProgress(progress));
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ mListeners.forEach(UnfoldListener::onStateChangeFinished);
+ }
+ }
+
+ public static class TestUnfoldTaskAnimator implements UnfoldTaskAnimator {
+
+ public static final float UNSET = -1f;
+ private Predicate<TaskInfo> mTaskMatcher = (info) -> false;
+
+ Map<Integer, TaskInfo> mTasksMap = new HashMap<>();
+ Set<Integer> mResetTasks = new HashSet<>();
+
+ boolean mInitialized = false;
+ boolean mStarted = false;
+ float mLastAppliedProgress = UNSET;
+
+ @Override
+ public void init() {
+ mInitialized = true;
+ }
+
+ @Override
+ public void start() {
+ mStarted = true;
+ }
+
+ @Override
+ public void stop() {
+ mStarted = false;
+ }
+
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return mTaskMatcher.test(taskInfo);
+ }
+
+ @Override
+ public void applyAnimationProgress(float progress, Transaction transaction) {
+ mLastAppliedProgress = progress;
+ }
+
+ public void setTaskMatcher(Predicate<TaskInfo> taskMatcher) {
+ mTaskMatcher = taskMatcher;
+ }
+
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
+ mTasksMap.put(taskInfo.taskId, taskInfo);
+ }
+
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
+ mTasksMap.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public void onTaskChanged(TaskInfo taskInfo) {
+ mTasksMap.put(taskInfo.taskId, taskInfo);
+ }
+
+ @Override
+ public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
+ mResetTasks.add(taskInfo.taskId);
+ }
+
+ @Override
+ public void resetAllSurfaces(Transaction transaction) {
+ mTasksMap.values().forEach((t) -> mResetTasks.add(t.taskId));
+ }
+
+ @Override
+ public boolean hasActiveTasks() {
+ return mTasksMap.size() > 0;
+ }
+
+ public List<TaskInfo> getCurrentTasks() {
+ return new ArrayList<>(mTasksMap.values());
+ }
+ }
+}