Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +37 −1 Original line number Diff line number Diff line Loading @@ -70,7 +70,9 @@ public class DisplayController { private final DesktopState mDesktopState; private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); private DisplayTopology mDisplayTopology; public DisplayController(Context context, IWindowManager wmService, ShellInit shellInit, Loading Loading @@ -198,10 +200,27 @@ public class DisplayController { * Updates the insets for a given display. */ public void updateDisplayInsets(int displayId, InsetsState state) { final Rect oldStableBounds = new Rect(); final Rect newStableBounds = new Rect(); final DisplayLayout oldDisplayLayout = getDisplayLayout(displayId); if (oldDisplayLayout != null) { oldDisplayLayout.getStableBounds(oldStableBounds); } final DisplayRecord r = mDisplays.get(displayId); if (r != null) { r.setInsets(state); } final DisplayLayout newDisplayLayout = getDisplayLayout(displayId); if (newDisplayLayout != null) { newDisplayLayout.getStableBounds(newStableBounds); } if (oldStableBounds != newStableBounds) { for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onStableInsetsChanging( displayId, oldDisplayLayout); } } } /** Loading Loading @@ -350,12 +369,13 @@ public class DisplayController { final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext : mContext.createDisplayContext(display); DisplayLayout oldLayout = dr.mDisplayLayout; final Context context = perDisplayContext.createConfigurationContext(newConfig); final DisplayLayout displayLayout = dr.createLayout(context, display); dr.setDisplayLayout(context, displayLayout); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); displayId, newConfig, oldLayout); } } } Loading Loading @@ -586,6 +606,22 @@ public class DisplayController { */ default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} /** * Called when a display's window-container configuration changes, includes old layout. */ default void onDisplayConfigurationChanged(int displayId, Configuration newConfig, DisplayLayout oldLayout) { this.onDisplayConfigurationChanged(displayId, newConfig); } /** * Notifies listeners of a stable insets change. * This is usually called after a configuration change when the system components update * their bounds. * @param displayId display who's layout is changing. * @param oldLayout the layout of this display before the change is applied. */ default void onStableInsetsChanging(int displayId, DisplayLayout oldLayout) {} /** * Called when a display is removed. */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1729,7 +1729,8 @@ public abstract class WMShellModule { Optional<DesktopDisplayModeController> desktopDisplayModeController, DesktopRepositoryInitializer desktopRepositoryInitializer, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState DesktopState desktopState, Transitions transitions ) { if (!desktopState.canEnterDesktopMode()) { return Optional.empty(); Loading @@ -1747,7 +1748,8 @@ public abstract class WMShellModule { desktopTasksController.get(), desktopDisplayModeController.get(), desksTransitionObserver.get(), desktopState)); desktopState, transitions)); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +138 −1 Original line number Diff line number Diff line Loading @@ -17,16 +17,22 @@ package com.android.wm.shell.desktopmode import android.content.Context import android.content.res.Configuration import android.graphics.Rect import android.os.IBinder import android.os.Trace import android.os.UserHandle import android.os.UserManager import android.util.ArraySet import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.window.DesktopExperienceFlags import android.window.DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS import android.window.DesktopModeFlags import android.window.DisplayAreaInfo import android.window.TransitionInfo import com.android.app.tracing.traceSection import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog Loading @@ -34,6 +40,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.data.DesktopRepository import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer Loading @@ -48,6 +55,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopState import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.UserChangeListener import com.android.wm.shell.transition.Transitions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.launch Loading @@ -66,7 +74,12 @@ class DesktopDisplayEventHandler( private val desktopDisplayModeController: DesktopDisplayModeController, private val desksTransitionObserver: DesksTransitionObserver, private val desktopState: DesktopState, ) : OnDisplaysChangedListener, OnDeskRemovedListener, PreserveDisplayRequestHandler { private val transitions: Transitions, ) : OnDisplaysChangedListener, OnDeskRemovedListener, PreserveDisplayRequestHandler, Transitions.TransitionObserver { private val onDisplayAreaChangeListener = OnDisplayAreaChangeListener { displayId -> logV("displayAreaChanged in displayId=%d", displayId) Loading @@ -79,6 +92,10 @@ class DesktopDisplayEventHandler( // displayId to its uniqueId since we will not be able to fetch it after disconnect. private val uniqueIdByDisplayId = mutableMapOf<Int, String>() private val oldDpiLayoutByDisplayId = mutableMapOf<Int, DisplayLayout>() private val boundsChangedByDisplayId = mutableSetOf<Int>() private val stableBoundsChangedByDisplayId = mutableSetOf<Int>() private val displayConfigById = mutableMapOf<Int, Configuration>() // All uniqueDisplayIds that are currently being restored; any further requests // to restore them will no-op. @VisibleForTesting val displaysMidRestoration = ArraySet<String>() Loading Loading @@ -107,6 +124,126 @@ class DesktopDisplayEventHandler( } } override fun onDisplayConfigurationChanged( displayId: Int, newConfig: Configuration?, oldLayout: DisplayLayout?, ) { val newDisplayLayout = displayController.getDisplayLayout(displayId) val oldDisplayLayout = oldDpiLayoutByDisplayId[displayId] ?: oldLayout if (oldDisplayLayout == null || newDisplayLayout == null) return newConfig?.let { displayConfigById.put(displayId, it) } if (newDisplayLayout.densityDpi() == oldDisplayLayout.densityDpi()) { return } oldDpiLayoutByDisplayId.put(displayId, oldDisplayLayout) val oldStableBounds = Rect() val newStableBounds = Rect() oldDisplayLayout.getStableBounds(oldStableBounds) newDisplayLayout.getStableBounds(newStableBounds) when { oldStableBounds == newStableBounds -> {} // Width update means resolution is updated, and we should wait for TRANSIT_CHANGE // transition to apply new resolution logic. displayResolutionChanged(oldDisplayLayout, newDisplayLayout) -> { transitions.registerObserver(this) boundsChangedByDisplayId.add(displayId) } taskbarInsetsUpdated(oldStableBounds, newStableBounds) -> { stableBoundsChangedByDisplayId.add(displayId) } } resizeTasksIfPreconditionsSatisfied(displayId, newConfig) } override fun onTransitionReady( transition: IBinder, info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, ) { val displayId = info.changes[0].endDisplayId val config = displayConfigById[displayId] if (info.type == TRANSIT_CHANGE) { resizeTasksIfPreconditionsSatisfied(displayId, config, true) } } override fun onStableInsetsChanging(displayId: Int, oldLayout: DisplayLayout?) { val oldStableBounds = Rect() val newStableBounds = Rect() val oldestLayout = oldDpiLayoutByDisplayId[displayId] ?: oldLayout val newLayout = displayController.getDisplayLayout(displayId) val config = displayConfigById[displayId] if (oldestLayout == null || newLayout == null) return oldDpiLayoutByDisplayId.put(displayId, oldestLayout) oldestLayout.getStableBounds(oldStableBounds) newLayout.getStableBounds(newStableBounds) when { oldStableBounds == newStableBounds -> { // No change in stable bounds. } // Width or height updates mean the resolution has changed. displayResolutionChanged(oldestLayout, newLayout) -> { boundsChangedByDisplayId.add(displayId) } taskbarInsetsUpdated(oldStableBounds, newStableBounds) -> { stableBoundsChangedByDisplayId.add(displayId) } } resizeTasksIfPreconditionsSatisfied(displayId, config) } private fun displayResolutionChanged( oldestLayout: DisplayLayout, newLayout: DisplayLayout, ): Boolean = oldestLayout.width() != newLayout.width() || oldestLayout.height() != newLayout.height() private fun taskbarInsetsUpdated(oldStableBounds: Rect, newStableBounds: Rect): Boolean = oldStableBounds.bottom != newStableBounds.bottom private fun resizeTasksIfPreconditionsSatisfied( displayId: Int, config: Configuration?, boundsChangeReady: Boolean = false, ) { when { config == null -> {} dpiChangedAndInsetsReadyForDisplay(displayId) -> { desktopTasksController.onDisplayDpiChanging( displayId, config, oldDpiLayoutByDisplayId[displayId], ) oldDpiLayoutByDisplayId.remove(displayId) stableBoundsChangedByDisplayId.remove(displayId) } resolutionChangedAndInsetsReadyForDisplay(displayId, boundsChangeReady) -> { desktopTasksController.onDisplayDpiChanging( displayId, config, oldDpiLayoutByDisplayId[displayId], ) transitions.unregisterObserver(this) oldDpiLayoutByDisplayId.remove(displayId) boundsChangedByDisplayId.remove(displayId) } } } private fun dpiChangedAndInsetsReadyForDisplay(displayId: Int): Boolean = displayId in oldDpiLayoutByDisplayId && displayId in stableBoundsChangedByDisplayId private fun resolutionChangedAndInsetsReadyForDisplay( displayId: Int, transitionReady: Boolean, ): Boolean = displayId in oldDpiLayoutByDisplayId && displayId in boundsChangedByDisplayId && transitionReady override fun onDisplayAdded(displayId: Int) = traceSection( Trace.TRACE_TAG_WINDOW_MANAGER, Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +156 −14 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import com.android.wm.shell.desktopmode.data.DesktopRepository.DeskChangeListene import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer.DeskRecreationFactory import com.android.wm.shell.desktopmode.data.persistence.DesktopTaskTilingState import com.android.wm.shell.desktopmode.desktopfirst.DesktopFirstListenerManager import com.android.wm.shell.desktopmode.desktopfirst.isDisplayDesktopFirst import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider Loading Loading @@ -187,6 +188,7 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener import com.android.wm.shell.windowdecor.extension.isFullscreen import com.android.wm.shell.windowdecor.extension.isMultiWindow import com.android.wm.shell.windowdecor.extension.requestingImmersive import com.android.wm.shell.windowdecor.tiling.DesktopTilingWindowDecoration.Companion.getDividerBoundsForZombieSession import com.android.wm.shell.windowdecor.tiling.SnapEventHandler import com.android.wm.shell.windowdecor.tiling.TilingDisplayReconnectEventHandler import java.io.PrintWriter Loading Loading @@ -942,7 +944,55 @@ class DesktopTasksController( val taskIds = desktopRepository.getActiveTaskIdsInDesk(deskId) for (taskId in taskIds) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: continue applyFreeformDisplayChange(wct, task, destinationDisplayId, deskId) val taskTilingState = when (taskId) { desktopRepository.getLeftTiledTask(deskId) -> DesktopTaskTilingState.LEFT desktopRepository.getRightTiledTask(deskId) -> DesktopTaskTilingState.RIGHT else -> DesktopTaskTilingState.NONE } val newStableBounds = Rect() val oldStableBounds = Rect() val sourceLayout = displayController.getDisplayLayout(task.displayId) ?: return val destLayout = destDisplayLayout ?: return destLayout.getStableBounds(newStableBounds) sourceLayout.getStableBounds(oldStableBounds) val newDisplayContext = displayController.getDisplayContext(destinationDisplayId) ?: return val newToOldDpiRatio = destLayout.densityDpi().toDouble() / sourceLayout.densityDpi().toDouble() val dividerBounds: Rect? = when (taskTilingState) { DesktopTaskTilingState.LEFT -> getDividerBoundsForZombieSession( task.configuration.windowConfiguration.bounds, null, newStableBounds, oldStableBounds, newToOldDpiRatio, newDisplayContext, ) DesktopTaskTilingState.RIGHT -> getDividerBoundsForZombieSession( null, task.configuration.windowConfiguration.bounds, newStableBounds, oldStableBounds, newToOldDpiRatio, newDisplayContext, ) else -> null } applyFreeformDisplayChange( wct, task, destDisplayLayout, displayController.getDisplayLayout(task.displayId), taskTilingState, deskId, dividerBounds, ) } runOnTransitStartList.add { transition -> desksTransitionObserver.addPendingTransition( Loading @@ -968,6 +1018,58 @@ class DesktopTasksController( } } fun onDisplayDpiChanging( displayId: Int, newConfig: Configuration, oldDisplayLayout: DisplayLayout?, ) { if (!DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue) return val newDisplayLayout = displayController.getDisplayLayout(displayId) ?: return if (oldDisplayLayout == null) return val oldStableBounds = Rect() oldDisplayLayout.getStableBounds(oldStableBounds) val newToOldDpiRatio = newDisplayLayout.densityDpi().toDouble() / oldDisplayLayout.densityDpi() snapEventHandler.onDisplayLayoutChange( displayId, newConfig, oldStableBounds, newToOldDpiRatio, ) val stableBounds = Rect() newDisplayLayout?.getStableBounds(stableBounds) val wct = WindowContainerTransaction() val userId = userRepositories.current.userId userRepositories.forAllRepositories { userRepo -> if (userId == userRepo.userId) { val deskIds = userRepo.getDeskIds(displayId).toList() for (deskId in deskIds) { val deskTasks = userRepo.getActiveTaskIdsInDesk(deskId) if (deskTasks.isEmpty()) continue for (taskId in deskTasks) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: continue val taskTilingState = when (taskId) { userRepo.getLeftTiledTask(deskId) -> DesktopTaskTilingState.LEFT userRepo.getRightTiledTask(deskId) -> DesktopTaskTilingState.RIGHT else -> DesktopTaskTilingState.NONE } applyFreeformDisplayChange( wct, task, newDisplayLayout, oldDisplayLayout, taskTilingState, deskId, ) } } } } transitions.startTransition(TRANSIT_CHANGE, wct, null) } private fun handleProjectedModeDisconnect( desktopRepository: DesktopRepository, wct: WindowContainerTransaction, Loading Loading @@ -2539,7 +2641,16 @@ class DesktopTasksController( wct.setAppBounds(task.token, appBounds) } } else if (DesktopExperienceFlags.ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT.isTrue) { applyFreeformDisplayChange(wct, task, displayId, destinationDeskId) applyFreeformDisplayChange( wct, task, displayController.getDisplayLayout(displayId), displayController.getDisplayLayout(task.displayId), // Tiling state will be broken if it exists when a task is moved to next // display. DesktopTaskTilingState.NONE, destinationDeskId, ) } } Loading Loading @@ -4576,20 +4687,24 @@ class DesktopTasksController( } /** * Apply changes to move a freeform task from one display to another, which includes handling * density changes between displays. * Apply changes to move a freeform task on display or it's setting changing, which includes * handling density changes between displays or on the same display. */ private fun applyFreeformDisplayChange( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo, destDisplayId: Int, destDeskId: Int, destLayout: DisplayLayout?, sourceLayout: DisplayLayout?, taskTilingState: DesktopTaskTilingState, deskId: Int, updatedDividerBounds: Rect? = null, ) { val sourceLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val destLayout = displayController.getDisplayLayout(destDisplayId) ?: return if (sourceLayout == null || destLayout == null) return val bounds = taskInfo.configuration.windowConfiguration.bounds val scaledWidth = bounds.width() * destLayout.densityDpi() / sourceLayout.densityDpi() val scaledHeight = bounds.height() * destLayout.densityDpi() / sourceLayout.densityDpi() val newToOldDpiRatio = destLayout.densityDpi().toDouble() / sourceLayout.densityDpi().toDouble() val scaledWidth = bounds.width() * newToOldDpiRatio val scaledHeight = bounds.height() * newToOldDpiRatio val sourceWidthMargin = sourceLayout.width() - bounds.width() val sourceHeightMargin = sourceLayout.height() - bounds.height() val destWidthMargin = destLayout.width() - scaledWidth Loading @@ -4606,12 +4721,39 @@ class DesktopTasksController( } else { destHeightMargin / 2 } val sourceStableBounds = Rect() val destStableBounds = Rect() sourceLayout.getStableBounds(sourceStableBounds) destLayout.getStableBounds(destStableBounds) val boundsWithinDisplay = if (destWidthMargin >= 0 && destHeightMargin >= 0) { Rect(0, 0, scaledWidth, scaledHeight).apply { if (taskTilingState == DesktopTaskTilingState.LEFT) { val dividerBounds = updatedDividerBounds ?: snapEventHandler.getDividerBounds(deskId) Rect( destStableBounds.left, destStableBounds.top, dividerBounds.left, destStableBounds.bottom, ) } else if (taskTilingState == DesktopTaskTilingState.RIGHT) { val dividerBounds = updatedDividerBounds ?: snapEventHandler.getDividerBounds(deskId) Rect( dividerBounds.right, destStableBounds.top, destStableBounds.right, destStableBounds.bottom, ) } else if ( destWidthMargin >= 0 && destHeightMargin >= 0 && (bounds.width() < sourceStableBounds.width() || bounds.height() < sourceStableBounds.height()) ) { Rect(0, 0, scaledWidth.toInt(), scaledHeight.toInt()).apply { offsetTo( scaledLeft.coerceIn(0, destWidthMargin), scaledTop.coerceIn(0, destHeightMargin), scaledLeft.coerceIn(0.0, destWidthMargin).toInt(), scaledTop.coerceIn(0.0, destHeightMargin).toInt(), ) } } else { Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +12 −0 Original line number Diff line number Diff line Loading @@ -1058,6 +1058,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTilingDecorViewModel.onExplodedViewReorder(deskId, topTaskId); } @Override public void onDisplayLayoutChange(int displayId, Configuration config, @NonNull Rect oldStableBounds, double newToOldDpiRatio) { mDesktopTilingDecorViewModel.onDisplayLayoutChange(displayId, config, oldStableBounds, newToOldDpiRatio); } @Override public @NonNull Rect getDividerBounds(int deskId) { return mDesktopTilingDecorViewModel.getDividerBounds(deskId); } @Override public void onDeskActivated(int deskId, int displayId) { if (mDesktopTilingDecorViewModel.tilingDeskActive(deskId)) { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +37 −1 Original line number Diff line number Diff line Loading @@ -70,7 +70,9 @@ public class DisplayController { private final DesktopState mDesktopState; private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); private DisplayTopology mDisplayTopology; public DisplayController(Context context, IWindowManager wmService, ShellInit shellInit, Loading Loading @@ -198,10 +200,27 @@ public class DisplayController { * Updates the insets for a given display. */ public void updateDisplayInsets(int displayId, InsetsState state) { final Rect oldStableBounds = new Rect(); final Rect newStableBounds = new Rect(); final DisplayLayout oldDisplayLayout = getDisplayLayout(displayId); if (oldDisplayLayout != null) { oldDisplayLayout.getStableBounds(oldStableBounds); } final DisplayRecord r = mDisplays.get(displayId); if (r != null) { r.setInsets(state); } final DisplayLayout newDisplayLayout = getDisplayLayout(displayId); if (newDisplayLayout != null) { newDisplayLayout.getStableBounds(newStableBounds); } if (oldStableBounds != newStableBounds) { for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onStableInsetsChanging( displayId, oldDisplayLayout); } } } /** Loading Loading @@ -350,12 +369,13 @@ public class DisplayController { final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext : mContext.createDisplayContext(display); DisplayLayout oldLayout = dr.mDisplayLayout; final Context context = perDisplayContext.createConfigurationContext(newConfig); final DisplayLayout displayLayout = dr.createLayout(context, display); dr.setDisplayLayout(context, displayLayout); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); displayId, newConfig, oldLayout); } } } Loading Loading @@ -586,6 +606,22 @@ public class DisplayController { */ default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} /** * Called when a display's window-container configuration changes, includes old layout. */ default void onDisplayConfigurationChanged(int displayId, Configuration newConfig, DisplayLayout oldLayout) { this.onDisplayConfigurationChanged(displayId, newConfig); } /** * Notifies listeners of a stable insets change. * This is usually called after a configuration change when the system components update * their bounds. * @param displayId display who's layout is changing. * @param oldLayout the layout of this display before the change is applied. */ default void onStableInsetsChanging(int displayId, DisplayLayout oldLayout) {} /** * Called when a display is removed. */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1729,7 +1729,8 @@ public abstract class WMShellModule { Optional<DesktopDisplayModeController> desktopDisplayModeController, DesktopRepositoryInitializer desktopRepositoryInitializer, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState DesktopState desktopState, Transitions transitions ) { if (!desktopState.canEnterDesktopMode()) { return Optional.empty(); Loading @@ -1747,7 +1748,8 @@ public abstract class WMShellModule { desktopTasksController.get(), desktopDisplayModeController.get(), desksTransitionObserver.get(), desktopState)); desktopState, transitions)); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +138 −1 Original line number Diff line number Diff line Loading @@ -17,16 +17,22 @@ package com.android.wm.shell.desktopmode import android.content.Context import android.content.res.Configuration import android.graphics.Rect import android.os.IBinder import android.os.Trace import android.os.UserHandle import android.os.UserManager import android.util.ArraySet import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.window.DesktopExperienceFlags import android.window.DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_ACTIVATION_IN_DESKTOP_FIRST_DISPLAYS import android.window.DesktopModeFlags import android.window.DisplayAreaInfo import android.window.TransitionInfo import com.android.app.tracing.traceSection import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog Loading @@ -34,6 +40,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.data.DesktopRepository import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer Loading @@ -48,6 +55,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopState import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.UserChangeListener import com.android.wm.shell.transition.Transitions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.launch Loading @@ -66,7 +74,12 @@ class DesktopDisplayEventHandler( private val desktopDisplayModeController: DesktopDisplayModeController, private val desksTransitionObserver: DesksTransitionObserver, private val desktopState: DesktopState, ) : OnDisplaysChangedListener, OnDeskRemovedListener, PreserveDisplayRequestHandler { private val transitions: Transitions, ) : OnDisplaysChangedListener, OnDeskRemovedListener, PreserveDisplayRequestHandler, Transitions.TransitionObserver { private val onDisplayAreaChangeListener = OnDisplayAreaChangeListener { displayId -> logV("displayAreaChanged in displayId=%d", displayId) Loading @@ -79,6 +92,10 @@ class DesktopDisplayEventHandler( // displayId to its uniqueId since we will not be able to fetch it after disconnect. private val uniqueIdByDisplayId = mutableMapOf<Int, String>() private val oldDpiLayoutByDisplayId = mutableMapOf<Int, DisplayLayout>() private val boundsChangedByDisplayId = mutableSetOf<Int>() private val stableBoundsChangedByDisplayId = mutableSetOf<Int>() private val displayConfigById = mutableMapOf<Int, Configuration>() // All uniqueDisplayIds that are currently being restored; any further requests // to restore them will no-op. @VisibleForTesting val displaysMidRestoration = ArraySet<String>() Loading Loading @@ -107,6 +124,126 @@ class DesktopDisplayEventHandler( } } override fun onDisplayConfigurationChanged( displayId: Int, newConfig: Configuration?, oldLayout: DisplayLayout?, ) { val newDisplayLayout = displayController.getDisplayLayout(displayId) val oldDisplayLayout = oldDpiLayoutByDisplayId[displayId] ?: oldLayout if (oldDisplayLayout == null || newDisplayLayout == null) return newConfig?.let { displayConfigById.put(displayId, it) } if (newDisplayLayout.densityDpi() == oldDisplayLayout.densityDpi()) { return } oldDpiLayoutByDisplayId.put(displayId, oldDisplayLayout) val oldStableBounds = Rect() val newStableBounds = Rect() oldDisplayLayout.getStableBounds(oldStableBounds) newDisplayLayout.getStableBounds(newStableBounds) when { oldStableBounds == newStableBounds -> {} // Width update means resolution is updated, and we should wait for TRANSIT_CHANGE // transition to apply new resolution logic. displayResolutionChanged(oldDisplayLayout, newDisplayLayout) -> { transitions.registerObserver(this) boundsChangedByDisplayId.add(displayId) } taskbarInsetsUpdated(oldStableBounds, newStableBounds) -> { stableBoundsChangedByDisplayId.add(displayId) } } resizeTasksIfPreconditionsSatisfied(displayId, newConfig) } override fun onTransitionReady( transition: IBinder, info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, ) { val displayId = info.changes[0].endDisplayId val config = displayConfigById[displayId] if (info.type == TRANSIT_CHANGE) { resizeTasksIfPreconditionsSatisfied(displayId, config, true) } } override fun onStableInsetsChanging(displayId: Int, oldLayout: DisplayLayout?) { val oldStableBounds = Rect() val newStableBounds = Rect() val oldestLayout = oldDpiLayoutByDisplayId[displayId] ?: oldLayout val newLayout = displayController.getDisplayLayout(displayId) val config = displayConfigById[displayId] if (oldestLayout == null || newLayout == null) return oldDpiLayoutByDisplayId.put(displayId, oldestLayout) oldestLayout.getStableBounds(oldStableBounds) newLayout.getStableBounds(newStableBounds) when { oldStableBounds == newStableBounds -> { // No change in stable bounds. } // Width or height updates mean the resolution has changed. displayResolutionChanged(oldestLayout, newLayout) -> { boundsChangedByDisplayId.add(displayId) } taskbarInsetsUpdated(oldStableBounds, newStableBounds) -> { stableBoundsChangedByDisplayId.add(displayId) } } resizeTasksIfPreconditionsSatisfied(displayId, config) } private fun displayResolutionChanged( oldestLayout: DisplayLayout, newLayout: DisplayLayout, ): Boolean = oldestLayout.width() != newLayout.width() || oldestLayout.height() != newLayout.height() private fun taskbarInsetsUpdated(oldStableBounds: Rect, newStableBounds: Rect): Boolean = oldStableBounds.bottom != newStableBounds.bottom private fun resizeTasksIfPreconditionsSatisfied( displayId: Int, config: Configuration?, boundsChangeReady: Boolean = false, ) { when { config == null -> {} dpiChangedAndInsetsReadyForDisplay(displayId) -> { desktopTasksController.onDisplayDpiChanging( displayId, config, oldDpiLayoutByDisplayId[displayId], ) oldDpiLayoutByDisplayId.remove(displayId) stableBoundsChangedByDisplayId.remove(displayId) } resolutionChangedAndInsetsReadyForDisplay(displayId, boundsChangeReady) -> { desktopTasksController.onDisplayDpiChanging( displayId, config, oldDpiLayoutByDisplayId[displayId], ) transitions.unregisterObserver(this) oldDpiLayoutByDisplayId.remove(displayId) boundsChangedByDisplayId.remove(displayId) } } } private fun dpiChangedAndInsetsReadyForDisplay(displayId: Int): Boolean = displayId in oldDpiLayoutByDisplayId && displayId in stableBoundsChangedByDisplayId private fun resolutionChangedAndInsetsReadyForDisplay( displayId: Int, transitionReady: Boolean, ): Boolean = displayId in oldDpiLayoutByDisplayId && displayId in boundsChangedByDisplayId && transitionReady override fun onDisplayAdded(displayId: Int) = traceSection( Trace.TRACE_TAG_WINDOW_MANAGER, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +156 −14 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import com.android.wm.shell.desktopmode.data.DesktopRepository.DeskChangeListene import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer import com.android.wm.shell.desktopmode.data.DesktopRepositoryInitializer.DeskRecreationFactory import com.android.wm.shell.desktopmode.data.persistence.DesktopTaskTilingState import com.android.wm.shell.desktopmode.desktopfirst.DesktopFirstListenerManager import com.android.wm.shell.desktopmode.desktopfirst.isDisplayDesktopFirst import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider Loading Loading @@ -187,6 +188,7 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener import com.android.wm.shell.windowdecor.extension.isFullscreen import com.android.wm.shell.windowdecor.extension.isMultiWindow import com.android.wm.shell.windowdecor.extension.requestingImmersive import com.android.wm.shell.windowdecor.tiling.DesktopTilingWindowDecoration.Companion.getDividerBoundsForZombieSession import com.android.wm.shell.windowdecor.tiling.SnapEventHandler import com.android.wm.shell.windowdecor.tiling.TilingDisplayReconnectEventHandler import java.io.PrintWriter Loading Loading @@ -942,7 +944,55 @@ class DesktopTasksController( val taskIds = desktopRepository.getActiveTaskIdsInDesk(deskId) for (taskId in taskIds) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: continue applyFreeformDisplayChange(wct, task, destinationDisplayId, deskId) val taskTilingState = when (taskId) { desktopRepository.getLeftTiledTask(deskId) -> DesktopTaskTilingState.LEFT desktopRepository.getRightTiledTask(deskId) -> DesktopTaskTilingState.RIGHT else -> DesktopTaskTilingState.NONE } val newStableBounds = Rect() val oldStableBounds = Rect() val sourceLayout = displayController.getDisplayLayout(task.displayId) ?: return val destLayout = destDisplayLayout ?: return destLayout.getStableBounds(newStableBounds) sourceLayout.getStableBounds(oldStableBounds) val newDisplayContext = displayController.getDisplayContext(destinationDisplayId) ?: return val newToOldDpiRatio = destLayout.densityDpi().toDouble() / sourceLayout.densityDpi().toDouble() val dividerBounds: Rect? = when (taskTilingState) { DesktopTaskTilingState.LEFT -> getDividerBoundsForZombieSession( task.configuration.windowConfiguration.bounds, null, newStableBounds, oldStableBounds, newToOldDpiRatio, newDisplayContext, ) DesktopTaskTilingState.RIGHT -> getDividerBoundsForZombieSession( null, task.configuration.windowConfiguration.bounds, newStableBounds, oldStableBounds, newToOldDpiRatio, newDisplayContext, ) else -> null } applyFreeformDisplayChange( wct, task, destDisplayLayout, displayController.getDisplayLayout(task.displayId), taskTilingState, deskId, dividerBounds, ) } runOnTransitStartList.add { transition -> desksTransitionObserver.addPendingTransition( Loading @@ -968,6 +1018,58 @@ class DesktopTasksController( } } fun onDisplayDpiChanging( displayId: Int, newConfig: Configuration, oldDisplayLayout: DisplayLayout?, ) { if (!DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue) return val newDisplayLayout = displayController.getDisplayLayout(displayId) ?: return if (oldDisplayLayout == null) return val oldStableBounds = Rect() oldDisplayLayout.getStableBounds(oldStableBounds) val newToOldDpiRatio = newDisplayLayout.densityDpi().toDouble() / oldDisplayLayout.densityDpi() snapEventHandler.onDisplayLayoutChange( displayId, newConfig, oldStableBounds, newToOldDpiRatio, ) val stableBounds = Rect() newDisplayLayout?.getStableBounds(stableBounds) val wct = WindowContainerTransaction() val userId = userRepositories.current.userId userRepositories.forAllRepositories { userRepo -> if (userId == userRepo.userId) { val deskIds = userRepo.getDeskIds(displayId).toList() for (deskId in deskIds) { val deskTasks = userRepo.getActiveTaskIdsInDesk(deskId) if (deskTasks.isEmpty()) continue for (taskId in deskTasks) { val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: continue val taskTilingState = when (taskId) { userRepo.getLeftTiledTask(deskId) -> DesktopTaskTilingState.LEFT userRepo.getRightTiledTask(deskId) -> DesktopTaskTilingState.RIGHT else -> DesktopTaskTilingState.NONE } applyFreeformDisplayChange( wct, task, newDisplayLayout, oldDisplayLayout, taskTilingState, deskId, ) } } } } transitions.startTransition(TRANSIT_CHANGE, wct, null) } private fun handleProjectedModeDisconnect( desktopRepository: DesktopRepository, wct: WindowContainerTransaction, Loading Loading @@ -2539,7 +2641,16 @@ class DesktopTasksController( wct.setAppBounds(task.token, appBounds) } } else if (DesktopExperienceFlags.ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT.isTrue) { applyFreeformDisplayChange(wct, task, displayId, destinationDeskId) applyFreeformDisplayChange( wct, task, displayController.getDisplayLayout(displayId), displayController.getDisplayLayout(task.displayId), // Tiling state will be broken if it exists when a task is moved to next // display. DesktopTaskTilingState.NONE, destinationDeskId, ) } } Loading Loading @@ -4576,20 +4687,24 @@ class DesktopTasksController( } /** * Apply changes to move a freeform task from one display to another, which includes handling * density changes between displays. * Apply changes to move a freeform task on display or it's setting changing, which includes * handling density changes between displays or on the same display. */ private fun applyFreeformDisplayChange( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo, destDisplayId: Int, destDeskId: Int, destLayout: DisplayLayout?, sourceLayout: DisplayLayout?, taskTilingState: DesktopTaskTilingState, deskId: Int, updatedDividerBounds: Rect? = null, ) { val sourceLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val destLayout = displayController.getDisplayLayout(destDisplayId) ?: return if (sourceLayout == null || destLayout == null) return val bounds = taskInfo.configuration.windowConfiguration.bounds val scaledWidth = bounds.width() * destLayout.densityDpi() / sourceLayout.densityDpi() val scaledHeight = bounds.height() * destLayout.densityDpi() / sourceLayout.densityDpi() val newToOldDpiRatio = destLayout.densityDpi().toDouble() / sourceLayout.densityDpi().toDouble() val scaledWidth = bounds.width() * newToOldDpiRatio val scaledHeight = bounds.height() * newToOldDpiRatio val sourceWidthMargin = sourceLayout.width() - bounds.width() val sourceHeightMargin = sourceLayout.height() - bounds.height() val destWidthMargin = destLayout.width() - scaledWidth Loading @@ -4606,12 +4721,39 @@ class DesktopTasksController( } else { destHeightMargin / 2 } val sourceStableBounds = Rect() val destStableBounds = Rect() sourceLayout.getStableBounds(sourceStableBounds) destLayout.getStableBounds(destStableBounds) val boundsWithinDisplay = if (destWidthMargin >= 0 && destHeightMargin >= 0) { Rect(0, 0, scaledWidth, scaledHeight).apply { if (taskTilingState == DesktopTaskTilingState.LEFT) { val dividerBounds = updatedDividerBounds ?: snapEventHandler.getDividerBounds(deskId) Rect( destStableBounds.left, destStableBounds.top, dividerBounds.left, destStableBounds.bottom, ) } else if (taskTilingState == DesktopTaskTilingState.RIGHT) { val dividerBounds = updatedDividerBounds ?: snapEventHandler.getDividerBounds(deskId) Rect( dividerBounds.right, destStableBounds.top, destStableBounds.right, destStableBounds.bottom, ) } else if ( destWidthMargin >= 0 && destHeightMargin >= 0 && (bounds.width() < sourceStableBounds.width() || bounds.height() < sourceStableBounds.height()) ) { Rect(0, 0, scaledWidth.toInt(), scaledHeight.toInt()).apply { offsetTo( scaledLeft.coerceIn(0, destWidthMargin), scaledTop.coerceIn(0, destHeightMargin), scaledLeft.coerceIn(0.0, destWidthMargin).toInt(), scaledTop.coerceIn(0.0, destHeightMargin).toInt(), ) } } else { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +12 −0 Original line number Diff line number Diff line Loading @@ -1058,6 +1058,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTilingDecorViewModel.onExplodedViewReorder(deskId, topTaskId); } @Override public void onDisplayLayoutChange(int displayId, Configuration config, @NonNull Rect oldStableBounds, double newToOldDpiRatio) { mDesktopTilingDecorViewModel.onDisplayLayoutChange(displayId, config, oldStableBounds, newToOldDpiRatio); } @Override public @NonNull Rect getDividerBounds(int deskId) { return mDesktopTilingDecorViewModel.getDividerBounds(deskId); } @Override public void onDeskActivated(int deskId, int displayId) { if (mDesktopTilingDecorViewModel.tilingDeskActive(deskId)) { Loading