diff options
11 files changed, 435 insertions, 12 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java new file mode 100644 index 000000000000..26aae2d2aa78 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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.shared; + +import com.android.wm.shell.shared.annotations.ExternalThread; + +/** + * Listener to get focus-related transition callbacks. + */ +@ExternalThread +public interface FocusTransitionListener { + /** + * Called when a transition changes the top, focused display. + */ + void onFocusedDisplayChanged(int displayId); +} diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl new file mode 100644 index 000000000000..b91d5b6e2769 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 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.shared; + +/** + * Listener interface that to get focus-related transition callbacks. + */ +oneway interface IFocusTransitionListener { + + /** + * Called when a transition changes the top, focused display. + */ + void onFocusedDisplayChanged(int displayId); +} diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl index 3256abf09116..02615a96a86c 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl @@ -20,6 +20,7 @@ import android.view.SurfaceControl; import android.window.RemoteTransition; import android.window.TransitionFilter; +import com.android.wm.shell.shared.IFocusTransitionListener; import com.android.wm.shell.shared.IHomeTransitionListener; /** @@ -59,4 +60,9 @@ interface IShellTransitions { */ oneway void registerRemoteForTakeover(in TransitionFilter filter, in RemoteTransition remoteTransition) = 6; + + /** + * Set listener that will receive callbacks about transitions involving focus switch. + */ + oneway void setFocusTransitionListener(in IFocusTransitionListener listener) = 7; } diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java index 6d4ab4c1bd09..2db4311fb771 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java @@ -22,6 +22,8 @@ import android.window.TransitionFilter; import com.android.wm.shell.shared.annotations.ExternalThread; +import java.util.concurrent.Executor; + /** * Interface to manage remote transitions. */ @@ -44,4 +46,15 @@ public interface ShellTransitions { * Unregisters a remote transition for all operations. */ default void unregisterRemote(@NonNull RemoteTransition remoteTransition) {} + + /** + * Sets listener that will receive callbacks about transitions involving focus switch. + */ + default void setFocusTransitionListener(@NonNull FocusTransitionListener listener, + Executor executor) {} + + /** + * Unsets listener that will receive callbacks about transitions involving focus switch. + */ + default void unsetFocusTransitionListener(@NonNull FocusTransitionListener listener) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index bec2ea58e106..4227a6e2903f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -123,6 +123,7 @@ import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.taskview.TaskViewFactory; import com.android.wm.shell.taskview.TaskViewFactoryController; import com.android.wm.shell.taskview.TaskViewTransitions; +import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.HomeTransitionObserver; import com.android.wm.shell.transition.MixedTransitionHandler; import com.android.wm.shell.transition.Transitions; @@ -742,14 +743,15 @@ public abstract class WMShellBaseModule { @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - HomeTransitionObserver homeTransitionObserver) { + HomeTransitionObserver homeTransitionObserver, + FocusTransitionObserver focusTransitionObserver) { if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer, pool, displayController, mainExecutor, mainHandler, animExecutor, - rootTaskDisplayAreaOrganizer, homeTransitionObserver); + rootTaskDisplayAreaOrganizer, homeTransitionObserver, focusTransitionObserver); } @WMSingleton @@ -761,6 +763,12 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides + static FocusTransitionObserver provideFocusTransitionObserver() { + return new FocusTransitionObserver(); + } + + @WMSingleton + @Provides static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) { return new TaskViewTransitions(transitions); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java new file mode 100644 index 000000000000..2f5059f3161c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2024 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.transition; + +import static android.view.Display.INVALID_DISPLAY; +import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; + +import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; +import static com.android.wm.shell.transition.Transitions.TransitionObserver; + +import android.annotation.NonNull; +import android.app.ActivityManager.RunningTaskInfo; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.SurfaceControl; +import android.window.TransitionInfo; + +import com.android.wm.shell.shared.FocusTransitionListener; +import com.android.wm.shell.shared.IFocusTransitionListener; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * The {@link TransitionObserver} that observes for transitions involving focus switch. + * It reports transitions to callers outside of the process via {@link IFocusTransitionListener}, + * and callers within the process via {@link FocusTransitionListener}. + */ +public class FocusTransitionObserver implements TransitionObserver { + private static final String TAG = FocusTransitionObserver.class.getSimpleName(); + + private IFocusTransitionListener mRemoteListener; + private final Map<FocusTransitionListener, Executor> mLocalListeners = + new HashMap<>(); + + private int mFocusedDisplayId = INVALID_DISPLAY; + + public FocusTransitionObserver() {} + + @Override + public void onTransitionReady(@NonNull IBinder transition, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + final List<TransitionInfo.Change> changes = info.getChanges(); + for (int i = changes.size() - 1; i >= 0; i--) { + final TransitionInfo.Change change = changes.get(i); + final RunningTaskInfo task = change.getTaskInfo(); + if (task != null && task.isFocused && change.hasFlags(FLAG_MOVED_TO_TOP)) { + if (mFocusedDisplayId != task.displayId) { + mFocusedDisplayId = task.displayId; + notifyFocusedDisplayChanged(); + } + return; + } + } + } + + @Override + public void onTransitionStarting(@NonNull IBinder transition) {} + + @Override + public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {} + + @Override + public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {} + + /** + * Sets the focus transition listener that receives any transitions resulting in focus switch. + * This is for calls from outside the Shell, within the host process. + * + */ + public void setLocalFocusTransitionListener(FocusTransitionListener listener, + Executor executor) { + if (!enableDisplayFocusInShellTransitions()) { + return; + } + mLocalListeners.put(listener, executor); + executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId)); + } + + /** + * Sets the focus transition listener that receives any transitions resulting in focus switch. + * This is for calls from outside the Shell, within the host process. + * + */ + public void unsetLocalFocusTransitionListener(FocusTransitionListener listener) { + if (!enableDisplayFocusInShellTransitions()) { + return; + } + mLocalListeners.remove(listener); + } + + /** + * Sets the focus transition listener that receives any transitions resulting in focus switch. + * This is for calls from outside the host process. + */ + public void setRemoteFocusTransitionListener(Transitions transitions, + IFocusTransitionListener listener) { + if (!enableDisplayFocusInShellTransitions()) { + return; + } + mRemoteListener = listener; + notifyFocusedDisplayChangedToRemote(); + } + + /** + * Notifies the listener that display focus has changed. + */ + public void notifyFocusedDisplayChanged() { + notifyFocusedDisplayChangedToRemote(); + mLocalListeners.forEach((listener, executor) -> + executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId))); + } + + private void notifyFocusedDisplayChangedToRemote() { + if (mRemoteListener != null) { + try { + mRemoteListener.onFocusedDisplayChanged(mFocusedDisplayId); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call notifyFocusedDisplayChangedToRemote", e); + } + } + } +} 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 d03832d3e85e..d280dcd252b4 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 @@ -87,6 +87,8 @@ import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.shared.FocusTransitionListener; +import com.android.wm.shell.shared.IFocusTransitionListener; import com.android.wm.shell.shared.IHomeTransitionListener; import com.android.wm.shell.shared.IShellTransitions; import com.android.wm.shell.shared.ShellTransitions; @@ -103,6 +105,7 @@ import com.android.wm.shell.transition.tracing.TransitionTracer; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.Executor; /** * Plays transition animations. Within this player, each transition has a lifecycle. @@ -224,6 +227,7 @@ public class Transitions implements RemoteCallable<Transitions>, private final ArrayList<TransitionObserver> mObservers = new ArrayList<>(); private HomeTransitionObserver mHomeTransitionObserver; + private FocusTransitionObserver mFocusTransitionObserver; /** List of {@link Runnable} instances to run when the last active transition has finished. */ private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>(); @@ -309,10 +313,12 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, - @NonNull HomeTransitionObserver observer) { + @NonNull HomeTransitionObserver homeTransitionObserver, + @NonNull FocusTransitionObserver focusTransitionObserver) { this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool, displayController, mainExecutor, mainHandler, animExecutor, - new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit), observer); + new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit), + homeTransitionObserver, focusTransitionObserver); } public Transitions(@NonNull Context context, @@ -326,7 +332,8 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer, - @NonNull HomeTransitionObserver observer) { + @NonNull HomeTransitionObserver homeTransitionObserver, + @NonNull FocusTransitionObserver focusTransitionObserver) { mOrganizer = organizer; mContext = context; mMainExecutor = mainExecutor; @@ -345,7 +352,8 @@ public class Transitions implements RemoteCallable<Transitions>, mHandlers.add(mRemoteTransitionHandler); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote"); shellInit.addInitCallback(this::onInit, this); - mHomeTransitionObserver = observer; + mHomeTransitionObserver = homeTransitionObserver; + mFocusTransitionObserver = focusTransitionObserver; if (android.tracing.Flags.perfettoTransitionTracing()) { mTransitionTracer = new PerfettoTransitionTracer(); @@ -384,6 +392,8 @@ public class Transitions implements RemoteCallable<Transitions>, mShellCommandHandler.addCommandCallback("transitions", this, this); mShellCommandHandler.addDumpCallback(this::dump, this); + + registerObserver(mFocusTransitionObserver); } public boolean isRegistered() { @@ -1573,6 +1583,21 @@ public class Transitions implements RemoteCallable<Transitions>, mMainExecutor.execute( () -> mRemoteTransitionHandler.removeFiltered(remoteTransition)); } + + @Override + public void setFocusTransitionListener(FocusTransitionListener listener, + Executor executor) { + mMainExecutor.execute(() -> + mFocusTransitionObserver.setLocalFocusTransitionListener(listener, executor)); + + } + + @Override + public void unsetFocusTransitionListener(FocusTransitionListener listener) { + mMainExecutor.execute(() -> + mFocusTransitionObserver.unsetLocalFocusTransitionListener(listener)); + + } } /** @@ -1634,6 +1659,15 @@ public class Transitions implements RemoteCallable<Transitions>, } @Override + public void setFocusTransitionListener(IFocusTransitionListener listener) { + executeRemoteCallWithTaskPermission(mTransitions, "setFocusTransitionListener", + (transitions) -> { + transitions.mFocusTransitionObserver.setRemoteFocusTransitionListener( + transitions, listener); + }); + } + + @Override public SurfaceControl getHomeTaskOverlayContainer() { SurfaceControl[] result = new SurfaceControl[1]; executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index a6c16c43c8cb..67eda8bfecd1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -74,6 +74,7 @@ import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.DefaultMixedHandler; +import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.HomeTransitionObserver; import com.android.wm.shell.transition.Transitions; @@ -429,7 +430,8 @@ public class StageCoordinatorTests extends ShellTestCase { ShellInit shellInit = new ShellInit(mMainExecutor); final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor, - mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); + mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class), + mock(FocusTransitionObserver.class)); shellInit.init(); return t; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java new file mode 100644 index 000000000000..d37b4cf4b4b3 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2024 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.transition; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager.RunningTaskInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionInfo.TransitionMode; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.window.flags.Flags; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.shared.IFocusTransitionListener; +import com.android.wm.shell.shared.TransactionPool; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for the focus transition observer. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +@RequiresFlagsEnabled(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS) +public class FocusTransitionObserverTest extends ShellTestCase { + + static final int SECONDARY_DISPLAY_ID = 1; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private IFocusTransitionListener mListener; + private Transitions mTransition; + private FocusTransitionObserver mFocusTransitionObserver; + + @Before + public void setUp() { + mListener = mock(IFocusTransitionListener.class); + when(mListener.asBinder()).thenReturn(mock(IBinder.class)); + + mFocusTransitionObserver = new FocusTransitionObserver(); + mTransition = + new Transitions(InstrumentationRegistry.getInstrumentation().getTargetContext(), + mock(ShellInit.class), mock(ShellController.class), + mock(ShellTaskOrganizer.class), mock(TransactionPool.class), + mock(DisplayController.class), new TestShellExecutor(), + new Handler(Looper.getMainLooper()), new TestShellExecutor(), + mock(HomeTransitionObserver.class), + mFocusTransitionObserver); + mFocusTransitionObserver.setRemoteFocusTransitionListener(mTransition, mListener); + } + + @Test + public void testTransitionWithMovedToFrontFlagChangesDisplayFocus() throws RemoteException { + final IBinder binder = mock(IBinder.class); + final SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class); + + // Open a task on the default display, which doesn't change display focus because the + // default display already has it. + TransitionInfo info = mock(TransitionInfo.class); + final List<TransitionInfo.Change> changes = new ArrayList<>(); + setupChange(changes, 123 /* taskId */, TRANSIT_OPEN, DEFAULT_DISPLAY, + true /* focused */); + when(info.getChanges()).thenReturn(changes); + mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx); + verify(mListener, never()).onFocusedDisplayChanged(SECONDARY_DISPLAY_ID); + clearInvocations(mListener); + + // Open a new task on the secondary display and verify display focus changes to the display. + changes.clear(); + setupChange(changes, 456 /* taskId */, TRANSIT_OPEN, SECONDARY_DISPLAY_ID, + true /* focused */); + when(info.getChanges()).thenReturn(changes); + mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx); + verify(mListener, times(1)).onFocusedDisplayChanged(SECONDARY_DISPLAY_ID); + clearInvocations(mListener); + + // Open the first task to front and verify display focus goes back to the default display. + changes.clear(); + setupChange(changes, 123 /* taskId */, TRANSIT_TO_FRONT, DEFAULT_DISPLAY, + true /* focused */); + when(info.getChanges()).thenReturn(changes); + mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx); + verify(mListener, times(1)).onFocusedDisplayChanged(DEFAULT_DISPLAY); + clearInvocations(mListener); + + // Open another task on the default display and verify no display focus switch as it's + // already on the default display. + changes.clear(); + setupChange(changes, 789 /* taskId */, TRANSIT_OPEN, DEFAULT_DISPLAY, + true /* focused */); + when(info.getChanges()).thenReturn(changes); + mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx); + verify(mListener, never()).onFocusedDisplayChanged(DEFAULT_DISPLAY); + } + + private void setupChange(List<TransitionInfo.Change> changes, int taskId, + @TransitionMode int mode, int displayId, boolean focused) { + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + RunningTaskInfo taskInfo = mock(RunningTaskInfo.class); + taskInfo.taskId = taskId; + taskInfo.isFocused = focused; + when(change.hasFlags(FLAG_MOVED_TO_TOP)).thenReturn(focused); + taskInfo.displayId = displayId; + when(change.getTaskInfo()).thenReturn(taskInfo); + when(change.getMode()).thenReturn(mode); + changes.add(change); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index 8f49de0a98fb..8dfdfb4dcbcf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -100,7 +100,8 @@ public class HomeTransitionObserverTest extends ShellTestCase { mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor); mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class), mOrganizer, mTransactionPool, mDisplayController, mMainExecutor, - mMainHandler, mAnimExecutor, mHomeTransitionObserver); + mMainHandler, mAnimExecutor, mHomeTransitionObserver, + mock(FocusTransitionObserver.class)); mHomeTransitionObserver.setHomeTransitionListener(mTransition, mListener); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index aea14b900647..6cde0569796d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -158,7 +158,8 @@ public class ShellTransitionTests extends ShellTestCase { ShellInit shellInit = mock(ShellInit.class); final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, - mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); + mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class), + mock(FocusTransitionObserver.class)); // One from Transitions, one from RootTaskDisplayAreaOrganizer verify(shellInit).addInitCallback(any(), eq(t)); verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class)); @@ -170,7 +171,8 @@ public class ShellTransitionTests extends ShellTestCase { ShellController shellController = mock(ShellController.class); final Transitions t = new Transitions(mContext, shellInit, shellController, mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, - mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); + mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class), + mock(FocusTransitionObserver.class)); shellInit.init(); verify(shellController, times(1)).addExternalInterface( eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any()); @@ -1238,7 +1240,8 @@ public class ShellTransitionTests extends ShellTestCase { final Transitions transitions = new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, - mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); + mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class), + mock(FocusTransitionObserver.class)); final RecentsTransitionHandler recentsHandler = new RecentsTransitionHandler(shellInit, mock(ShellTaskOrganizer.class), transitions, mock(RecentTasksController.class), mock(HomeTransitionObserver.class)); @@ -1780,7 +1783,8 @@ public class ShellTransitionTests extends ShellTestCase { ShellInit shellInit = new ShellInit(mMainExecutor); final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, - mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); + mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class), + mock(FocusTransitionObserver.class)); shellInit.init(); return t; } |