Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1210,7 +1210,8 @@ public abstract class WMShellModule { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, IWindowManager windowManager, Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController Optional<DesktopTasksController> desktopTasksController, ShellTaskOrganizer shellTaskOrganizer ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); Loading @@ -1224,7 +1225,8 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, windowManager, desktopUserRepositories.get(), desktopTasksController.get())); desktopTasksController.get(), shellTaskOrganizer)); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +27 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.wm.shell.desktopmode import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.windowingModeToString import android.content.Context import android.provider.Settings import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS Loading @@ -27,6 +30,7 @@ import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener Loading @@ -45,6 +49,7 @@ class DesktopDisplayEventHandler( private val windowManager: IWindowManager, private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, private val shellTaskOrganizer: ShellTaskOrganizer, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desktopRepository: DesktopRepository Loading Loading @@ -124,13 +129,34 @@ class DesktopDisplayEventHandler( } val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY) requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." } if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) { val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode if (currentDisplayWindowingMode == targetDisplayWindowingMode) { // Already in the target mode. return } logV( "As an external display is connected, changing default display's windowing mode from" + " ${windowingModeToString(currentDisplayWindowingMode)}" + " to ${windowingModeToString(targetDisplayWindowingMode)}" ) val wct = WindowContainerTransaction() wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode) shellTaskOrganizer .getRunningTasks(DEFAULT_DISPLAY) .filter { it.activityType == ACTIVITY_TYPE_STANDARD } .forEach { // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy when (it.windowingMode) { currentDisplayWindowingMode -> { wct.setWindowingMode(it.token, currentDisplayWindowingMode) } targetDisplayWindowingMode -> { wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED) } } } transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt +76 −16 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.wm.shell.desktopmode import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.content.ContentResolver import android.os.Binder import android.platform.test.annotations.EnableFlags Loading @@ -37,7 +39,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.common.ShellExecutor Loading Loading @@ -81,12 +85,20 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories @Mock private lateinit var mockDesktopRepository: DesktopRepository @Mock private lateinit var mockDesktopTasksController: DesktopTasksController @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer private lateinit var mockitoSession: StaticMockitoSession private lateinit var shellInit: ShellInit private lateinit var handler: DesktopDisplayEventHandler private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>() private val runningTasks = mutableListOf<RunningTaskInfo>() private val externalDisplayId = 100 private val freeformTask = TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build() private val fullscreenTask = TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) @Before fun setUp() { Loading @@ -99,8 +111,8 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { shellInit = spy(ShellInit(testExecutor)) whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) .thenReturn(defaultTDA) handler = DesktopDisplayEventHandler( context, Loading @@ -111,7 +123,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { mockWindowManager, mockDesktopUserRepositories, mockDesktopTasksController, shellTaskOrganizer, ) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } runningTasks.add(freeformTask) runningTasks.add(fullscreenTask) shellInit.init() verify(displayController) .addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture()) Loading @@ -127,9 +143,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { extendedDisplayEnabled: Boolean, expectTransition: Boolean, ) { val externalDisplayId = 100 val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode } val settingsSession = ExtendedDisplaySettingsSession( Loading @@ -138,23 +152,17 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { ) settingsSession.use { // The external display connected. whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId)) onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId) tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM // The external display disconnected. whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY)) onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId) connectExternalDisplay() defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM disconnectExternalDisplay() if (expectTransition) { val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(2)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode) assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode) .isEqualTo(defaultWindowingMode) } else { verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) Loading Loading @@ -227,6 +235,58 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) } @Test fun displayWindowingModeSwitch_existingTasksOnConnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN } ExtendedDisplaySettingsSession(context.contentResolver, 1).use { connectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } } @Test fun displayWindowingModeSwitch_existingTasksOnDisconnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN } ExtendedDisplaySettingsSession(context.contentResolver, 1).use { disconnectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) } } private fun connectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId)) onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId) } private fun disconnectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY)) onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId) } private class ExtendedDisplaySettingsSession( private val contentResolver: ContentResolver, private val overrideValue: Int, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1210,7 +1210,8 @@ public abstract class WMShellModule { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, IWindowManager windowManager, Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController Optional<DesktopTasksController> desktopTasksController, ShellTaskOrganizer shellTaskOrganizer ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); Loading @@ -1224,7 +1225,8 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, windowManager, desktopUserRepositories.get(), desktopTasksController.get())); desktopTasksController.get(), shellTaskOrganizer)); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +27 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.wm.shell.desktopmode import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.windowingModeToString import android.content.Context import android.provider.Settings import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS Loading @@ -27,6 +30,7 @@ import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener Loading @@ -45,6 +49,7 @@ class DesktopDisplayEventHandler( private val windowManager: IWindowManager, private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, private val shellTaskOrganizer: ShellTaskOrganizer, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desktopRepository: DesktopRepository Loading Loading @@ -124,13 +129,34 @@ class DesktopDisplayEventHandler( } val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY) requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." } if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) { val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode if (currentDisplayWindowingMode == targetDisplayWindowingMode) { // Already in the target mode. return } logV( "As an external display is connected, changing default display's windowing mode from" + " ${windowingModeToString(currentDisplayWindowingMode)}" + " to ${windowingModeToString(targetDisplayWindowingMode)}" ) val wct = WindowContainerTransaction() wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode) shellTaskOrganizer .getRunningTasks(DEFAULT_DISPLAY) .filter { it.activityType == ACTIVITY_TYPE_STANDARD } .forEach { // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy when (it.windowingMode) { currentDisplayWindowingMode -> { wct.setWindowingMode(it.token, currentDisplayWindowingMode) } targetDisplayWindowingMode -> { wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED) } } } transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt +76 −16 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.wm.shell.desktopmode import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.content.ContentResolver import android.os.Binder import android.platform.test.annotations.EnableFlags Loading @@ -37,7 +39,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.common.ShellExecutor Loading Loading @@ -81,12 +85,20 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories @Mock private lateinit var mockDesktopRepository: DesktopRepository @Mock private lateinit var mockDesktopTasksController: DesktopTasksController @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer private lateinit var mockitoSession: StaticMockitoSession private lateinit var shellInit: ShellInit private lateinit var handler: DesktopDisplayEventHandler private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>() private val runningTasks = mutableListOf<RunningTaskInfo>() private val externalDisplayId = 100 private val freeformTask = TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build() private val fullscreenTask = TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) @Before fun setUp() { Loading @@ -99,8 +111,8 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { shellInit = spy(ShellInit(testExecutor)) whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) .thenReturn(defaultTDA) handler = DesktopDisplayEventHandler( context, Loading @@ -111,7 +123,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { mockWindowManager, mockDesktopUserRepositories, mockDesktopTasksController, shellTaskOrganizer, ) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } runningTasks.add(freeformTask) runningTasks.add(fullscreenTask) shellInit.init() verify(displayController) .addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture()) Loading @@ -127,9 +143,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { extendedDisplayEnabled: Boolean, expectTransition: Boolean, ) { val externalDisplayId = 100 val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode } val settingsSession = ExtendedDisplaySettingsSession( Loading @@ -138,23 +152,17 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { ) settingsSession.use { // The external display connected. whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId)) onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId) tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM // The external display disconnected. whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY)) onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId) connectExternalDisplay() defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM disconnectExternalDisplay() if (expectTransition) { val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(2)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode) assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode) .isEqualTo(defaultWindowingMode) } else { verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) Loading Loading @@ -227,6 +235,58 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) } @Test fun displayWindowingModeSwitch_existingTasksOnConnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN } ExtendedDisplaySettingsSession(context.contentResolver, 1).use { connectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } } @Test fun displayWindowingModeSwitch_existingTasksOnDisconnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN } ExtendedDisplaySettingsSession(context.contentResolver, 1).use { disconnectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) } } private fun connectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId)) onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId) } private fun disconnectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) .thenReturn(intArrayOf(DEFAULT_DISPLAY)) onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId) } private class ExtendedDisplaySettingsSession( private val contentResolver: ContentResolver, private val overrideValue: Int, Loading