Merge "Revert "Migrate unfold animation to Shell transitions [Part 3]"" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3b628d7c69
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -34,7 +35,6 @@ 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<UnfoldAnimationController> mUnfoldController;
|
||||
private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
|
||||
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<UnfoldAnimationController> unfoldAnimationController,
|
||||
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
Optional<FreeformTaskListener> freeformTaskListenerOptional,
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
@@ -93,7 +93,7 @@ public class ShellInitImpl {
|
||||
mSplitScreenOptional = splitScreenOptional;
|
||||
mFullscreenTaskListener = fullscreenTaskListener;
|
||||
mPipTouchHandlerOptional = pipTouchHandlerOptional;
|
||||
mUnfoldController = unfoldAnimationController;
|
||||
mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
|
||||
mUnfoldTransitionHandler = unfoldTransitionHandler;
|
||||
mFreeformTaskListenerOptional = freeformTaskListenerOptional;
|
||||
mRecentTasks = recentTasks;
|
||||
@@ -146,7 +146,7 @@ public class ShellInitImpl {
|
||||
mShellTaskOrganizer.addListenerForType(
|
||||
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
|
||||
|
||||
mUnfoldController.ifPresent(UnfoldAnimationController::init);
|
||||
mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
|
||||
mRecentTasks.ifPresent(RecentTasksController::init);
|
||||
|
||||
// Initialize kids mode task organizer
|
||||
|
||||
@@ -55,7 +55,6 @@ 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,42 +178,34 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
@Nullable
|
||||
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,
|
||||
unfoldAnimationController, recentTasks);
|
||||
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);
|
||||
}
|
||||
@@ -446,9 +437,6 @@ 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);
|
||||
}
|
||||
@@ -470,11 +458,6 @@ 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);
|
||||
@@ -524,10 +507,6 @@ 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);
|
||||
|
||||
@@ -178,11 +178,6 @@ 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);
|
||||
|
||||
@@ -64,6 +64,7 @@ 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;
|
||||
@@ -87,7 +88,6 @@ 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,11 +176,9 @@ 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, unfoldAnimationController,
|
||||
recentTasksOptional);
|
||||
return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@@ -192,12 +190,10 @@ 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, unfoldAnimationController,
|
||||
recentTasksOptional);
|
||||
displayController, displayInsetsController, recentTasksOptional);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@@ -294,11 +290,13 @@ 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, recentTasksOptional);
|
||||
return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
|
||||
recentTasksOptional);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,13 +310,12 @@ public abstract class WMShellBaseModule {
|
||||
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
|
||||
@BindsOptionalOf
|
||||
@DynamicOverride
|
||||
abstract UnfoldAnimationController optionalUnfoldController();
|
||||
abstract FullscreenUnfoldController optionalFullscreenUnfoldController();
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<UnfoldAnimationController> provideUnfoldController(
|
||||
@DynamicOverride Lazy<Optional<UnfoldAnimationController>>
|
||||
fullscreenUnfoldController,
|
||||
static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
|
||||
@DynamicOverride Lazy<Optional<FullscreenUnfoldController>> fullscreenUnfoldController,
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider) {
|
||||
if (progressProvider.isPresent()
|
||||
&& progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
|
||||
@@ -643,7 +640,7 @@ public abstract class WMShellBaseModule {
|
||||
Optional<SplitScreenController> splitScreenOptional,
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional,
|
||||
FullscreenTaskListener fullscreenTaskListener,
|
||||
Optional<UnfoldAnimationController> unfoldAnimationController,
|
||||
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
Optional<FreeformTaskListener> freeformTaskListener,
|
||||
Optional<RecentTasksController> recentTasksOptional,
|
||||
@@ -660,7 +657,7 @@ public abstract class WMShellBaseModule {
|
||||
splitScreenOptional,
|
||||
pipTouchHandlerOptional,
|
||||
fullscreenTaskListener,
|
||||
unfoldAnimationController,
|
||||
appUnfoldTransitionController,
|
||||
unfoldTransitionHandler,
|
||||
freeformTaskListener,
|
||||
recentTasksOptional,
|
||||
|
||||
@@ -45,6 +45,7 @@ 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,22 +68,17 @@ 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 dagger.Binds;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -96,7 +92,7 @@ import dagger.Provides;
|
||||
* dependencies should go into {@link WMShellBaseModule}.
|
||||
*/
|
||||
@Module(includes = WMShellBaseModule.class)
|
||||
public abstract class WMShellModule {
|
||||
public class WMShellModule {
|
||||
|
||||
//
|
||||
// Bubbles
|
||||
@@ -177,11 +173,12 @@ public abstract class WMShellModule {
|
||||
DisplayImeController displayImeController,
|
||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||
TransactionPool transactionPool, IconProvider iconProvider,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
|
||||
return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
|
||||
rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController,
|
||||
displayInsetsController, transitions, transactionPool, iconProvider,
|
||||
recentTasks);
|
||||
recentTasks, stageTaskUnfoldControllerProvider);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -336,77 +333,62 @@ public abstract class WMShellModule {
|
||||
//
|
||||
// Unfold transition
|
||||
//
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
@DynamicOverride
|
||||
static UnfoldAnimationController provideUnfoldAnimationController(
|
||||
static FullscreenUnfoldController provideFullscreenUnfoldController(
|
||||
Optional<ShellUnfoldProgressProvider> progressProvider,
|
||||
TransactionPool transactionPool,
|
||||
@UnfoldTransition SplitTaskUnfoldAnimator splitAnimator,
|
||||
FullscreenUnfoldTaskAnimator fullscreenAnimator,
|
||||
Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
FullscreenUnfoldTaskAnimator fullscreenUnfoldTaskAnimator,
|
||||
UnfoldBackgroundController unfoldBackgroundController,
|
||||
@ShellMainThread ShellExecutor mainExecutor
|
||||
) {
|
||||
final List<UnfoldTaskAnimator> animators = new ArrayList<>();
|
||||
animators.add(splitAnimator);
|
||||
animators.add(fullscreenAnimator);
|
||||
|
||||
return new UnfoldAnimationController(
|
||||
transactionPool,
|
||||
progressProvider.get(),
|
||||
animators,
|
||||
unfoldTransitionHandler,
|
||||
mainExecutor
|
||||
);
|
||||
return new FullscreenUnfoldController(mainExecutor,
|
||||
unfoldBackgroundController, progressProvider.get(),
|
||||
unfoldTransitionHandler.get(), fullscreenUnfoldTaskAnimator);
|
||||
}
|
||||
|
||||
|
||||
@Provides
|
||||
static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator(
|
||||
Context context,
|
||||
UnfoldBackgroundController unfoldBackgroundController,
|
||||
DisplayInsetsController displayInsetsController
|
||||
) {
|
||||
return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
|
||||
displayInsetsController);
|
||||
return new FullscreenUnfoldTaskAnimator(context, displayInsetsController);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimatorBase(
|
||||
Context context,
|
||||
UnfoldBackgroundController backgroundController,
|
||||
@ShellMainThread ShellExecutor executor,
|
||||
Lazy<Optional<SplitScreenController>> splitScreenOptional,
|
||||
DisplayInsetsController 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,
|
||||
@UnfoldShellTransition SplitTaskUnfoldAnimator unfoldAnimator,
|
||||
UnfoldBackgroundController backgroundController,
|
||||
TransactionPool transactionPool,
|
||||
Transitions transitions,
|
||||
@ShellMainThread ShellExecutor executor) {
|
||||
return new UnfoldTransitionHandler(progressProvider.get(), animator,
|
||||
unfoldAnimator, transactionPool, executor, transitions);
|
||||
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
|
||||
));
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
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;
|
||||
@@ -44,17 +48,22 @@ 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) {
|
||||
this(syncQueue, Optional.empty());
|
||||
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
|
||||
Optional<FullscreenUnfoldController> unfoldController) {
|
||||
this(syncQueue, unfoldController, Optional.empty());
|
||||
}
|
||||
|
||||
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
|
||||
Optional<FullscreenUnfoldController> unfoldController,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
mSyncQueue = syncQueue;
|
||||
mFullscreenUnfoldController = unfoldController.orElse(null);
|
||||
mRecentTasksOptional = recentTasks;
|
||||
}
|
||||
|
||||
@@ -67,6 +76,7 @@ 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 -> {
|
||||
@@ -84,6 +94,8 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
|
||||
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
|
||||
|
||||
updateRecentsForVisibleFullscreenTask(taskInfo);
|
||||
@@ -105,6 +117,7 @@ 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",
|
||||
@@ -162,4 +175,65 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,6 @@ 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;
|
||||
@@ -147,11 +146,9 @@ 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,
|
||||
unfoldAnimationController, recentTasks);
|
||||
super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null, recentTasks);
|
||||
mContext = context;
|
||||
mMainHandler = mainHandler;
|
||||
mSyncQueue = syncTransactionQueue;
|
||||
@@ -167,9 +164,8 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
|
||||
SyncTransactionQueue syncTransactionQueue,
|
||||
DisplayController displayController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
Optional<UnfoldAnimationController> unfoldAnimationController,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
super(mainExecutor, context, /* compatUI= */ null, unfoldAnimationController, recentTasks);
|
||||
super(mainExecutor, context, /* compatUI= */ null, recentTasks);
|
||||
mContext = context;
|
||||
mMainHandler = mainHandler;
|
||||
mSyncQueue = syncTransactionQueue;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.view.SurfaceSession;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -37,9 +38,10 @@ class MainStage extends StageTaskListener {
|
||||
|
||||
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider) {
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
||||
iconProvider);
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||
stageTaskUnfoldController);
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.view.SurfaceSession;
|
||||
@@ -37,9 +38,10 @@ class SideStage extends StageTaskListener {
|
||||
|
||||
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
|
||||
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider) {
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
|
||||
iconProvider);
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
|
||||
stageTaskUnfoldController);
|
||||
}
|
||||
|
||||
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
|
||||
|
||||
@@ -18,7 +18,6 @@ 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;
|
||||
@@ -59,7 +58,6 @@ 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) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@ 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}.
|
||||
@@ -136,6 +138,7 @@ 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
|
||||
@@ -149,7 +152,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
DisplayImeController displayImeController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||
mTaskOrganizer = shellTaskOrganizer;
|
||||
mSyncQueue = syncQueue;
|
||||
mContext = context;
|
||||
@@ -160,6 +164,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mTransitions = transitions;
|
||||
mTransactionPool = transactionPool;
|
||||
mUnfoldControllerProvider = unfoldControllerProvider;
|
||||
mLogger = new SplitscreenEventLogger();
|
||||
mIconProvider = iconProvider;
|
||||
mRecentTasksOptional = recentTasks;
|
||||
@@ -185,7 +190,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
|
||||
mTaskOrganizer, mDisplayController, mDisplayImeController,
|
||||
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
|
||||
mIconProvider, mMainExecutor, mRecentTasksOptional);
|
||||
mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,14 +226,6 @@ 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);
|
||||
@@ -541,17 +538,6 @@ 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++) {
|
||||
|
||||
@@ -118,6 +118,8 @@ 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.
|
||||
@@ -144,8 +146,10 @@ 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;
|
||||
@@ -206,7 +210,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
DisplayInsetsController displayInsetsController, Transitions transitions,
|
||||
TransactionPool transactionPool, SplitscreenEventLogger logger,
|
||||
IconProvider iconProvider, ShellExecutor mainExecutor,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||
mContext = context;
|
||||
mDisplayId = displayId;
|
||||
mSyncQueue = syncQueue;
|
||||
@@ -214,7 +219,8 @@ 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(
|
||||
@@ -224,7 +230,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mMainStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
iconProvider);
|
||||
iconProvider,
|
||||
mMainUnfoldController);
|
||||
mSideStage = new SideStage(
|
||||
mContext,
|
||||
mTaskOrganizer,
|
||||
@@ -232,7 +239,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSideStageListener,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
iconProvider);
|
||||
iconProvider,
|
||||
mSideUnfoldController);
|
||||
mDisplayController = displayController;
|
||||
mDisplayImeController = displayImeController;
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
@@ -255,7 +263,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
|
||||
Transitions transitions, TransactionPool transactionPool,
|
||||
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
|
||||
mContext = context;
|
||||
mDisplayId = displayId;
|
||||
mSyncQueue = syncQueue;
|
||||
@@ -269,6 +278,8 @@ 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;
|
||||
@@ -625,7 +636,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
onLayoutSizeChanged(mSplitLayout);
|
||||
} else {
|
||||
updateWindowBounds(mSplitLayout, wct);
|
||||
sendOnBoundsChanged();
|
||||
updateUnfoldBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -855,10 +866,6 @@ 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);
|
||||
}
|
||||
@@ -871,14 +878,6 @@ 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;
|
||||
@@ -942,7 +941,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
final SplitScreen.SplitScreenListener l = mListeners.get(i);
|
||||
l.onSplitVisibilityChanged(mDividerVisible);
|
||||
}
|
||||
sendOnBoundsChanged();
|
||||
|
||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||
mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
|
||||
mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
|
||||
updateUnfoldBounds();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -963,6 +967,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
|
||||
}
|
||||
|
||||
if (mMainUnfoldController != null && mSideUnfoldController != null) {
|
||||
mMainUnfoldController.init();
|
||||
mSideUnfoldController.init();
|
||||
}
|
||||
|
||||
onRootTaskAppeared();
|
||||
}
|
||||
|
||||
@@ -976,8 +985,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mRootTaskInfo = taskInfo;
|
||||
if (mSplitLayout != null
|
||||
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
|
||||
&& mMainStage.isActive()
|
||||
&& !ENABLE_SHELL_TRANSITIONS) {
|
||||
&& 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;
|
||||
}
|
||||
// Clear the divider remote animating flag as the divider will be re-rendered to apply
|
||||
// the new rotation config.
|
||||
mIsDividerRemoteAnimating = false;
|
||||
@@ -1227,7 +1241,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
public void onLayoutSizeChanged(SplitLayout layout) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
updateWindowBounds(layout, wct);
|
||||
sendOnBoundsChanged();
|
||||
updateUnfoldBounds();
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
setResizingSplits(false /* resizing */);
|
||||
@@ -1238,6 +1252,15 @@ 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();
|
||||
}
|
||||
@@ -1317,11 +1340,6 @@ 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;
|
||||
@@ -1334,7 +1352,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
|
||||
}
|
||||
updateWindowBounds(mSplitLayout, wct);
|
||||
sendOnBoundsChanged();
|
||||
updateUnfoldBounds();
|
||||
}
|
||||
|
||||
private void onFoldedStateChanged(boolean folded) {
|
||||
|
||||
@@ -97,14 +97,18 @@ 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) {
|
||||
SurfaceSession surfaceSession, IconProvider iconProvider,
|
||||
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
|
||||
mContext = context;
|
||||
mCallbacks = callbacks;
|
||||
mSyncQueue = syncQueue;
|
||||
mSurfaceSession = surfaceSession;
|
||||
mIconProvider = iconProvider;
|
||||
mStageTaskUnfoldController = stageTaskUnfoldController;
|
||||
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
|
||||
}
|
||||
|
||||
@@ -195,6 +199,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
+ "\n mRootTaskInfo: " + mRootTaskInfo);
|
||||
}
|
||||
|
||||
if (mStageTaskUnfoldController != null) {
|
||||
mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,6 +270,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
+ "\n mRootTaskInfo: " + mRootTaskInfo);
|
||||
}
|
||||
|
||||
if (mStageTaskUnfoldController != null) {
|
||||
mStageTaskUnfoldController.onTaskVanished(taskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.unfold.animation;
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
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.app.TaskInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
@@ -34,130 +32,67 @@ 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.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.ShellUnfoldProgressProvider;
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
|
||||
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Controls transformations of the split screen task surfaces in response
|
||||
* to the unfolding/folding action on foldable devices
|
||||
*/
|
||||
public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
DisplayInsetsController.OnInsetsChangedListener, SplitScreenListener {
|
||||
public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
|
||||
|
||||
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
|
||||
private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final DisplayInsetsController mDisplayInsetsController;
|
||||
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 int mExpandedTaskBarHeight;
|
||||
private final float mWindowCornerRadiusPx;
|
||||
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 final Rect mStageBounds = new Rect();
|
||||
private final TransactionPool mTransactionPool;
|
||||
|
||||
private InsetsSource mTaskbarInsetsSource;
|
||||
private boolean mBothStagesVisible;
|
||||
|
||||
@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;
|
||||
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;
|
||||
mUnfoldBackgroundController = unfoldBackgroundController;
|
||||
mSplitScreenController = splitScreenController;
|
||||
mBackgroundController = backgroundController;
|
||||
mDisplayInsetsController = displayInsetsController;
|
||||
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.taskbar_frame_height);
|
||||
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
}
|
||||
|
||||
/** Initializes the animator, this should be called only once */
|
||||
@Override
|
||||
/**
|
||||
* Initializes the controller, starts listening for the external events
|
||||
*/
|
||||
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();
|
||||
@@ -165,73 +100,44 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a split task in the animator
|
||||
* @param taskInfo info of the task
|
||||
* @param leash the surface of the task
|
||||
* Called when split screen task appeared
|
||||
* @param taskInfo info for the appeared task
|
||||
* @param leash surface leash for the appeared task
|
||||
*/
|
||||
@Override
|
||||
public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
// Only handle child task surface here.
|
||||
if (!taskInfo.hasParentTask()) return;
|
||||
|
||||
AnimationContext context = new AnimationContext(leash);
|
||||
mAnimationContextByTaskId.put(taskInfo.taskId, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the task from the unfold animation
|
||||
* @param taskInfo info of the task
|
||||
* Called when a split screen task vanished
|
||||
* @param taskInfo info for the vanished task
|
||||
*/
|
||||
@Override
|
||||
public void onTaskVanished(TaskInfo taskInfo) {
|
||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
if (!taskInfo.hasParentTask()) return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
public void onStateChangeProgress(float progress) {
|
||||
if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
|
||||
|
||||
/**
|
||||
* Clear all registered tasks
|
||||
*/
|
||||
@Override
|
||||
public void clearTasks() {
|
||||
mAnimationContextByTaskId.clear();
|
||||
}
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
mBackgroundController.ensureBackground(transaction);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
resetSurface(transaction, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all surface transformation that could have been introduced by the animator
|
||||
* @param transaction to write changes to
|
||||
*/
|
||||
@Override
|
||||
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));
|
||||
@@ -239,25 +145,53 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
|
||||
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
|
||||
}
|
||||
|
||||
transaction.apply();
|
||||
|
||||
mTransactionPool.release(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareStartTransaction(Transaction transaction) {
|
||||
mUnfoldBackgroundController.ensureBackground(transaction);
|
||||
mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFinishTransaction(Transaction transaction) {
|
||||
mUnfoldBackgroundController.removeBackground(transaction);
|
||||
public void onStateChangeFinished() {
|
||||
resetTransformations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there are tasks to animate
|
||||
* Called when split screen visibility changes
|
||||
* @param bothStagesVisible true if both stages of the split screen are visible
|
||||
*/
|
||||
@Override
|
||||
public boolean hasActiveTasks() {
|
||||
return mAnimationContextByTaskId.size() > 0;
|
||||
public void onSplitVisibilityChanged(boolean bothStagesVisible) {
|
||||
mBothStagesVisible = bothStagesVisible;
|
||||
if (!bothStagesVisible) {
|
||||
resetTransformations();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when split screen stage bounds changed
|
||||
* @param bounds new bounds for this stage
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
|
||||
@@ -268,24 +202,26 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
|
||||
private class AnimationContext {
|
||||
final SurfaceControl mLeash;
|
||||
|
||||
final Rect mStartCropRect = new Rect();
|
||||
final Rect mEndCropRect = new Rect();
|
||||
final Rect mCurrentCropRect = new Rect();
|
||||
|
||||
@SplitScreen.StageType
|
||||
int mStageType = STAGE_TYPE_UNDEFINED;
|
||||
private @SplitPosition int mSplitPosition = SPLIT_POSITION_UNDEFINED;
|
||||
private boolean mIsLandscape = false;
|
||||
|
||||
private AnimationContext(SurfaceControl leash) {
|
||||
mLeash = leash;
|
||||
this.mLeash = leash;
|
||||
update();
|
||||
}
|
||||
|
||||
private void update(@SplitPosition int splitPosition, boolean isLandscape) {
|
||||
this.mSplitPosition = splitPosition;
|
||||
this.mIsLandscape = isLandscape;
|
||||
update();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
final Rect stageBounds = mStageType == STAGE_TYPE_MAIN
|
||||
? mMainStageBounds : mSideStageBounds;
|
||||
|
||||
mStartCropRect.set(stageBounds);
|
||||
mStartCropRect.set(mStageBounds);
|
||||
|
||||
boolean taskbarExpanded = isTaskbarExpanded();
|
||||
if (taskbarExpanded) {
|
||||
@@ -303,8 +239,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
|
||||
// Sides adjacent to split bar or task bar are not be animated.
|
||||
Insets margins;
|
||||
final boolean isLandscape = mRootStageBounds.width() > mRootStageBounds.height();
|
||||
if (isLandscape) { // Left and right splits.
|
||||
if (mIsLandscape) { // Left and right splits.
|
||||
margins = getLandscapeMargins(margin, taskbarExpanded);
|
||||
} else { // Top and bottom splits.
|
||||
margins = getPortraitMargins(margin, taskbarExpanded);
|
||||
@@ -316,9 +251,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
int left = margin;
|
||||
int right = margin;
|
||||
int bottom = taskbarExpanded ? 0 : margin; // Taskbar margin.
|
||||
final int splitPosition = mStageType == STAGE_TYPE_MAIN
|
||||
? mMainStagePosition : mSideStagePosition;
|
||||
if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
|
||||
if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
|
||||
right = 0; // Divider margin.
|
||||
} else {
|
||||
left = 0; // Divider margin.
|
||||
@@ -329,9 +262,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
|
||||
private Insets getPortraitMargins(int margin, boolean taskbarExpanded) {
|
||||
int bottom = margin;
|
||||
int top = margin;
|
||||
final int splitPosition = mStageType == STAGE_TYPE_MAIN
|
||||
? mMainStagePosition : mSideStagePosition;
|
||||
if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
|
||||
if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
|
||||
bottom = 0; // Divider margin.
|
||||
} else { // Bottom split.
|
||||
top = 0; // Divider margin.
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
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;
|
||||
@@ -33,22 +35,19 @@ 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 UnfoldAnimationController}.
|
||||
* {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}.
|
||||
*/
|
||||
public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
|
||||
|
||||
private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
|
||||
private final Transitions mTransitions;
|
||||
private final UnfoldBackgroundController mUnfoldBackgroundController;
|
||||
private final Executor mExecutor;
|
||||
private final TransactionPool mTransactionPool;
|
||||
|
||||
@@ -57,26 +56,22 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
@Nullable
|
||||
private IBinder mTransition;
|
||||
|
||||
private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
|
||||
private final FullscreenUnfoldTaskAnimator mFullscreenAnimator;
|
||||
|
||||
public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider,
|
||||
FullscreenUnfoldTaskAnimator fullscreenUnfoldAnimator,
|
||||
SplitTaskUnfoldAnimator splitUnfoldTaskAnimator,
|
||||
TransactionPool transactionPool,
|
||||
FullscreenUnfoldTaskAnimator animator, TransactionPool transactionPool,
|
||||
UnfoldBackgroundController unfoldBackgroundController,
|
||||
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() {
|
||||
for (int i = 0; i < mAnimators.size(); i++) {
|
||||
mAnimators.get(i).init();
|
||||
}
|
||||
mFullscreenAnimator.init();
|
||||
mTransitions.addHandler(this);
|
||||
mUnfoldProgressProvider.addListener(mExecutor, this);
|
||||
}
|
||||
@@ -88,67 +83,44 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
|
||||
@NonNull TransitionFinishCallback finishCallback) {
|
||||
if (transition != mTransition) return false;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
mFullscreenAnimator.resetAllSurfaces(finishTransaction);
|
||||
mUnfoldBackgroundController.removeBackground(finishTransaction);
|
||||
mFinishCallback = finishCallback;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeProgress(float progress) {
|
||||
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);
|
||||
}
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
mFullscreenAnimator.applyAnimationProgress(progress, transaction);
|
||||
transaction.apply();
|
||||
mTransactionPool.release(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChangeFinished() {
|
||||
if (mFinishCallback == null) return;
|
||||
|
||||
for (int i = 0; i < mAnimators.size(); i++) {
|
||||
final UnfoldTaskAnimator animator = mAnimators.get(i);
|
||||
animator.clearTasks();
|
||||
animator.stop();
|
||||
if (mFinishCallback != null) {
|
||||
mFinishCallback.onTransitionFinished(null, null);
|
||||
mFinishCallback = null;
|
||||
mTransition = null;
|
||||
mFullscreenAnimator.clearTasks();
|
||||
}
|
||||
|
||||
mFinishCallback.onTransitionFinished(null, null);
|
||||
mFinishCallback = null;
|
||||
mTransition = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
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;
|
||||
@@ -36,8 +33,6 @@ 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
|
||||
@@ -47,10 +42,10 @@ import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||
*
|
||||
* This class is used by
|
||||
* {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and
|
||||
* {@link UnfoldAnimationController}. They use independent
|
||||
* {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. They use independent
|
||||
* instances of FullscreenUnfoldTaskAnimator.
|
||||
*/
|
||||
public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
|
||||
public class FullscreenUnfoldTaskAnimator implements
|
||||
DisplayInsetsController.OnInsetsChangedListener {
|
||||
|
||||
private static final float[] FLOAT_9 = new float[9];
|
||||
@@ -65,15 +60,12 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
|
||||
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);
|
||||
@@ -96,32 +88,27 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
|
||||
return mAnimationContextByTaskId.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
|
||||
public void addTask(TaskInfo taskInfo, SurfaceControl leash) {
|
||||
AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
|
||||
taskInfo);
|
||||
mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskChanged(TaskInfo taskInfo) {
|
||||
public void onTaskInfoChanged(TaskInfo taskInfo) {
|
||||
AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
|
||||
if (animationContext != null) {
|
||||
animationContext.update(mTaskbarInsetsSource, taskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(TaskInfo taskInfo) {
|
||||
public void removeTask(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) {
|
||||
@@ -129,7 +116,6 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAnimationProgress(float progress, Transaction transaction) {
|
||||
if (mAnimationContextByTaskId.size() == 0) return;
|
||||
|
||||
@@ -146,29 +132,11 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
|
||||
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);
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* 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) {}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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 {}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* 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 {}
|
||||
@@ -133,7 +133,7 @@ public class ShellTaskOrganizerTests {
|
||||
.when(mTaskOrganizerController).registerTaskOrganizer(any());
|
||||
} catch (RemoteException e) {}
|
||||
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
|
||||
mCompatUI, Optional.empty(), Optional.empty()));
|
||||
mCompatUI, Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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(), Optional.empty(), mObserver));
|
||||
mDisplayInsetsController, Optional.empty(), mObserver));
|
||||
mOrganizer.initialize(mStartingWindowController);
|
||||
doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
|
||||
doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
|
||||
|
||||
@@ -80,7 +80,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
|
||||
mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
|
||||
mMainExecutor));
|
||||
mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
|
||||
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController));
|
||||
null /* sizeCompatUI */, Optional.of(mRecentTasksController));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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);
|
||||
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
mSyncQueue, mSurfaceSession, mIconProvider, null);
|
||||
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
public class SplitTestUtils {
|
||||
|
||||
static SplitLayout createMockSplitLayout() {
|
||||
@@ -72,10 +74,12 @@ public class SplitTestUtils {
|
||||
DisplayInsetsController insetsController, SplitLayout splitLayout,
|
||||
Transitions transitions, TransactionPool transactionPool,
|
||||
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Provider<Optional<StageTaskUnfoldController>> unfoldController) {
|
||||
super(context, displayId, syncQueue, taskOrganizer, mainStage,
|
||||
sideStage, displayController, imeController, insetsController, splitLayout,
|
||||
transitions, transactionPool, logger, mainExecutor, recentTasks);
|
||||
transitions, transactionPool, logger, mainExecutor, recentTasks,
|
||||
unfoldController);
|
||||
|
||||
// Prepare root task for testing.
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
|
||||
@@ -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);
|
||||
mIconProvider, null);
|
||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mIconProvider);
|
||||
mIconProvider, null);
|
||||
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());
|
||||
mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty);
|
||||
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
|
||||
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
|
||||
.when(mTransitions).startTransition(anyInt(), any(), any());
|
||||
|
||||
@@ -34,7 +34,6 @@ 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;
|
||||
@@ -59,7 +58,6 @@ 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;
|
||||
@@ -70,6 +68,8 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* Tests for {@link StageCoordinator}
|
||||
*/
|
||||
@@ -85,6 +85,10 @@ 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;
|
||||
@@ -103,7 +107,6 @@ 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;
|
||||
@@ -116,12 +119,11 @@ 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()));
|
||||
mMainExecutor, Optional.empty(), new UnfoldControllerProvider()));
|
||||
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();
|
||||
@@ -165,6 +167,13 @@ public class StageCoordinatorTests extends ShellTestCase {
|
||||
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootTaskAppeared_initializesUnfoldControllers() {
|
||||
verify(mMainUnfoldController).init();
|
||||
verify(mSideUnfoldController).init();
|
||||
verify(mStageCoordinator).onRootTaskAppeared();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootTaskInfoChanged_updatesSplitLayout() {
|
||||
mStageCoordinator.onTaskInfoChanged(mRootTask);
|
||||
@@ -175,25 +184,26 @@ public class StageCoordinatorTests extends ShellTestCase {
|
||||
@Test
|
||||
public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
|
||||
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||
final SplitScreenListener listener = mock(SplitScreenListener.class);
|
||||
mStageCoordinator.registerSplitScreenListener(listener);
|
||||
clearInvocations(listener);
|
||||
clearInvocations(mMainUnfoldController, mSideUnfoldController);
|
||||
|
||||
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
|
||||
|
||||
verify(listener).onSplitBoundsChanged(mRootBounds, mBounds2, mBounds1);
|
||||
verify(mMainUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
|
||||
false);
|
||||
verify(mSideUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
|
||||
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
|
||||
final SplitScreenListener listener = mock(SplitScreenListener.class);
|
||||
mStageCoordinator.registerSplitScreenListener(listener);
|
||||
clearInvocations(listener);
|
||||
clearInvocations(mMainUnfoldController, mSideUnfoldController);
|
||||
|
||||
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
|
||||
|
||||
verify(listener).onSplitBoundsChanged(mRootBounds, mBounds1, mBounds2);
|
||||
verify(mMainUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT,
|
||||
false);
|
||||
verify(mSideUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -304,4 +314,20 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ 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;
|
||||
|
||||
@@ -71,6 +72,8 @@ 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();
|
||||
@@ -89,7 +92,8 @@ public final class StageTaskListenerTests extends ShellTestCase {
|
||||
mCallbacks,
|
||||
mSyncQueue,
|
||||
mSurfaceSession,
|
||||
mIconProvider);
|
||||
mIconProvider,
|
||||
mStageTaskUnfoldController);
|
||||
mRootTask = new TestRunningTaskInfoBuilder().build();
|
||||
mRootTask.parentTaskId = INVALID_TASK_ID;
|
||||
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
|
||||
@@ -126,6 +130,30 @@ 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();
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user