Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -491,7 +491,8 @@ public abstract class WMShellModule { Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState) { DesktopState desktopState, Optional<DesktopImeHandler> desktopImeHandler) { return new FreeformTaskTransitionObserver( shellInit, transitions, Loading @@ -500,7 +501,8 @@ public abstract class WMShellModule { taskChangeListener, focusTransitionObserver, desksTransitionObserver, desktopState); desktopState, desktopImeHandler); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt +19 −1 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ class DesktopImeHandler( } private val taskToImeTarget = mutableMapOf<Int, ImeTarget>() private var imeTriggeredTransition: IBinder? = null data class ImeTarget( var topTask: ActivityManager.RunningTaskInfo, Loading Loading @@ -133,7 +134,7 @@ class DesktopImeHandler( logD("Moving task %d due to IME", imeTarget.topTask.taskId) val wct = WindowContainerTransaction().setBounds(imeTarget.topTask.token, finalBounds) transitions.startTransition(TRANSIT_CHANGE, wct, this) imeTriggeredTransition = transitions.startTransition(TRANSIT_CHANGE, wct, this) taskToImeTarget[currentTopTask.taskId]?.newBounds = finalBounds } else { val wct = WindowContainerTransaction() Loading @@ -144,6 +145,7 @@ class DesktopImeHandler( if (task.configuration.windowConfiguration.bounds == imeTarget.newBounds) { val finalBounds = imeTarget.previousBounds ?: return@forEach logD("Restoring task %d due to IME", taskId) // Restore the previous bounds if they exist wct.setBounds(imeTarget.topTask.token, finalBounds) } Loading @@ -164,6 +166,22 @@ class DesktopImeHandler( shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } /** * If a transition related to a target that we have previously moved up, remove it from the * target list so we do not restore its bounds. */ fun onTransitionReady(transition: IBinder, info: TransitionInfo) { // Do nothing if we get the callback for IME triggered transition if (transition == imeTriggeredTransition || taskToImeTarget.isEmpty()) return // If there is a transition targeting the IME targets remove them from the list info.changes.forEach { change -> if (taskToImeTarget[change.taskInfo?.taskId] != null) { taskToImeTarget.remove(change.taskInfo?.taskId) } } } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +8 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.window.WindowContainerToken; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.wm.shell.desktopmode.DesktopImeHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver; import com.android.wm.shell.shared.desktopmode.DesktopState; Loading Loading @@ -56,6 +57,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs private final Optional<TaskChangeListener> mTaskChangeListener; private final FocusTransitionObserver mFocusTransitionObserver; private final Optional<DesksTransitionObserver> mDesksTransitionObserver; private final Optional<DesktopImeHandler> mDesktopImeHandler; private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo = new HashMap<>(); Loading @@ -71,13 +73,15 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState) { DesktopState desktopState, Optional<DesktopImeHandler> desktopImeHandler) { mTransitions = transitions; mDesktopImmersiveController = desktopImmersiveController; mWindowDecorViewModel = windowDecorViewModel; mTaskChangeListener = taskChangeListener; mFocusTransitionObserver = focusTransitionObserver; mDesksTransitionObserver = desksTransitionObserver; mDesktopImeHandler = desktopImeHandler; if (FreeformComponents.requiresFreeformComponents(desktopState)) { shellInit.addInitCallback(this::onInit, this); } Loading Loading @@ -109,6 +113,9 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs // TODO(371503964): Remove this once the unified task repository is ready. mFocusTransitionObserver.updateFocusState(info); // Call after the focus state update to have the correct focused window. mDesktopImeHandler.ifPresent(o -> o.onTransitionReady(transition, info)); final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>(); final ArrayList<WindowContainerToken> taskParents = new ArrayList<>(); for (TransitionInfo.Change change : info.getChanges()) { Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt +73 −0 Original line number Diff line number Diff line Loading @@ -20,10 +20,14 @@ import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration import android.content.Context import android.graphics.Rect import android.os.IBinder import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_TO_BACK import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerTransaction import com.android.window.flags.Flags import com.android.wm.shell.ShellTaskOrganizer Loading @@ -40,6 +44,7 @@ import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Before import org.mockito.ArgumentCaptor import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq Loading Loading @@ -339,6 +344,62 @@ class DesktopImeHandlerTest : ShellTestCase() { .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS, ) fun onImeStartPositioning_newTransitionOnTask_doesNotRestorePreImeBounds() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = true, isFloating = false, t = mock(), ) // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Create a transition that affects the freeform task we modified previously imeHandler.onTransitionReady( transition = Mockito.mock(IBinder::class.java), info = createToBackTransition(freeformTask), ) // This should be no op. imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = false, isFloating = false, t = mock(), ) // Task is not moved back to original position with a new transition. verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Loading Loading @@ -393,6 +454,18 @@ class DesktopImeHandlerTest : ShellTestCase() { } } private fun createToBackTransition(task: RunningTaskInfo?) = TransitionInfo(TRANSIT_TO_BACK, /* flags= */ 0).apply { addChange( Change(mock(), mock()).apply { mode = TRANSIT_TO_BACK parent = null taskInfo = task flags = flags } ) } private companion object { private const val DISPLAY_DIMENSION_SHORT = 1600 private const val DISPLAY_DIMENSION_LONG = 2560 Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java +4 −2 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest; import com.android.window.flags.Flags; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.desktopmode.DesktopImeHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver; import com.android.wm.shell.shared.desktopmode.FakeDesktopState; Loading Loading @@ -76,8 +77,8 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase { @Mock private TaskChangeListener mTaskChangeListener; @Mock private FocusTransitionObserver mFocusTransitionObserver; @Mock private DesksTransitionObserver mDesksTransitionObserver; @Mock private DesktopImeHandler mDesktopImeHandler; private FakeDesktopState mDesktopState; private FreeformTaskTransitionObserver mTransitionObserver; private AutoCloseable mMocksInits = null; Loading @@ -102,7 +103,8 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase { Optional.of(mTaskChangeListener), mFocusTransitionObserver, Optional.of(mDesksTransitionObserver), mDesktopState); mDesktopState, Optional.of(mDesktopImeHandler)); final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver)); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -491,7 +491,8 @@ public abstract class WMShellModule { Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState) { DesktopState desktopState, Optional<DesktopImeHandler> desktopImeHandler) { return new FreeformTaskTransitionObserver( shellInit, transitions, Loading @@ -500,7 +501,8 @@ public abstract class WMShellModule { taskChangeListener, focusTransitionObserver, desksTransitionObserver, desktopState); desktopState, desktopImeHandler); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt +19 −1 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ class DesktopImeHandler( } private val taskToImeTarget = mutableMapOf<Int, ImeTarget>() private var imeTriggeredTransition: IBinder? = null data class ImeTarget( var topTask: ActivityManager.RunningTaskInfo, Loading Loading @@ -133,7 +134,7 @@ class DesktopImeHandler( logD("Moving task %d due to IME", imeTarget.topTask.taskId) val wct = WindowContainerTransaction().setBounds(imeTarget.topTask.token, finalBounds) transitions.startTransition(TRANSIT_CHANGE, wct, this) imeTriggeredTransition = transitions.startTransition(TRANSIT_CHANGE, wct, this) taskToImeTarget[currentTopTask.taskId]?.newBounds = finalBounds } else { val wct = WindowContainerTransaction() Loading @@ -144,6 +145,7 @@ class DesktopImeHandler( if (task.configuration.windowConfiguration.bounds == imeTarget.newBounds) { val finalBounds = imeTarget.previousBounds ?: return@forEach logD("Restoring task %d due to IME", taskId) // Restore the previous bounds if they exist wct.setBounds(imeTarget.topTask.token, finalBounds) } Loading @@ -164,6 +166,22 @@ class DesktopImeHandler( shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } /** * If a transition related to a target that we have previously moved up, remove it from the * target list so we do not restore its bounds. */ fun onTransitionReady(transition: IBinder, info: TransitionInfo) { // Do nothing if we get the callback for IME triggered transition if (transition == imeTriggeredTransition || taskToImeTarget.isEmpty()) return // If there is a transition targeting the IME targets remove them from the list info.changes.forEach { change -> if (taskToImeTarget[change.taskInfo?.taskId] != null) { taskToImeTarget.remove(change.taskInfo?.taskId) } } } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +8 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.window.WindowContainerToken; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.wm.shell.desktopmode.DesktopImeHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver; import com.android.wm.shell.shared.desktopmode.DesktopState; Loading Loading @@ -56,6 +57,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs private final Optional<TaskChangeListener> mTaskChangeListener; private final FocusTransitionObserver mFocusTransitionObserver; private final Optional<DesksTransitionObserver> mDesksTransitionObserver; private final Optional<DesktopImeHandler> mDesktopImeHandler; private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo = new HashMap<>(); Loading @@ -71,13 +73,15 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver, Optional<DesksTransitionObserver> desksTransitionObserver, DesktopState desktopState) { DesktopState desktopState, Optional<DesktopImeHandler> desktopImeHandler) { mTransitions = transitions; mDesktopImmersiveController = desktopImmersiveController; mWindowDecorViewModel = windowDecorViewModel; mTaskChangeListener = taskChangeListener; mFocusTransitionObserver = focusTransitionObserver; mDesksTransitionObserver = desksTransitionObserver; mDesktopImeHandler = desktopImeHandler; if (FreeformComponents.requiresFreeformComponents(desktopState)) { shellInit.addInitCallback(this::onInit, this); } Loading Loading @@ -109,6 +113,9 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs // TODO(371503964): Remove this once the unified task repository is ready. mFocusTransitionObserver.updateFocusState(info); // Call after the focus state update to have the correct focused window. mDesktopImeHandler.ifPresent(o -> o.onTransitionReady(transition, info)); final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>(); final ArrayList<WindowContainerToken> taskParents = new ArrayList<>(); for (TransitionInfo.Change change : info.getChanges()) { Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt +73 −0 Original line number Diff line number Diff line Loading @@ -20,10 +20,14 @@ import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration import android.content.Context import android.graphics.Rect import android.os.IBinder import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_TO_BACK import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerTransaction import com.android.window.flags.Flags import com.android.wm.shell.ShellTaskOrganizer Loading @@ -40,6 +44,7 @@ import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Before import org.mockito.ArgumentCaptor import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq Loading Loading @@ -339,6 +344,62 @@ class DesktopImeHandlerTest : ShellTestCase() { .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS, ) fun onImeStartPositioning_newTransitionOnTask_doesNotRestorePreImeBounds() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = true, isFloating = false, t = mock(), ) // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Create a transition that affects the freeform task we modified previously imeHandler.onTransitionReady( transition = Mockito.mock(IBinder::class.java), info = createToBackTransition(freeformTask), ) // This should be no op. imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = false, isFloating = false, t = mock(), ) // Task is not moved back to original position with a new transition. verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Loading Loading @@ -393,6 +454,18 @@ class DesktopImeHandlerTest : ShellTestCase() { } } private fun createToBackTransition(task: RunningTaskInfo?) = TransitionInfo(TRANSIT_TO_BACK, /* flags= */ 0).apply { addChange( Change(mock(), mock()).apply { mode = TRANSIT_TO_BACK parent = null taskInfo = task flags = flags } ) } private companion object { private const val DISPLAY_DIMENSION_SHORT = 1600 private const val DISPLAY_DIMENSION_LONG = 2560 Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java +4 −2 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest; import com.android.window.flags.Flags; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.desktopmode.DesktopImeHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver; import com.android.wm.shell.shared.desktopmode.FakeDesktopState; Loading Loading @@ -76,8 +77,8 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase { @Mock private TaskChangeListener mTaskChangeListener; @Mock private FocusTransitionObserver mFocusTransitionObserver; @Mock private DesksTransitionObserver mDesksTransitionObserver; @Mock private DesktopImeHandler mDesktopImeHandler; private FakeDesktopState mDesktopState; private FreeformTaskTransitionObserver mTransitionObserver; private AutoCloseable mMocksInits = null; Loading @@ -102,7 +103,8 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase { Optional.of(mTaskChangeListener), mFocusTransitionObserver, Optional.of(mDesksTransitionObserver), mDesktopState); mDesktopState, Optional.of(mDesktopImeHandler)); final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver)); Loading