diff options
Diffstat (limited to 'libs')
37 files changed, 533 insertions, 204 deletions
diff --git a/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto index 6e0110193a05..c82a70c9a44e 100644 --- a/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto +++ b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto @@ -37,6 +37,9 @@ message WmShellTransitionTraceProto { required fixed64 magic_number = 1; repeated Transition transitions = 2; repeated HandlerMapping handlerMappings = 3; + /* offset between real-time clock and elapsed time clock in nanoseconds. + Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */ + optional fixed64 real_to_elapsed_time_offset_nanos = 4; } message Transition { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index 19eff0e43169..1793a3d0feb4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -255,7 +255,7 @@ class ActivityEmbeddingAnimationSpec { private boolean shouldShowBackdrop(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change) { final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE, - mTransitionAnimation); + mTransitionAnimation, false); return a != null && a.getShowBackdrop(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index e8014af463bd..adc0c9c4322a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -479,7 +479,7 @@ public class BubbleExpandedView extends LinearLayout { void applyThemeAttrs() { final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ android.R.attr.dialogCornerRadius, - android.R.attr.colorBackgroundFloating}); + com.android.internal.R.attr.materialColorSurfaceBright}); boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( mContext.getResources()); mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index e7dede757578..2832c553c20c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -412,7 +412,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Releases and re-inflates {@link DividerView} on the root surface. */ public void update(SurfaceControl.Transaction t) { - if (!mInitialized) return; + if (!mInitialized) { + init(); + return; + } mSplitWindowManager.release(t); mImePositionProcessor.reset(); mSplitWindowManager.init(this, mInsetsState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 838e37a905db..2bbd870f024d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -47,6 +47,8 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import dagger.Lazy; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; @@ -55,8 +57,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import dagger.Lazy; - /** * Controller to show/update compat UI components on Tasks based on whether the foreground * activities are in compatibility mode. @@ -284,13 +284,18 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellTaskOrganizer.TaskListener taskListener) { CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId); if (layout != null) { - // UI already exists, update the UI layout. - if (!layout.updateCompatInfo(taskInfo, taskListener, - showOnDisplay(layout.getDisplayId()))) { - // The layout is no longer eligible to be shown, remove from active layouts. + if (layout.needsToBeRecreated(taskInfo, taskListener)) { mActiveCompatLayouts.remove(taskInfo.taskId); + layout.release(); + } else { + // UI already exists, update the UI layout. + if (!layout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(layout.getDisplayId()))) { + // The layout is no longer eligible to be shown, remove from active layouts. + mActiveCompatLayouts.remove(taskInfo.taskId); + } + return; } - return; } // Create a new UI layout. @@ -433,13 +438,18 @@ public class CompatUIController implements OnDisplaysChangedListener, private void createOrUpdateReachabilityEduLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { if (mActiveReachabilityEduLayout != null) { - // UI already exists, update the UI layout. - if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener, - showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) { - // The layout is no longer eligible to be shown, remove from active layouts. + if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) { + mActiveReachabilityEduLayout.release(); mActiveReachabilityEduLayout = null; + } else { + // UI already exists, update the UI layout. + if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) { + // The layout is no longer eligible to be shown, remove from active layouts. + mActiveReachabilityEduLayout = null; + } + return; } - return; } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index 659229228a57..d4778fa7a58a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -22,7 +22,6 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED; import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.app.TaskInfo.CameraCompatControlState; @@ -53,9 +52,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked; - @NonNull - private TaskInfo mTaskInfo; - // Remember the last reported states in case visibility changes due to keyguard or IME updates. @VisibleForTesting boolean mHasSizeCompat; @@ -77,7 +73,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration, Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) { super(context, taskInfo, syncQueue, taskListener, displayLayout); - mTaskInfo = taskInfo; mCallback = callback; mHasSizeCompat = taskInfo.topActivityInSizeCompat; mCameraCompatControlState = taskInfo.cameraCompatControlState; @@ -129,7 +124,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { - mTaskInfo = taskInfo; final boolean prevHasSizeCompat = mHasSizeCompat; final int prevCameraCompatControlState = mCameraCompatControlState; mHasSizeCompat = taskInfo.topActivityInSizeCompat; @@ -149,7 +143,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { /** Called when the restart button is clicked. */ void onRestartButtonClicked() { - mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener())); + mOnRestartButtonClicked.accept(Pair.create(getLastTaskInfo(), getTaskListener())); } /** Called when the camera treatment button is clicked. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java index 9c4e79cd631b..180498c50c78 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -26,6 +26,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; @@ -65,6 +66,9 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana private DisplayLayout mDisplayLayout; private final Rect mStableBounds; + @NonNull + private TaskInfo mTaskInfo; + /** * Utility class for adding and releasing a View hierarchy for this {@link * WindowlessWindowManager} to {@code mLeash}. @@ -83,6 +87,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) { super(taskInfo.configuration, null /* rootSurface */, null /* hostInputToken */); + mTaskInfo = taskInfo; mContext = context; mSyncQueue = syncQueue; mTaskConfig = taskInfo.configuration; @@ -95,6 +100,17 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana } /** + * @return {@code true} if the instance of the specific {@link CompatUIWindowManagerAbstract} + * for the current task id needs to be recreated loading the related resources. This happens + * if the user switches between Light/Dark mode, if the device is docked/undocked or if the + * user switches between multi-window mode to fullscreen where the + * {@link ShellTaskOrganizer.TaskListener} implementation is different. + */ + boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { + return hasUiModeChanged(mTaskInfo, taskInfo) || hasTaskListenerChanged(taskListener); + } + + /** * Returns the z-order of this window which will be passed to the {@link SurfaceControl} once * {@link #attachToParentSurface} is called. * @@ -195,6 +211,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana @VisibleForTesting(visibility = PROTECTED) public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { + mTaskInfo = taskInfo; final Configuration prevTaskConfig = mTaskConfig; final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; mTaskConfig = taskInfo.configuration; @@ -315,6 +332,11 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana updateSurfacePosition(); } + @Nullable + protected TaskInfo getLastTaskInfo() { + return mTaskInfo; + } + /** * Called following a change in the task bounds, display layout stable bounds, or the layout * direction. @@ -402,4 +424,12 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana protected final String getTag() { return getClass().getSimpleName(); } + + protected boolean hasTaskListenerChanged(ShellTaskOrganizer.TaskListener newTaskListener) { + return !mTaskListener.equals(newTaskListener); + } + + protected static boolean hasUiModeChanged(TaskInfo currentTaskInfo, TaskInfo newTaskInfo) { + return currentTaskInfo.configuration.uiMode != newTaskInfo.configuration.uiMode; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java index 959c50d5c640..9a67258ded2e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java @@ -19,7 +19,6 @@ package com.android.wm.shell.compatui; import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; @@ -69,9 +68,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { @VisibleForTesting LetterboxEduDialogLayout mLayout; - @NonNull - private TaskInfo mTaskInfo; - /** * The vertical margin between the dialog container and the task stable bounds (excluding * insets). @@ -99,7 +95,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { DialogAnimationController<LetterboxEduDialogLayout> animationController, DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration) { super(context, taskInfo, syncQueue, taskListener, displayLayout); - mTaskInfo = taskInfo; mTransitions = transitions; mOnDismissCallback = onDismissCallback; mAnimationController = animationController; @@ -197,7 +192,7 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { mLayout.setDismissOnClickListener(null); mAnimationController.startExitAnimation(mLayout, () -> { release(); - mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener())); + mOnDismissCallback.accept(Pair.create(getLastTaskInfo(), getTaskListener())); }); } @@ -210,7 +205,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { - mTaskInfo = taskInfo; mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation; return super.updateCompatInfo(taskInfo, taskListener, canShow); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java index a18ab9154e01..95bb1fe1c986 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java @@ -20,7 +20,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; @@ -52,9 +51,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { private final ShellExecutor mMainExecutor; - @NonNull - private TaskInfo mTaskInfo; - private boolean mIsActivityLetterboxed; private int mLetterboxVerticalPosition; @@ -86,7 +82,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor) { super(context, taskInfo, syncQueue, taskListener, displayLayout); - mTaskInfo = taskInfo; mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition; mLetterboxHorizontalPosition = taskInfo.topActivityLetterboxHorizontalPosition; @@ -136,7 +131,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { - mTaskInfo = taskInfo; final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed; final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition; final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition; @@ -222,14 +216,14 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { if (mLayout == null) { return; } - + final TaskInfo lastTaskInfo = getLastTaskInfo(); final boolean eligibleForDisplayHorizontalEducation = mForceUpdate - || !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(mTaskInfo) + || !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo) || (mHasUserDoubleTapped && (mLetterboxHorizontalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxHorizontalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); final boolean eligibleForDisplayVerticalEducation = mForceUpdate - || !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(mTaskInfo) + || !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo) || (mHasUserDoubleTapped && (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); @@ -241,7 +235,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { mLayout.handleVisibility(eligibleForDisplayHorizontalEducation, eligibleForDisplayVerticalEducation, mLetterboxVerticalPosition, mLetterboxHorizontalPosition, availableWidth, - availableHeight, mCompatUIConfiguration, mTaskInfo); + availableHeight, mCompatUIConfiguration, lastTaskInfo); if (!mHasLetterboxSizeChanged) { updateHideTime(); mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java index 51e5141a28af..a770da28fbd1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java @@ -19,7 +19,6 @@ package com.android.wm.shell.compatui; import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; @@ -67,9 +66,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { */ private final int mDialogVerticalMargin; - @NonNull - private TaskInfo mTaskInfo; - @Nullable @VisibleForTesting RestartDialogLayout mLayout; @@ -95,7 +91,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { DialogAnimationController<RestartDialogLayout> animationController, CompatUIConfiguration compatUIConfiguration) { super(context, taskInfo, syncQueue, taskListener, displayLayout); - mTaskInfo = taskInfo; mTransitions = transitions; mOnDismissCallback = onDismissCallback; mOnRestartCallback = onRestartCallback; @@ -125,7 +120,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { protected boolean eligibleToShowLayout() { // We don't show this dialog if the user has explicitly selected so clicking on a checkbox. return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null - || mCompatUIConfiguration.shouldShowRestartDialogAgain(mTaskInfo)); + || mCompatUIConfiguration.shouldShowRestartDialogAgain(getLastTaskInfo())); } @Override @@ -143,18 +138,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { mRequestRestartDialog = enabled; } - @Override - public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, - boolean canShow) { - mTaskInfo = taskInfo; - return super.updateCompatInfo(taskInfo, taskListener, canShow); - } - - boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { - return taskInfo.configuration.uiMode != mTaskInfo.configuration.uiMode - || !getTaskListener().equals(taskListener); - } - private void updateDialogMargins() { if (mLayout == null) { return; @@ -191,6 +174,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { // Dialog has already been released. return; } + final TaskInfo lastTaskInfo = getLastTaskInfo(); mLayout.setDismissOnClickListener(this::onDismiss); mLayout.setRestartOnClickListener(dontShowAgain -> { if (mLayout != null) { @@ -200,9 +184,9 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { }); } if (dontShowAgain) { - mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo); + mCompatUIConfiguration.setDontShowRestartDialogAgain(lastTaskInfo); } - mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener())); + mOnRestartCallback.accept(Pair.create(lastTaskInfo, getTaskListener())); }); // Focus on the dialog title for accessibility. mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); @@ -216,7 +200,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { mLayout.setDismissOnClickListener(null); mAnimationController.startExitAnimation(mLayout, () -> { release(); - mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener())); + mOnDismissCallback.accept(Pair.create(getLastTaskInfo(), getTaskListener())); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 00cc57f0b99c..3ab175d3b68a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -23,6 +23,8 @@ import android.util.SparseArray import androidx.core.util.forEach import androidx.core.util.keyIterator import androidx.core.util.valueIterator +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.util.KtProtoLog import java.util.concurrent.Executor import java.util.function.Consumer @@ -140,6 +142,12 @@ class DesktopModeTaskRepository { val added = displayData.getOrCreate(displayId).activeTasks.add(taskId) if (added) { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTaskRepo: add active task=%d displayId=%d", + taskId, + displayId + ) activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } } return added @@ -158,6 +166,9 @@ class DesktopModeTaskRepository { result = true } } + if (result) { + KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove active task=%d", taskId) + } return result } @@ -221,6 +232,17 @@ class DesktopModeTaskRepository { displayData[displayId]?.visibleTasks?.remove(taskId) } val newCount = getVisibleTaskCount(displayId) + + if (prevCount != newCount) { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTaskRepo: update task visibility taskId=%d visible=%b displayId=%d", + taskId, + visible, + displayId + ) + } + // Check if count changed and if there was no tasks or this is the first task if (prevCount != newCount && (prevCount == 0 || newCount == 0)) { notifyVisibleTaskListeners(displayId, newCount > 0) @@ -244,6 +266,11 @@ class DesktopModeTaskRepository { * Add (or move if it already exists) the task to the top of the ordered list. */ fun addOrMoveFreeformTaskToTop(taskId: Int) { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTaskRepo: add or move task to top taskId=%d", + taskId + ) if (freeformTasksInZOrder.contains(taskId)) { freeformTasksInZOrder.remove(taskId) } @@ -254,6 +281,11 @@ class DesktopModeTaskRepository { * Remove the task from the ordered list. */ fun removeFreeformTask(taskId: Int) { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTaskRepo: remove freeform task from ordered list taskId=%d", + taskId + ) freeformTasksInZOrder.remove(taskId) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index b3109388da2c..91bb155d9d01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -39,7 +39,6 @@ import android.window.TransitionRequestInfo import android.window.WindowContainerToken import android.window.WindowContainerTransaction import androidx.annotation.BinderThread -import com.android.internal.protolog.common.ProtoLog import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController @@ -56,6 +55,7 @@ import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.util.KtProtoLog import java.util.concurrent.Executor import java.util.function.Consumer @@ -91,7 +91,7 @@ class DesktopTasksController( } private fun onInit() { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") + KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") shellController.addExternalInterface( ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE, { createExternalInterface() }, @@ -102,7 +102,7 @@ class DesktopTasksController( /** Show all tasks, that are part of the desktop, on top of launcher */ fun showDesktopApps(displayId: Int) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") val wct = WindowContainerTransaction() // TODO(b/278084491): pass in display id bringDesktopAppsToFront(displayId, wct) @@ -130,8 +130,11 @@ class DesktopTasksController( /** Move a task to desktop */ fun moveToDesktop(task: RunningTaskInfo) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDesktop: %d", task.taskId) - + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToDesktop taskId=%d", + task.taskId + ) val wct = WindowContainerTransaction() // Bring other apps to front first bringDesktopAppsToFront(task.displayId, wct) @@ -147,10 +150,12 @@ class DesktopTasksController( * Moves a single task to freeform and sets the taskBounds to the passed in bounds, * startBounds */ - fun moveToFreeform( - taskInfo: RunningTaskInfo, - startBounds: Rect - ) { + fun moveToFreeform(taskInfo: RunningTaskInfo, startBounds: Rect) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToFreeform with bounds taskId=%d", + taskInfo.taskId + ) val wct = WindowContainerTransaction() moveHomeTaskToFront(wct) addMoveToDesktopChanges(wct, taskInfo.getToken()) @@ -165,10 +170,12 @@ class DesktopTasksController( } /** Brings apps to front and sets freeform task bounds */ - private fun moveToDesktopWithAnimation( - taskInfo: RunningTaskInfo, - freeformBounds: Rect - ) { + private fun moveToDesktopWithAnimation(taskInfo: RunningTaskInfo, freeformBounds: Rect) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToDesktop with animation taskId=%d", + taskInfo.taskId + ) val wct = WindowContainerTransaction() bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo.getToken()) @@ -190,7 +197,11 @@ class DesktopTasksController( /** Move a task to fullscreen */ fun moveToFullscreen(task: RunningTaskInfo) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId) + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToFullscreen taskId=%d", + task.taskId + ) val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) @@ -206,6 +217,11 @@ class DesktopTasksController( * status bar area */ fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: cancelMoveToFreeform taskId=%d", + task.taskId + ) val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -218,6 +234,11 @@ class DesktopTasksController( } private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveToFullscreen with animation taskId=%d", + task.taskId + ) val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) @@ -230,8 +251,14 @@ class DesktopTasksController( } } - /** Move a task to the front **/ + /** Move a task to the front */ fun moveTaskToFront(taskInfo: RunningTaskInfo) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: moveTaskToFront taskId=%d", + taskInfo.taskId + ) + val wct = WindowContainerTransaction() wct.reorder(taskInfo.token, true) if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -255,10 +282,10 @@ class DesktopTasksController( fun moveToNextDisplay(taskId: Int) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) if (task == null) { - ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId) + KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId) return } - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d taskDisplayId=%d", + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d taskDisplayId=%d", taskId, task.displayId) val displayIds = rootTaskDisplayAreaOrganizer.displayIds.sorted() @@ -269,7 +296,7 @@ class DesktopTasksController( newDisplayId = displayIds.firstOrNull { displayId -> displayId < task.displayId } } if (newDisplayId == null) { - ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found") + KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found") return } moveToDisplay(task, newDisplayId) @@ -281,17 +308,17 @@ class DesktopTasksController( * No-op if task is already on that display per [RunningTaskInfo.displayId]. */ private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDisplay: taskId=%d displayId=%d", + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDisplay: taskId=%d displayId=%d", task.taskId, displayId) if (task.displayId == displayId) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display") + KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display") return } val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId) if (displayAreaInfo == null) { - ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found") + KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found") return } @@ -316,7 +343,7 @@ class DesktopTasksController( } private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront") val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId) // First move home to front and then other tasks on top of it @@ -397,9 +424,9 @@ class DesktopTasksController( if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { - ProtoLog.d( + KtProtoLog.d( WM_SHELL_DESKTOP_MODE, - "DesktopTasksController#handleRequest: switch fullscreen task to freeform," + + "DesktopTasksController: switch fullscreen task to freeform on transition" + " taskId=%d", task.taskId ) @@ -414,9 +441,9 @@ class DesktopTasksController( // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { - ProtoLog.d( + KtProtoLog.d( WM_SHELL_DESKTOP_MODE, - "DesktopTasksController#handleRequest: switch freeform task to fullscreen," + + "DesktopTasksController: switch freeform task to fullscreen oon transition" + " taskId=%d", task.taskId ) @@ -627,8 +654,6 @@ class DesktopTasksController( } } - - /** The interface for calls from outside the host process. */ @BinderThread private class IDesktopModeImpl(private var controller: DesktopTasksController?) : diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS index ccbb9cf298a2..a3803ed82844 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS @@ -1,3 +1,4 @@ # WM shell sub-module freeform owners atsjenk@google.com +jorgegil@google.com madym@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 24d0b996a3cb..f51eb5299dd9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -180,6 +180,35 @@ public class PipBoundsAlgorithm { return null; } + + /** + * Returns the source hint rect if it is valid (if provided and is contained by the current + * task bounds, while not smaller than the destination bounds). + */ + @Nullable + public static Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds, + Rect destinationBounds) { + Rect sourceRectHint = getValidSourceHintRect(params, sourceBounds); + if (!isSourceRectHintValidForEnterPip(sourceRectHint, destinationBounds)) { + sourceRectHint = null; + } + return sourceRectHint; + } + + /** + * This is a situation in which the source rect hint on at least one axis is smaller + * than the destination bounds, which represents a problem because we would have to scale + * up that axis to fit the bounds. So instead, just fallback to the non-source hint + * animation in this case. + * + * @return {@code false} if the given source is too small to use for the entering animation. + */ + static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint, Rect destinationBounds) { + return sourceRectHint != null + && sourceRectHint.width() > destinationBounds.width() + && sourceRectHint.height() > destinationBounds.height(); + } + public float getDefaultAspectRatio() { return mDefaultAspectRatio; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 6cedcf534f3b..363d6759f8d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1657,8 +1657,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, "%s: Abort animation, invalid leash", TAG); return null; } - if (isInPipDirection(direction) - && !isSourceRectHintValidForEnterPip(sourceHintRect, destinationBounds)) { + if (isInPipDirection(direction) && !PipBoundsAlgorithm + .isSourceRectHintValidForEnterPip(sourceHintRect, destinationBounds)) { // The given source rect hint is too small for enter PiP animation, reset it to null. sourceHintRect = null; } @@ -1757,20 +1757,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * This is a situation in which the source rect hint on at least one axis is smaller - * than the destination bounds, which represents a problem because we would have to scale - * up that axis to fit the bounds. So instead, just fallback to the non-source hint - * animation in this case. - * - * @return {@code false} if the given source is too small to use for the entering animation. - */ - private boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint, Rect destinationBounds) { - return sourceRectHint != null - && sourceRectHint.width() > destinationBounds.width() - && sourceRectHint.height() > destinationBounds.height(); - } - - /** * Sync with {@link SplitScreenController} on destination bounds if PiP is going to * split screen. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 98db707d1105..046d6fce443b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -470,6 +470,7 @@ public class PipTransition extends PipTransitionController { @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TaskInfo taskInfo, @Nullable TransitionInfo.Change pipTaskChange) { TransitionInfo.Change pipChange = pipTaskChange; + SurfaceControl activitySc = null; if (mCurrentPipTaskToken == null) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: There is no existing PiP Task for TRANSIT_EXIT_PIP", TAG); @@ -482,6 +483,7 @@ public class PipTransition extends PipTransitionController { if (mCurrentPipTaskToken.equals(change.getLastParent())) { // Find the activity that is exiting PiP. pipChange = change; + activitySc = change.getLeash(); break; } } @@ -498,17 +500,36 @@ public class PipTransition extends PipTransitionController { // case it may not be in the screen coordinate. // Reparent the pip leash to the root with max layer so that we can animate it outside of // parent crop, and make sure it is not covered by other windows. - final SurfaceControl pipLeash = pipChange.getLeash(); - final int rootIdx = TransitionUtil.rootIndexFor(pipChange, info); - startTransaction.reparent(pipLeash, info.getRoot(rootIdx).getLeash()); + final TransitionInfo.Root root = TransitionUtil.getRootFor(pipChange, info); + final SurfaceControl pipLeash; + if (activitySc != null) { + // Use a local leash to animate activity in case the activity has letterbox which may + // be broken by PiP animation, e.g. always end at 0,0 in parent and unable to include + // letterbox area in crop bounds. + final SurfaceControl activitySurface = pipChange.getLeash(); + pipLeash = new SurfaceControl.Builder() + .setName(activitySc + "_pip-leash") + .setContainerLayer() + .setHidden(false) + .setParent(root.getLeash()) + .build(); + startTransaction.reparent(activitySurface, pipLeash); + // Put the activity at local position with offset in case it is letterboxed. + final Point activityOffset = pipChange.getEndRelOffset(); + startTransaction.setPosition(activitySc, activityOffset.x, activityOffset.y); + } else { + pipLeash = pipChange.getLeash(); + startTransaction.reparent(pipLeash, root.getLeash()); + } startTransaction.setLayer(pipLeash, Integer.MAX_VALUE); // Note: because of this, the bounds to animate should be translated to the root coordinate. - final Point offset = info.getRoot(rootIdx).getOffset(); + final Point offset = root.getOffset(); final Rect currentBounds = mPipBoundsState.getBounds(); currentBounds.offset(-offset.x, -offset.y); startTransaction.setPosition(pipLeash, currentBounds.left, currentBounds.top); final WindowContainerToken pipTaskToken = pipChange.getContainer(); + final boolean useLocalLeash = activitySc != null; final boolean toFullscreen = pipChange.getEndAbsBounds().equals( mPipBoundsState.getDisplayBounds()); mFinishCallback = (wct, wctCB) -> { @@ -518,6 +539,14 @@ public class PipTransition extends PipTransitionController { wct.setBounds(pipTaskToken, null); mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP); } + if (useLocalLeash) { + if (mPipAnimationController.isAnimating()) { + mPipAnimationController.getCurrentAnimator().end(); + } + // Make sure the animator don't use the released leash, e.g. mergeAnimation. + mPipAnimationController.resetAnimatorState(); + finishTransaction.remove(pipLeash); + } finishCallback.onTransitionFinished(wct, wctCB); }; mFinishTransaction = finishTransaction; @@ -545,7 +574,8 @@ public class PipTransition extends PipTransitionController { // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); destinationBounds.offset(-offset.x, -offset.y); - startTransaction.setWindowCrop(pipLeash, destinationBounds); + startTransaction.setWindowCrop(pipLeash, destinationBounds.width(), + destinationBounds.height()); mSurfaceTransactionHelper.scale(startTransaction, pipLeash, destinationBounds, currentBounds); startTransaction.apply(); @@ -764,7 +794,7 @@ public class PipTransition extends PipTransitionController { final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds(); int rotationDelta = deltaRotation(startRotation, endRotation); Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( - taskInfo.pictureInPictureParams, currentBounds); + taskInfo.pictureInPictureParams, currentBounds, destinationBounds); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { // Need to get the bounds of new rotation in old rotation for fixed rotation, computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index af52350f5b48..c654c1b0d431 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -337,6 +337,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return isTaskInSplitScreen(taskId) && isSplitScreenVisible(); } + /** Check whether the task is the single-top root or the root of one of the stages. */ + public boolean isTaskRootOrStageRoot(int taskId) { + return mStageCoordinator.isRootOrStageRoot(taskId); + } + public @SplitPosition int getSplitPosition(int taskId) { return mStageCoordinator.getSplitPosition(taskId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 5c2f1438c08e..749549d1ca55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -377,6 +377,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return STAGE_TYPE_UNDEFINED; } + boolean isRootOrStageRoot(int taskId) { + if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskId) { + return true; + } + return mMainStage.isRootTaskId(taskId) || mSideStage.isRootTaskId(taskId); + } + boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { prepareEnterSplitScreen(wct, task, stagePosition); @@ -1538,7 +1545,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void finishEnterSplitScreen(SurfaceControl.Transaction t) { - mSplitLayout.init(); + mSplitLayout.update(t); setDividerVisibility(true, t); // Ensure divider surface are re-parented back into the hierarchy at the end of the // transition. See Transition#buildFinishTransaction for more detail. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 18b09b090794..e2e9270cd6cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -286,6 +286,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } + boolean isRootTaskId(int taskId) { + return mRootTaskInfo != null && mRootTaskInfo.taskId == taskId; + } + void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY, boolean immediately) { if (mSplitDecorManager != null && mRootTaskInfo != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 94190c74f3e9..d27933e2f800 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -453,7 +453,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { return; } - finishTransaction.reparent(mTaskLeash, null).apply(); + finishTransaction.reparent(mTaskLeash, null); if (mListener != null) { final int taskId = mTaskInfo.taskId; @@ -490,13 +490,11 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { if (mSurfaceCreated) { // Surface is ready, so just reparent the task to this surface control startTransaction.reparent(mTaskLeash, mSurfaceControl) - .show(mTaskLeash) - .apply(); + .show(mTaskLeash); // Also reparent on finishTransaction since the finishTransaction will reparent back // to its "original" parent by default. finishTransaction.reparent(mTaskLeash, mSurfaceControl) - .setPosition(mTaskLeash, 0, 0) - .apply(); + .setPosition(mTaskLeash, 0, 0); mTaskViewTransitions.updateBoundsState(this, mTaskViewBase.getCurrentBoundsOnScreen()); mTaskViewTransitions.updateVisibilityState(this, true /* visible */); wct.setBounds(mTaskToken, mTaskViewBase.getCurrentBoundsOnScreen()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 689f9e1a3eda..fe2faaf79a1a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -363,7 +363,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { continue; } startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl()); - finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl()); + finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl()) + .setPosition(chg.getLeash(), 0, 0); changesHandled++; } } @@ -377,7 +378,6 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } // No animation, just show it immediately. startTransaction.apply(); - finishTransaction.apply(); finishCallback.onTransitionFinished(wct, null /* wctCB */); startNextTransition(); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 1ee52ae00645..21dca95d056a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -24,6 +24,7 @@ import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; @@ -36,12 +37,8 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_RELAUNCH; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL; @@ -92,7 +89,6 @@ import android.view.Choreographer; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; -import android.view.WindowManager.TransitionType; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; @@ -329,6 +325,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @ColorInt int backgroundColorForTransition = 0; final int wallpaperTransit = getWallpaperTransitType(info); boolean isDisplayRotationAnimationStarted = false; + final boolean isDreamTransition = isDreamTransition(info); + for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.hasAllFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY @@ -343,9 +341,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { continue; } final boolean isTask = change.getTaskInfo() != null; + final int mode = change.getMode(); boolean isSeamlessDisplayChange = false; - if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) { + if (mode == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) { if (info.getType() == TRANSIT_CHANGE) { final int anim = getRotationAnimationHint(change, info, mDisplayController); isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS; @@ -361,7 +360,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - if (change.getMode() == TRANSIT_CHANGE) { + if (mode == TRANSIT_CHANGE) { // If task is child task, only set position in parent and update crop when needed. if (isTask && change.getParent() != null && info.getChange(change.getParent()).getTaskInfo() != null) { @@ -410,8 +409,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Hide the invisible surface directly without animating it if there is a display // rotation animation playing. - if (isDisplayRotationAnimationStarted && TransitionUtil.isClosingType( - change.getMode())) { + if (isDisplayRotationAnimationStarted && TransitionUtil.isClosingType(mode)) { startTransaction.hide(change.getLeash()); continue; } @@ -424,16 +422,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; - Animation a = loadAnimation(info, change, wallpaperTransit); + Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition); if (a != null) { if (isTask) { - final @TransitionType int type = info.getType(); - final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN - || type == TRANSIT_CLOSE - || type == TRANSIT_TO_FRONT - || type == TRANSIT_TO_BACK; final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0; - if (isOpenOrCloseTransition && !isTranslucent + if (!isTranslucent && TransitionUtil.isOpenOrCloseMode(mode) + && TransitionUtil.isOpenOrCloseMode(info.getType()) && wallpaperTransit == WALLPAPER_TRANSITION_NONE) { // Use the overview background as the background for the animation final Context uiContext = ActivityThread.currentActivityThread() @@ -458,7 +452,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { backgroundColorForTransition); if (!isTask && a.hasExtension()) { - if (!TransitionUtil.isOpeningType(change.getMode())) { + if (!TransitionUtil.isOpeningType(mode)) { // Can screenshot now (before startTransaction is applied) edgeExtendWindow(change, a, startTransaction, finishTransaction); } else { @@ -469,7 +463,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - final Rect clipRect = TransitionUtil.isClosingType(change.getMode()) + final Rect clipRect = TransitionUtil.isClosingType(mode) ? new Rect(mRotator.getEndBoundsInStartRotation(change)) : new Rect(change.getEndAbsBounds()); clipRect.offsetTo(0, 0); @@ -519,6 +513,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return true; } + private static boolean isDreamTransition(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() != null + && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM) { + return true; + } + } + + return false; + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @@ -572,7 +578,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @Nullable private Animation loadAnimation(@NonNull TransitionInfo info, - @NonNull TransitionInfo.Change change, int wallpaperTransit) { + @NonNull TransitionInfo.Change change, int wallpaperTransit, + boolean isDreamTransition) { Animation a; final int type = info.getType(); @@ -630,7 +637,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // If there's a scene-transition, then jump-cut. return null; } else { - a = loadAttributeAnimation(info, change, wallpaperTransit, mTransitionAnimation); + a = loadAttributeAnimation( + info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition); } if (a != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java index 0cede902f034..e27e4f990407 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java @@ -28,7 +28,6 @@ import android.os.Trace; import android.util.Log; import com.android.internal.util.TraceBuffer; -import com.android.wm.shell.nano.HandlerMapping; import com.android.wm.shell.sysui.ShellCommandHandler; import com.google.protobuf.nano.MessageNano; @@ -41,6 +40,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Queue; +import java.util.concurrent.TimeUnit; /** * Helper class to collect and dump transition traces. @@ -241,6 +241,10 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { new com.android.wm.shell.nano.WmShellTransitionTraceProto(); proto.magicNumber = MAGIC_NUMBER_VALUE; writeHandlerMappingToProto(proto); + long timeOffsetNs = + TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()) + - SystemClock.elapsedRealtimeNanos(); + proto.realToElapsedTimeOffsetNanos = timeOffsetNs; int pid = android.os.Process.myPid(); LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() + " from process " + pid); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index 0f4645c0fdab..19d8384ace41 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -18,7 +18,6 @@ package com.android.wm.shell.transition; import static android.app.ActivityOptions.ANIM_FROM_STYLE; import static android.app.ActivityOptions.ANIM_NONE; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -63,7 +62,7 @@ public class TransitionAnimationHelper { @Nullable public static Animation loadAttributeAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, int wallpaperTransit, - @NonNull TransitionAnimation transitionAnimation) { + @NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) { final int type = info.getType(); final int changeMode = change.getMode(); final int changeFlags = change.getFlags(); @@ -71,11 +70,9 @@ public class TransitionAnimationHelper { final boolean isTask = change.getTaskInfo() != null; final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); final int overrideType = options != null ? options.getType() : ANIM_NONE; - final boolean isDream = - isTask && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM; int animAttr = 0; boolean translucent = false; - if (isDream) { + if (isDreamTransition) { if (type == TRANSIT_OPEN) { animAttr = enter ? R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index fda943d7dc02..ab27c55f20dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -501,7 +501,9 @@ public class Transitions implements RemoteCallable<Transitions>, */ private static void setupAnimHierarchy(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { - boolean isOpening = isOpeningType(info.getType()); + final int type = info.getType(); + final boolean isOpening = isOpeningType(type); + final boolean isClosing = isClosingType(type); for (int i = 0; i < info.getRootCount(); ++i) { t.show(info.getRoot(i).getLeash()); } @@ -554,7 +556,13 @@ public class Transitions implements RemoteCallable<Transitions>, layer = zSplitLine + numChanges - i; } } else { // CHANGE or other - layer = zSplitLine + numChanges - i; + if (isClosing) { + // Put below CLOSE mode. + layer = zSplitLine - i; + } else { + // Put above CLOSE mode. + layer = zSplitLine + numChanges - i; + } } t.setLayer(leash, layer); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt new file mode 100644 index 000000000000..9b48a542720c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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.util + +import android.util.Log +import com.android.internal.protolog.common.IProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogImpl + +/** + * Log messages using an API similar to [com.android.internal.protolog.common.ProtoLog]. Useful for + * logging from Kotlin classes as ProtoLog does not have support for Kotlin. + * + * All messages are logged to logcat if logging is enabled for that [IProtoLogGroup]. + */ +// TODO(b/168581922): remove once ProtoLog adds support for Kotlin +class KtProtoLog { + companion object { + /** @see [com.android.internal.protolog.common.ProtoLog.d] */ + fun d(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.d(group.tag, String.format(messageString, *args)) + } + } + + /** @see [com.android.internal.protolog.common.ProtoLog.v] */ + fun v(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.v(group.tag, String.format(messageString, *args)) + } + } + + /** @see [com.android.internal.protolog.common.ProtoLog.i] */ + fun i(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.i(group.tag, String.format(messageString, *args)) + } + } + + /** @see [com.android.internal.protolog.common.ProtoLog.w] */ + fun w(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.w(group.tag, String.format(messageString, *args)) + } + } + + /** @see [com.android.internal.protolog.common.ProtoLog.e] */ + fun e(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.e(group.tag, String.format(messageString, *args)) + } + } + + /** @see [com.android.internal.protolog.common.ProtoLog.wtf] */ + fun wtf(group: IProtoLogGroup, messageString: String, vararg args: Any) { + if (ShellProtoLogImpl.isEnabled(group)) { + Log.wtf(group.tag, String.format(messageString, *args)) + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java index 7d05c0e62e13..402b0ce7c87c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java @@ -68,6 +68,12 @@ public class TransitionUtil { return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK; } + /** Returns {@code true} if the transition is opening or closing mode. */ + public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) { + return mode == TRANSIT_OPEN || mode == TRANSIT_CLOSE + || mode == TRANSIT_TO_FRONT || mode == TRANSIT_TO_BACK; + } + /** Returns {@code true} if the transition has a display change. */ public static boolean hasDisplayChange(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -311,7 +317,7 @@ public class TransitionUtil { private static RemoteAnimationTarget getDividerTarget(TransitionInfo.Change change, SurfaceControl leash) { - return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, + return new RemoteAnimationTarget(-1 /* taskId */, newModeToLegacyMode(change.getMode()), leash, false /* isTranslucent */, null /* clipRect */, null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, new android.graphics.Point(0, 0) /* position */, change.getStartAbsBounds(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 2bb3cceb257f..39fb7936747e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; -import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.util.SparseArray; @@ -186,8 +185,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback( - windowDecoration, taskInfo); + final DragPositioningCallback dragPositioningCallback = + new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController, + null /* disallowedAreaForEndBounds */); final CaptionTouchEventListener touchEventListener = new CaptionTouchEventListener(taskInfo, dragPositioningCallback); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); @@ -198,17 +198,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { setupCaptionColor(taskInfo, windowDecoration); } - private FluidResizeTaskPositioner createDragPositioningCallback( - CaptionWindowDecoration windowDecoration, RunningTaskInfo taskInfo) { - final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width(); - final int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId) - .stableInsets().top; - final Rect disallowedAreaForEndBounds = new Rect(0, 0, screenWidth, - statusBarHeight); - return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, - mDisplayController, disallowedAreaForEndBounds); - } - private class CaptionTouchEventListener implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 0b821844e46c..54babce13205 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -760,6 +760,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; + if (mSplitScreenController.isPresent() + && mSplitScreenController.get().isTaskRootOrStageRoot(taskInfo.taskId)) { + return false; + } return DesktopModeStatus.isProto2Enabled() && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && mDisplayController.getDisplayContext(taskInfo.displayId) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java index 9bcb77f03abd..9f79d212a7b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java @@ -21,6 +21,8 @@ import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; +import androidx.annotation.Nullable; + import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -42,24 +44,24 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { private final Rect mRepositionTaskBounds = new Rect(); // If a task move (not resize) finishes in this region, the positioner will not attempt to // finalize the bounds there using WCT#setBounds - private final Rect mDisallowedAreaForEndBounds = new Rect(); + private final Rect mDisallowedAreaForEndBounds; private boolean mHasDragResized; private int mCtrlType; FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, - DisplayController displayController, Rect disallowedAreaForEndBounds) { + DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds) { this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds, dragStartListener -> {}, SurfaceControl.Transaction::new); } FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, - DisplayController displayController, Rect disallowedAreaForEndBounds, + DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds, DragPositioningCallbackUtility.DragStartListener dragStartListener, Supplier<SurfaceControl.Transaction> supplier) { mTaskOrganizer = taskOrganizer; mWindowDecoration = windowDecoration; mDisplayController = displayController; - mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds); + mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds); mDragStartListener = dragStartListener; mTransactionSupplier = supplier; } diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index 64dfc3ef845d..deebad545c5e 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -8,3 +8,4 @@ madym@google.com hwwang@google.com chenghsiuchang@google.com atsjenk@google.com +jorgegil@google.com diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java index 4de529885565..55781f1b4d6f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.app.TaskInfo; +import android.content.res.Configuration; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.util.Pair; @@ -455,12 +456,21 @@ public class CompatUIWindowManagerTest extends ShellTestCase { verify(mLayout).setCameraCompatHintVisibility(/* show= */ true); } + @Test + public void testWhenDockedStateHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + newTaskInfo.configuration.uiMode |= Configuration.UI_MODE_TYPE_DESK; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); + } + private static TaskInfo createTaskInfo(boolean hasSizeCompat, @TaskInfo.CameraCompatControlState int cameraCompatControlState) { ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = TASK_ID; taskInfo.topActivityInSizeCompat = hasSizeCompat; taskInfo.cameraCompatControlState = cameraCompatControlState; + taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK; return taskInfo; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java index 5bcc72e73cb9..973a99c269ea 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertNull; import android.app.ActivityManager; import android.app.TaskInfo; +import android.content.res.Configuration; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -31,6 +32,8 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; +import junit.framework.Assert; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,63 +49,61 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @SmallTest public class ReachabilityEduWindowManagerTest extends ShellTestCase { - - private static final int USER_ID = 1; - private static final int TASK_ID = 1; - @Mock private SyncTransactionQueue mSyncTransactionQueue; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock - private CompatUIController.CompatUICallback mCallback; - @Mock private CompatUIConfiguration mCompatUIConfiguration; @Mock private DisplayLayout mDisplayLayout; - private TestShellExecutor mExecutor; + private TaskInfo mTaskInfo; + private ReachabilityEduWindowManager mWindowManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mExecutor = new TestShellExecutor(); + mTaskInfo = new ActivityManager.RunningTaskInfo(); + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) + | Configuration.UI_MODE_NIGHT_NO; + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK) + | Configuration.UI_MODE_TYPE_NORMAL; + mWindowManager = createReachabilityEduWindowManager(mTaskInfo); } @Test public void testCreateLayout_notEligible_doesNotCreateLayout() { - final ReachabilityEduWindowManager windowManager = createReachabilityEduWindowManager( - createTaskInfo(/* userId= */ USER_ID, /*isLetterboxDoubleTapEnabled */ false)); - - assertFalse(windowManager.createLayout(/* canShow= */ true)); + assertFalse(mWindowManager.createLayout(/* canShow= */ true)); - assertNull(windowManager.mLayout); + assertNull(mWindowManager.mLayout); } - private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) { - return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue, - mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor); + @Test + public void testWhenDockedStateHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + newTaskInfo.configuration.uiMode = + (newTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK) + | Configuration.UI_MODE_TYPE_DESK; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); } - private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled) { - return createTaskInfo(userId, /* isLetterboxDoubleTapEnabled */ isLetterboxDoubleTapEnabled, - /* topActivityLetterboxVerticalPosition */ -1, - /* topActivityLetterboxHorizontalPosition */ -1, - /* topActivityLetterboxWidth */ -1, - /* topActivityLetterboxHeight */ -1); + @Test + public void testWhenDarkLightThemeHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) + | Configuration.UI_MODE_NIGHT_YES; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); } - private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled, - int topActivityLetterboxVerticalPosition, int topActivityLetterboxHorizontalPosition, - int topActivityLetterboxWidth, int topActivityLetterboxHeight) { - ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); - taskInfo.userId = userId; - taskInfo.taskId = TASK_ID; - taskInfo.isLetterboxDoubleTapEnabled = isLetterboxDoubleTapEnabled; - taskInfo.topActivityLetterboxVerticalPosition = topActivityLetterboxVerticalPosition; - taskInfo.topActivityLetterboxHorizontalPosition = topActivityLetterboxHorizontalPosition; - taskInfo.topActivityLetterboxWidth = topActivityLetterboxWidth; - taskInfo.topActivityLetterboxHeight = topActivityLetterboxHeight; - return taskInfo; + private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) { + return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue, + mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java new file mode 100644 index 000000000000..9f109a1d0f50 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogWindowManagerTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 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.compatui; + +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.content.res.Configuration; +import android.testing.AndroidTestingRunner; +import android.util.Pair; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.transition.Transitions; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.Consumer; + +/** + * Tests for {@link RestartDialogWindowManager}. + * + * Build/Install/Run: + * atest WMShellUnitTests:RestartDialogWindowManagerTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class RestartDialogWindowManagerTest extends ShellTestCase { + + @Mock + private SyncTransactionQueue mSyncTransactionQueue; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private CompatUIConfiguration mCompatUIConfiguration; + @Mock private Transitions mTransitions; + @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback; + @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback; + private RestartDialogWindowManager mWindowManager; + private TaskInfo mTaskInfo; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTaskInfo = new ActivityManager.RunningTaskInfo(); + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) + | Configuration.UI_MODE_NIGHT_NO; + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK) + | Configuration.UI_MODE_TYPE_NORMAL; + mWindowManager = new RestartDialogWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, + mTaskListener, new DisplayLayout(), mTransitions, mOnRestartCallback, + mOnDismissCallback, mCompatUIConfiguration); + } + + @Test + public void testWhenDockedStateHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + newTaskInfo.configuration.uiMode = + (newTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK) + | Configuration.UI_MODE_TYPE_DESK; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); + } + + @Test + public void testWhenDarkLightThemeHasChanged_needsToBeRecreated() { + ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo(); + mTaskInfo.configuration.uiMode = + (mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) + | Configuration.UI_MODE_NIGHT_YES; + + Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); + } +} diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 88e351963148..e21d6fb2fe14 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -45,14 +45,13 @@ const ui::Transform kIdentityTransform; // --- PointerController::DisplayInfoListener --- void PointerController::DisplayInfoListener::onWindowInfosChanged( - const std::vector<android::gui::WindowInfo>&, - const std::vector<android::gui::DisplayInfo>& displayInfos) { + const gui::WindowInfosUpdate& update) { std::scoped_lock lock(mLock); if (mPointerController == nullptr) return; // PointerController uses DisplayInfoListener's lock. base::ScopedLockAssertion assumeLocked(mPointerController->getLock()); - mPointerController->onDisplayInfosChangedLocked(displayInfos); + mPointerController->onDisplayInfosChangedLocked(update.displayInfos); } void PointerController::DisplayInfoListener::onPointerControllerDestroyed() { diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ca14b6e9bfdc..62ee74331302 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -19,6 +19,7 @@ #include <PointerControllerInterface.h> #include <gui/DisplayEventReceiver.h> +#include <gui/WindowInfosUpdate.h> #include <input/DisplayViewport.h> #include <input/Input.h> #include <utils/BitSet.h> @@ -114,8 +115,7 @@ private: class DisplayInfoListener : public gui::WindowInfosListener { public: explicit DisplayInfoListener(PointerController* pc) : mPointerController(pc){}; - void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&, - const std::vector<android::gui::DisplayInfo>&) override; + void onWindowInfosChanged(const gui::WindowInfosUpdate&) override; void onPointerControllerDestroyed(); // This lock is also used by PointerController. See PointerController::getLock(). diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 2378d42793a1..85747514aa03 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -343,7 +343,7 @@ TEST_F(PointerControllerWindowInfoListenerTest, localListenerCopy = registeredListener; } EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered"; - localListenerCopy->onWindowInfosChanged({}, {}); + localListenerCopy->onWindowInfosChanged({{}, {}, 0, 0}); } } // namespace android |