diff options
32 files changed, 1332 insertions, 325 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 51ddb17daa00..2d20feeb0832 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -346,15 +346,9 @@ public class ShellTaskOrganizer extends TaskOrganizer { return mTaskListeners.get(taskListenerType); } - @WindowingMode - public static int getWindowingMode(RunningTaskInfo taskInfo) { - return taskInfo.configuration.windowConfiguration.getWindowingMode(); - } - @VisibleForTesting static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) { - final int windowingMode = getWindowingMode(runningTaskInfo); - switch (windowingMode) { + switch (runningTaskInfo.getWindowingMode()) { case WINDOWING_MODE_FULLSCREEN: return runningTaskInfo.letterboxActivityBounds != null ? TASK_LISTENER_TYPE_LETTERBOX diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java new file mode 100644 index 000000000000..87ddb181dcce --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2020 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.common; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.INPUT_CONSUMER_PIP; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; + +import android.os.Binder; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.BatchedInputEventReceiver; +import android.view.Choreographer; +import android.view.IWindowManager; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.WindowManagerGlobal; + +import java.io.PrintWriter; + +/** + * Manages the input consumer that allows the Shell to directly receive input. + */ +public class PipInputConsumer { + + private static final String TAG = PipInputConsumer.class.getSimpleName(); + + /** + * Listener interface for callers to subscribe to input events. + */ + public interface InputListener { + /** Handles any input event. */ + boolean onInputEvent(InputEvent ev); + } + + /** + * Listener interface for callers to learn when this class is registered or unregistered with + * window manager + */ + public interface RegistrationListener { + void onRegistrationChanged(boolean isRegistered); + } + + /** + * Input handler used for the input consumer. Input events are batched and consumed with the + * SurfaceFlinger vsync. + */ + private final class InputEventReceiver extends BatchedInputEventReceiver { + + InputEventReceiver(InputChannel inputChannel, Looper looper, + Choreographer choreographer) { + super(inputChannel, looper, choreographer); + } + + @Override + public void onInputEvent(InputEvent event) { + boolean handled = true; + try { + if (mListener != null) { + handled = mListener.onInputEvent(event); + } + } finally { + finishInputEvent(event, handled); + } + } + } + + private final IWindowManager mWindowManager; + private final IBinder mToken; + private final String mName; + + private InputEventReceiver mInputEventReceiver; + private InputListener mListener; + private RegistrationListener mRegistrationListener; + + /** + * @param name the name corresponding to the input consumer that is defined in the system. + */ + public PipInputConsumer(IWindowManager windowManager, String name) { + mWindowManager = windowManager; + mToken = new Binder(); + mName = name; + } + + /** + * Sets the input listener. + */ + public void setInputListener(InputListener listener) { + mListener = listener; + } + + /** + * Sets the registration listener. + */ + public void setRegistrationListener(RegistrationListener listener) { + mRegistrationListener = listener; + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null); + } + } + + /** + * Check if the InputConsumer is currently registered with WindowManager + * + * @return {@code true} if registered, {@code false} if not. + */ + public boolean isRegistered() { + return mInputEventReceiver != null; + } + + /** + * Registers the input consumer. + */ + public void registerInputConsumer() { + registerInputConsumer(false); + } + + /** + * Registers the input consumer. + * @param withSfVsync the flag set using sf vsync signal or no + */ + public void registerInputConsumer(boolean withSfVsync) { + if (mInputEventReceiver != null) { + return; + } + final InputChannel inputChannel = new InputChannel(); + try { + // TODO(b/113087003): Support Picture-in-picture in multi-display. + mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); + mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel); + } catch (RemoteException e) { + Log.e(TAG, "Failed to create input consumer", e); + } + mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(), + withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance()); + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(true /* isRegistered */); + } + } + + /** + * Unregisters the input consumer. + */ + public void unregisterInputConsumer() { + if (mInputEventReceiver == null) { + return; + } + try { + // TODO(b/113087003): Support Picture-in-picture in multi-display. + mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); + } catch (RemoteException e) { + Log.e(TAG, "Failed to destroy input consumer", e); + } + mInputEventReceiver.dispose(); + mInputEventReceiver = null; + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(false /* isRegistered */); + } + } + + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null)); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java new file mode 100644 index 000000000000..0f6dd93f9c16 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 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.common; + +import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ITaskStackListener; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.os.IBinder; + +import androidx.annotation.BinderThread; +import androidx.annotation.MainThread; + +/** + * An interface to track task stack changes. Classes should implement this instead of + * {@link ITaskStackListener} to reduce IPC calls from system services. + */ +public interface TaskStackListenerCallback { + + @MainThread + default void onRecentTaskListUpdated() { } + + @MainThread + default void onRecentTaskListFrozenChanged(boolean frozen) { } + + @BinderThread + default void onTaskStackChangedBackground() { } + + @MainThread + default void onTaskStackChanged() { } + + @MainThread + default void onTaskProfileLocked(int taskId, int userId) { } + + @MainThread + default void onTaskDisplayChanged(int taskId, int newDisplayId) { } + + @MainThread + default void onTaskCreated(int taskId, ComponentName componentName) { } + + @MainThread + default void onTaskRemoved(int taskId) { } + + @MainThread + default void onTaskMovedToFront(int taskId) { } + + @MainThread + default void onTaskMovedToFront(RunningTaskInfo taskInfo) { + onTaskMovedToFront(taskInfo.taskId); + } + + @MainThread + default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { } + + @MainThread + default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { } + + @MainThread + default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } + + @MainThread + default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask, boolean wasVisible) { } + + @MainThread + default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } + + @MainThread + default void onActivityUnpinned() { } + + @MainThread + default void onActivityForcedResizable(String packageName, int taskId, int reason) { } + + @MainThread + default void onActivityDismissingDockedStack() { } + + @MainThread + default void onActivityLaunchOnSecondaryDisplayFailed() { } + + @MainThread + default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) { + onActivityLaunchOnSecondaryDisplayFailed(); + } + + @MainThread + default void onActivityLaunchOnSecondaryDisplayRerouted() { } + + @MainThread + default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) { + onActivityLaunchOnSecondaryDisplayRerouted(); + } + + @MainThread + default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } + + @MainThread + default void onActivityRotation(int displayId) { } + + @MainThread + default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java new file mode 100644 index 000000000000..7efacc7b2c1f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2020 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.common; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; +import android.content.ComponentName; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Trace; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.SomeArgs; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of a {@link android.app.TaskStackListener}. + */ +public class TaskStackListenerImpl extends TaskStackListener implements Handler.Callback { + private static final String TAG = TaskStackListenerImpl.class.getSimpleName(); + + private static final int ON_TASK_STACK_CHANGED = 1; + private static final int ON_TASK_SNAPSHOT_CHANGED = 2; + private static final int ON_ACTIVITY_PINNED = 3; + private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4; + private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5; + private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6; + private static final int ON_TASK_PROFILE_LOCKED = 7; + private static final int ON_ACTIVITY_UNPINNED = 8; + private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 9; + private static final int ON_TASK_CREATED = 10; + private static final int ON_TASK_REMOVED = 11; + private static final int ON_TASK_MOVED_TO_FRONT = 12; + private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 13; + private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 14; + private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 15; + private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 16; + private static final int ON_TASK_DISPLAY_CHANGED = 17; + private static final int ON_TASK_LIST_UPDATED = 18; + private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 19; + private static final int ON_TASK_DESCRIPTION_CHANGED = 20; + private static final int ON_ACTIVITY_ROTATION = 21; + + /** + * List of {@link TaskStackListenerCallback} registered from {@link #addListener}. + */ + private final List<TaskStackListenerCallback> mTaskStackListeners = new ArrayList<>(); + private final List<TaskStackListenerCallback> mTmpListeners = new ArrayList<>(); + + private final IActivityTaskManager mActivityTaskManager; + // NOTE: In this case we do want to use a handler since we rely on the message system to + // efficiently dedupe sequential calls + private Handler mHandler; + + public TaskStackListenerImpl(Handler handler) { + mActivityTaskManager = ActivityTaskManager.getService(); + mHandler = new Handler(handler.getLooper(), this); + } + + @VisibleForTesting + TaskStackListenerImpl(IActivityTaskManager activityTaskManager) { + mActivityTaskManager = activityTaskManager; + } + + @VisibleForTesting + void setHandler(Handler handler) { + mHandler = handler; + } + + public void addListener(TaskStackListenerCallback listener) { + final boolean wasEmpty; + synchronized (mTaskStackListeners) { + wasEmpty = mTaskStackListeners.isEmpty(); + mTaskStackListeners.add(listener); + } + if (wasEmpty) { + // Register mTaskStackListener to IActivityManager only once if needed. + try { + mActivityTaskManager.registerTaskStackListener(this); + } catch (Exception e) { + Log.w(TAG, "Failed to call registerTaskStackListener", e); + } + } + } + + public void removeListener(TaskStackListenerCallback listener) { + final boolean wasEmpty; + final boolean isEmpty; + synchronized (mTaskStackListeners) { + wasEmpty = mTaskStackListeners.isEmpty(); + mTaskStackListeners.remove(listener); + isEmpty = mTaskStackListeners.isEmpty(); + } + if (!wasEmpty && isEmpty) { + // Unregister mTaskStackListener once we have no more listeners + try { + mActivityTaskManager.unregisterTaskStackListener(this); + } catch (Exception e) { + Log.w(TAG, "Failed to call unregisterTaskStackListener", e); + } + } + } + + @Override + public void onRecentTaskListUpdated() { + mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget(); + } + + @Override + public void onRecentTaskListFrozenChanged(boolean frozen) { + mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */) + .sendToTarget(); + } + + @Override + public void onTaskStackChanged() { + // Call the task changed callback for the non-ui thread listeners first. Copy to a set + // of temp listeners so that we don't lock on mTaskStackListeners while calling all the + // callbacks. This call is always on the same binder thread, so we can just synchronize + // on the copying of the listener list. + synchronized (mTaskStackListeners) { + mTmpListeners.addAll(mTaskStackListeners); + } + for (int i = mTmpListeners.size() - 1; i >= 0; i--) { + mTmpListeners.get(i).onTaskStackChangedBackground(); + } + mTmpListeners.clear(); + + mHandler.removeMessages(ON_TASK_STACK_CHANGED); + mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED); + } + + @Override + public void onTaskProfileLocked(int taskId, int userId) { + mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget(); + } + + @Override + public void onTaskDisplayChanged(int taskId, int newDisplayId) { + mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget(); + } + + @Override + public void onTaskCreated(int taskId, ComponentName componentName) { + mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget(); + } + + @Override + public void onTaskRemoved(int taskId) { + mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget(); + } + + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget(); + } + + @Override + public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) { + mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget(); + } + + @Override + public void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { + mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget(); + } + + @Override + public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { + mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget(); + } + + @Override + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = packageName; + args.argi1 = userId; + args.argi2 = taskId; + args.argi3 = stackId; + mHandler.removeMessages(ON_ACTIVITY_PINNED); + mHandler.obtainMessage(ON_ACTIVITY_PINNED, args).sendToTarget(); + } + + @Override + public void onActivityUnpinned() { + mHandler.removeMessages(ON_ACTIVITY_UNPINNED); + mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED); + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask, boolean wasVisible) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = task; + args.argi1 = homeTaskVisible ? 1 : 0; + args.argi2 = clearedTask ? 1 : 0; + args.argi3 = wasVisible ? 1 : 0; + mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT); + mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget(); + } + + @Override + public void onActivityForcedResizable(String packageName, int taskId, int reason) { + mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName) + .sendToTarget(); + } + + @Override + public void onActivityDismissingDockedStack() { + mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayFailed( + ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId) { + mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED, + requestedDisplayId, + 0 /* unused */, + taskInfo).sendToTarget(); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayRerouted( + ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId) { + mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED, + requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget(); + } + + @Override + public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { + mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId, + requestedOrientation).sendToTarget(); + } + + @Override + public void onActivityRotation(int displayId) { + mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */) + .sendToTarget(); + } + + @Override + public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { + mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, + 0 /* unused */, + activityToken).sendToTarget(); + } + + @Override + public boolean handleMessage(Message msg) { + synchronized (mTaskStackListeners) { + switch (msg.what) { + case ON_TASK_STACK_CHANGED: { + Trace.beginSection("onTaskStackChanged"); + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskStackChanged(); + } + Trace.endSection(); + break; + } + case ON_TASK_SNAPSHOT_CHANGED: { + Trace.beginSection("onTaskSnapshotChanged"); + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1, + (ActivityManager.TaskSnapshot) msg.obj); + } + Trace.endSection(); + break; + } + case ON_ACTIVITY_PINNED: { + final SomeArgs args = (SomeArgs) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityPinned((String) args.arg1, args.argi1, + args.argi2, args.argi3); + } + break; + } + case ON_ACTIVITY_UNPINNED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityUnpinned(); + } + break; + } + case ON_ACTIVITY_RESTART_ATTEMPT: { + final SomeArgs args = (SomeArgs) msg.obj; + final ActivityManager.RunningTaskInfo + task = (ActivityManager.RunningTaskInfo) args.arg1; + final boolean homeTaskVisible = args.argi1 != 0; + final boolean clearedTask = args.argi2 != 0; + final boolean wasVisible = args.argi3 != 0; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityRestartAttempt(task, + homeTaskVisible, clearedTask, wasVisible); + } + break; + } + case ON_ACTIVITY_FORCED_RESIZABLE: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityForcedResizable( + (String) msg.obj, msg.arg1, msg.arg2); + } + break; + } + case ON_ACTIVITY_DISMISSING_DOCKED_STACK: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityDismissingDockedStack(); + } + break; + } + case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: { + final ActivityManager.RunningTaskInfo + info = (ActivityManager.RunningTaskInfo) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i) + .onActivityLaunchOnSecondaryDisplayFailed(info); + } + break; + } + case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED: { + final ActivityManager.RunningTaskInfo + info = (ActivityManager.RunningTaskInfo) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i) + .onActivityLaunchOnSecondaryDisplayRerouted(info); + } + break; + } + case ON_TASK_PROFILE_LOCKED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2); + } + break; + } + case ON_TASK_CREATED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskCreated(msg.arg1, + (ComponentName) msg.obj); + } + break; + } + case ON_TASK_REMOVED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskRemoved(msg.arg1); + } + break; + } + case ON_TASK_MOVED_TO_FRONT: { + final ActivityManager.RunningTaskInfo + info = (ActivityManager.RunningTaskInfo) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskMovedToFront(info); + } + break; + } + case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i) + .onActivityRequestedOrientationChanged(msg.arg1, msg.arg2); + } + break; + } + case ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onSizeCompatModeActivityChanged( + msg.arg1, (IBinder) msg.obj); + } + break; + } + case ON_BACK_PRESSED_ON_TASK_ROOT: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onBackPressedOnTaskRoot( + (ActivityManager.RunningTaskInfo) msg.obj); + } + break; + } + case ON_TASK_DISPLAY_CHANGED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2); + } + break; + } + case ON_TASK_LIST_UPDATED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onRecentTaskListUpdated(); + } + break; + } + case ON_TASK_LIST_FROZEN_UNFROZEN: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onRecentTaskListFrozenChanged( + msg.arg1 != 0); + } + break; + } + case ON_TASK_DESCRIPTION_CHANGED: { + final ActivityManager.RunningTaskInfo + info = (ActivityManager.RunningTaskInfo) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskDescriptionChanged(info); + } + break; + } + case ON_ACTIVITY_ROTATION: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityRotation(msg.arg1); + } + break; + } + } + } + if (msg.obj instanceof SomeArgs) { + ((SomeArgs) msg.obj).recycle(); + } + return true; + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 25890bc31b3c..8a547b4477fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS; @@ -67,6 +68,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * The policy for handling drag and drop operations to shell. @@ -133,20 +135,20 @@ public class DragAndDropPolicy { // TODO(b/169894807): For now, only allow splitting to the right/bottom until we // have split pairs mTargets.add(new Target(TYPE_FULLSCREEN, - new Rect(0, 0, w, h / 2), + new Rect(l, t, l + iw, t + ih / 2), new Rect(l, t, l + iw, t + ih), new Rect(0, 0, w, h))); mTargets.add(new Target(TYPE_SPLIT_BOTTOM, - new Rect(0, h / 2, w, h), + new Rect(l, t + ih / 2, l + iw, t + ih), new Rect(l, t + ih / 2, l + iw, t + ih), new Rect(0, h / 2, w, h))); } else { mTargets.add(new Target(TYPE_FULLSCREEN, - new Rect(0, 0, w / 2, h), + new Rect(l, t, l + iw / 2, t + ih), new Rect(l, t, l + iw, t + ih), new Rect(0, 0, w, h))); mTargets.add(new Target(TYPE_SPLIT_RIGHT, - new Rect(w / 2, 0, w, h), + new Rect(l + iw / 2, t, l + iw, t + ih), new Rect(l + iw / 2, t, l + iw, t + ih), new Rect(w / 2, 0, w, h))); } @@ -162,12 +164,12 @@ public class DragAndDropPolicy { secondarySplitBounds.intersect(new Rect(l, t, l + iw, t + ih)); if (isVerticalSplit) { mTargets.add(new Target(TYPE_FULLSCREEN, - new Rect(0, 0, w, secondarySplitRawBounds.top), + new Rect(l, t, l + iw, secondarySplitRawBounds.top), new Rect(l, t, l + iw, t + ih), new Rect(0, 0, w, secondarySplitRawBounds.top))); } else { mTargets.add(new Target(TYPE_FULLSCREEN, - new Rect(0, 0, secondarySplitRawBounds.left, h), + new Rect(l, t, secondarySplitRawBounds.left, t + ih), new Rect(l, t, l + iw, t + ih), new Rect(0, 0, w, h))); } @@ -178,7 +180,7 @@ public class DragAndDropPolicy { } else { // Otherwise only show the fullscreen target mTargets.add(new Target(TYPE_FULLSCREEN, - new Rect(0, 0, w, h), + new Rect(l, t, l + iw, t + ih), new Rect(l, t, l + iw, t + ih), new Rect(0, 0, w, h))); } @@ -210,6 +212,7 @@ public class DragAndDropPolicy { final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); final Intent dragData = mSession.dragData; + boolean deferAppLaunchUntilSplit = false; if (target.type == TYPE_FULLSCREEN) { if (mSplitScreen != null && mSplitScreen.isDividerVisible()) { // If in split, remove split and launch fullscreen @@ -226,20 +229,40 @@ public class DragAndDropPolicy { // Not in split, enter split now mStarter.enterSplitScreen(mSession.runningTaskId, target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP); + deferAppLaunchUntilSplit = true; } } - Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS) - ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) - : null; - if (isTask) { - mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts); - } else if (isShortcut) { - mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME), - dragData.getStringExtra(EXTRA_SHORTCUT_ID), - opts, dragData.getParcelableExtra(EXTRA_USER)); + final Runnable startAppRunnable = () -> { + Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS) + ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) + : null; + if (isTask) { + mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts); + } else if (isShortcut) { + mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME), + dragData.getStringExtra(EXTRA_SHORTCUT_ID), + opts, dragData.getParcelableExtra(EXTRA_USER)); + } else { + mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts); + } + }; + if (deferAppLaunchUntilSplit) { + // TODO(b/169894807): The enterSplitScreen() call above will trigger the current task + // into split, and we should wait for home and other tasks to be moved to + // split-secondary before trying to launch the new secondary task. This can be removed + // once we have app-pairs. + mSplitScreen.registerInSplitScreenListener(new Consumer<Boolean>() { + @Override + public void accept(Boolean inSplit) { + if (inSplit) { + startAppRunnable.run(); + mSplitScreen.unregisterInSplitScreenListener(this); + } + } + }); } else { - mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts); + startAppRunnable.run(); } } @@ -274,7 +297,7 @@ public class DragAndDropPolicy { * Updates the session data based on the current state of the system. */ void update() { - final ClipDescription description = mInitialDragData.getDescription(); + try { List<ActivityManager.RunningTaskInfo> tasks = mIActivityTaskManager.getFilteredTasks(1, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index fa857cdd174c..5b7531c09a7b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -135,17 +135,25 @@ public class DragLayout extends View { // visibility of the current region DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation( (int) event.getX(), (int) event.getY()); - if (target != null && mCurrentTarget != target) { + if (mCurrentTarget != target) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target); - Interpolator boundsInterpolator = FAST_OUT_SLOW_IN; - if (mCurrentTarget == null) { + if (target == null) { + // Animating to no target + mDropOutline.startVisibilityAnimation(false, LINEAR); + Rect finalBounds = new Rect(mCurrentTarget.drawRegion); + finalBounds.inset(mDisplayMargin, mDisplayMargin); + mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN); + } else if (mCurrentTarget == null) { + // Animating to first target mDropOutline.startVisibilityAnimation(true, LINEAR); Rect initialBounds = new Rect(target.drawRegion); initialBounds.inset(mDisplayMargin, mDisplayMargin); mDropOutline.setRegionBounds(initialBounds); - boundsInterpolator = LINEAR_OUT_SLOW_IN; + mDropOutline.startBoundsAnimation(target.drawRegion, LINEAR_OUT_SLOW_IN); + } else { + // Bounds change + mDropOutline.startBoundsAnimation(target.drawRegion, FAST_OUT_SLOW_IN); } - mDropOutline.startBoundsAnimation(target.drawRegion, boundsInterpolator); mCurrentTarget = target; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 7d039d4d92f6..f84936e5f386 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -19,6 +19,7 @@ package com.android.wm.shell.onehanded; import static android.os.UserHandle.USER_CURRENT; import static android.view.Display.DEFAULT_DISPLAY; +import android.content.ComponentName; import android.content.Context; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; @@ -38,6 +39,8 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import java.io.PrintWriter; @@ -164,7 +167,8 @@ public class OneHandedController implements OneHanded { */ @Nullable public static OneHandedController create( - Context context, DisplayController displayController) { + Context context, DisplayController displayController, + TaskStackListenerImpl taskStackListener) { if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { Slog.w(TAG, "Device doesn't support OneHanded feature"); return null; @@ -181,7 +185,7 @@ public class OneHandedController implements OneHanded { IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, displayController, organizer, touchHandler, - tutorialHandler, gestureHandler, overlayManager); + tutorialHandler, gestureHandler, overlayManager, taskStackListener); } @VisibleForTesting @@ -191,7 +195,8 @@ public class OneHandedController implements OneHanded { OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, OneHandedGestureHandler gestureHandler, - IOverlayManager overlayManager) { + IOverlayManager overlayManager, + TaskStackListenerImpl taskStackListener) { mContext = context; mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; @@ -216,6 +221,19 @@ public class OneHandedController implements OneHanded { setupTimeoutListener(); setupGesturalOverlay(); updateSettings(); + + taskStackListener.addListener( + new TaskStackListenerCallback() { + @Override + public void onTaskCreated(int taskId, ComponentName componentName) { + stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + + @Override + public void onTaskMovedToFront(int taskId) { + stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 8d5da1a5ffcb..46270cf24f48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -81,30 +81,6 @@ public interface Pip { } /** - * Called whenever an Activity is moved to the pinned stack from another stack. - */ - default void onActivityPinned(String packageName) { - } - - /** - * Called whenever an Activity is moved from the pinned stack to another stack - */ - default void onActivityUnpinned(ComponentName topActivity) { - } - - /** - * Called whenever IActivityManager.startActivity is called on an activity that is already - * running, but the task is either brought to the front or a new Intent is delivered to it. - * - * @param task information about the task the activity was relaunched into - * @param clearedTask whether or not the launch activity also cleared the task as a part of - * starting - */ - default void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean clearedTask) { - } - - /** * Called when display size or font size of settings changed */ default void onDensityOrFontScaleChanged() { @@ -132,12 +108,6 @@ public interface Pip { } /** - * Called when task stack changed. - */ - default void onTaskStackChanged() { - } - - /** * Resize the Pip to the appropriate size for the input state. * * @param state In Pip state also used to determine the new size for the Pip. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index c4da7974dadb..c7926455e0de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -16,12 +16,15 @@ package com.android.wm.shell.pip.phone; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; import android.app.RemoteAction; import android.content.ComponentName; @@ -32,9 +35,12 @@ import android.graphics.Rect; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.view.DisplayInfo; import android.view.IPinnedStackController; +import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; @@ -44,13 +50,17 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.PipInputConsumer; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipUtils; import java.io.PrintWriter; import java.util.function.Consumer; @@ -63,22 +73,22 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private Context mContext; private ShellExecutor mMainExecutor; + private DisplayController mDisplayController; + private PipInputConsumer mPipInputConsumer; + private WindowManagerShellWrapper mWindowManagerShellWrapper; + private PipAppOpsListener mAppOpsListener; + private PipMediaController mMediaController; + private PipBoundsHandler mPipBoundsHandler; + private PipBoundsState mPipBoundsState; + private PipTouchHandler mTouchHandler; private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); private final Rect mTmpNormalBounds = new Rect(); protected final Rect mReentryBounds = new Rect(); - private DisplayController mDisplayController; - private PipAppOpsListener mAppOpsListener; - private PipBoundsHandler mPipBoundsHandler; - private @NonNull PipBoundsState mPipBoundsState; - private PipMediaController mMediaController; - private PipTouchHandler mTouchHandler; - private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; - private WindowManagerShellWrapper mWindowManagerShellWrapper; - private boolean mIsInFixedRotation; + private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; protected PipMenuActivityController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; @@ -221,6 +231,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor ) { // Ensure that we are the primary user's SystemUI. @@ -254,7 +265,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac }); mMediaController = pipMediaController; mMenuController = pipMenuActivityController; + mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), + INPUT_CONSUMER_PIP); mTouchHandler = pipTouchHandler; + if (mTouchHandler != null) { + // Register the listener for input consumer touch events. Only for Phone + mPipInputConsumer.setInputListener(mTouchHandler::handleTouchEvent); + mPipInputConsumer.setRegistrationListener(mTouchHandler::onRegistrationChanged); + } mAppOpsListener = pipAppOpsListener; displayController.addDisplayChangingController(mRotationController); displayController.addDisplayWindowListener(mFixedRotationListener); @@ -270,43 +288,66 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } - } - @Override - public void onDensityOrFontScaleChanged() { - mMainExecutor.execute(() -> { - mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); - }); - } + try { + ActivityTaskManager.RootTaskInfo taskInfo = ActivityTaskManager.getService() + .getRootTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); + if (taskInfo != null) { + // If SystemUI restart, and it already existed a pinned stack, + // register the pip input consumer to ensure touch can send to it. + mPipInputConsumer.registerInputConsumer(true /* withSfVsync */); + } + } catch (RemoteException | UnsupportedOperationException e) { + Log.e(TAG, "Failed to register pinned stack listener", e); + e.printStackTrace(); + } - @Override - public void onActivityPinned(String packageName) { - mMainExecutor.execute(() -> { - mTouchHandler.onActivityPinned(); - mMediaController.onActivityPinned(); - mAppOpsListener.onActivityPinned(packageName); - }); + // Handle for system task stack changes. + taskStackListener.addListener( + new TaskStackListenerCallback() { + @Override + public void onActivityPinned(String packageName, int userId, int taskId, + int stackId) { + mMainExecutor.execute(() -> { + mTouchHandler.onActivityPinned(); + mMediaController.onActivityPinned(); + mAppOpsListener.onActivityPinned(packageName); + }); + mPipInputConsumer.registerInputConsumer(true /* withSfVsync */); + } + + @Override + public void onActivityUnpinned() { + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPipActivity(mContext); + final ComponentName topActivity = topPipActivityInfo.first; + mMainExecutor.execute(() -> { + mTouchHandler.onActivityUnpinned(topActivity); + mAppOpsListener.onActivityUnpinned(); + }); + mPipInputConsumer.unregisterInputConsumer(); + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (task.getWindowingMode() != WINDOWING_MODE_PINNED) { + return; + } + mTouchHandler.getMotionHelper().expandLeavePip( + clearedTask /* skipAnimation */); + } + }); } @Override - public void onActivityUnpinned(ComponentName topActivity) { + public void onDensityOrFontScaleChanged() { mMainExecutor.execute(() -> { - mTouchHandler.onActivityUnpinned(topActivity); - mAppOpsListener.onActivityUnpinned(); + mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); }); } @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean clearedTask) { - if (task.configuration.windowConfiguration.getWindowingMode() - != WINDOWING_MODE_PINNED) { - return; - } - mTouchHandler.getMotionHelper().expandLeavePip(clearedTask /* skipAnimation */); - } - - @Override public void onOverlayChanged() { mMainExecutor.execute(() -> { mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); @@ -474,6 +515,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsHandler.dump(pw, innerPrefix); mPipTaskOrganizer.dump(pw, innerPrefix); mPipBoundsState.dump(pw, innerPrefix); + mPipInputConsumer.dump(pw, innerPrefix); } /** @@ -483,17 +525,16 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public static PipController create(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, - PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper, - ShellExecutor mainExecutor) { + PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { Slog.w(TAG, "Device doesn't support Pip feature"); return null; } return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler, - pipBoundsState, pipMediaController, pipMenuActivityController, - pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, mainExecutor); + pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, + pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 3468b888c06a..402d79c6fbd1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -44,16 +44,20 @@ import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.view.DisplayInfo; import com.android.wm.shell.R; import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipUtils; import java.util.ArrayList; import java.util.List; @@ -225,6 +229,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipTaskOrganizer pipTaskOrganizer, PipMediaController pipMediaController, PipNotification pipNotification, + TaskStackListenerImpl taskStackListener, WindowManagerShellWrapper windowManagerShellWrapper) { mContext = context; mPipBoundsState = pipBoundsState; @@ -263,6 +268,27 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac Log.e(TAG, "Failed to register pinned stack listener", e); } + // Handle for system task stack changes. + taskStackListener.addListener( + new TaskStackListenerCallback() { + @Override + public void onTaskStackChanged() { + PipController.this.onTaskStackChanged(); + } + + @Override + public void onActivityPinned(String packageName, int userId, int taskId, + int stackId) { + PipController.this.onActivityPinned(packageName); + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + PipController.this.onActivityRestartAttempt(task, clearedTask); + } + }); + // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView PipMenuActivity.setPipController(this); } @@ -351,8 +377,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac resizePinnedStack(STATE_NO_PIP); } - @Override - public void onActivityPinned(String packageName) { + private void onActivityPinned(String packageName) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); RootTaskInfo taskInfo = getPinnedTaskInfo(); @@ -371,11 +396,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + private void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean clearedTask) { - if (task.configuration.windowConfiguration.getWindowingMode() - != WINDOWING_MODE_PINNED) { + if (task.getWindowingMode() != WINDOWING_MODE_PINNED) { return; } if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); @@ -384,8 +407,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac movePipToFullscreen(); } - @Override - public void onTaskStackChanged() { + private void onTaskStackChanged() { if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); if (getState() != STATE_NO_PIP) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index d1176733deb9..e55f065c1bb2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -27,9 +27,6 @@ import java.util.function.Consumer; * Interface to engage split screen feature. */ public interface SplitScreen { - /** Returns {@code true} if split screen is supported on the device. */ - boolean isSplitScreenSupported(); - /** Called when keyguard showing state changed. */ void onKeyguardVisibilityChanged(boolean isShowing); @@ -48,15 +45,6 @@ public interface SplitScreen { /** Switch to minimized state if appropriate. */ void setMinimized(boolean minimized); - /** Called when there's an activity forced resizable. */ - void onActivityForcedResizable(String packageName, int taskId, int reason); - - /** Called when there's an activity dismissing split screen. */ - void onActivityDismissingSplitScreen(); - - /** Called when there's an activity launch on secondary display failed. */ - void onActivityLaunchOnSecondaryDisplayFailed(); - /** Called when there's a task undocking. */ void onUndockingTask(); @@ -69,6 +57,9 @@ public interface SplitScreen { /** Registers listener that gets called whenever the existence of the divider changes. */ void registerInSplitScreenListener(Consumer<Boolean> listener); + /** Unregisters listener that gets called whenever the existence of the divider changes. */ + void unregisterInSplitScreenListener(Consumer<Boolean> listener); + /** Registers listener that gets called whenever the split screen bounds changes. */ void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 341a459902db..07af289c4f35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -20,9 +20,11 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.Display.DEFAULT_DISPLAY; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; @@ -48,12 +50,15 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -81,8 +86,8 @@ public class SplitScreenController implements SplitScreen, private final WindowManagerProxy mWindowManagerProxy; private final TaskOrganizer mTaskOrganizer; - private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners = - new ArrayList<>(); + private final CopyOnWriteArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners + = new CopyOnWriteArrayList<>(); private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners = new ArrayList<>(); @@ -107,7 +112,8 @@ public class SplitScreenController implements SplitScreen, public SplitScreenController(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController imeController, Handler handler, TransactionPool transactionPool, - ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue) { + ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, + TaskStackListenerImpl taskStackListener) { mContext = context; mDisplayController = displayController; mSystemWindows = systemWindows; @@ -162,6 +168,40 @@ public class SplitScreenController implements SplitScreen, mWindowManager = new DividerWindowManager(mSystemWindows); mDisplayController.addDisplayWindowListener(this); // Don't initialize the divider or anything until we get the default display. + + taskStackListener.addListener( + new TaskStackListenerCallback() { + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (!wasVisible || task.getWindowingMode() + != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || !mSplits.isSplitScreenSupported()) { + return; + } + + if (isMinimized()) { + onUndockingTask(); + } + } + + @Override + public void onActivityForcedResizable(String packageName, int taskId, + int reason) { + mForcedResizableController.activityForcedResizable(packageName, taskId, + reason); + } + + @Override + public void onActivityDismissingDockedStack() { + mForcedResizableController.activityDismissingSplitScreen(); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayFailed() { + mForcedResizableController.activityLaunchOnSecondaryDisplayFailed(); + } + }); } void onSplitScreenSupported() { @@ -173,11 +213,6 @@ public class SplitScreenController implements SplitScreen, } @Override - public boolean isSplitScreenSupported() { - return mSplits.isSplitScreenSupported(); - } - - @Override public void onKeyguardVisibilityChanged(boolean showing) { if (!isSplitActive() || mView == null) { return; @@ -404,21 +439,6 @@ public class SplitScreenController implements SplitScreen, } @Override - public void onActivityForcedResizable(String packageName, int taskId, int reason) { - mForcedResizableController.activityForcedResizable(packageName, taskId, reason); - } - - @Override - public void onActivityDismissingSplitScreen() { - mForcedResizableController.activityDismissingSplitScreen(); - } - - @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { - mForcedResizableController.activityLaunchOnSecondaryDisplayFailed(); - } - - @Override public void onUndockingTask() { if (mView != null) { mView.onUndockingTask(); @@ -459,6 +479,17 @@ public class SplitScreenController implements SplitScreen, } @Override + public void unregisterInSplitScreenListener(Consumer<Boolean> listener) { + synchronized (mDockedStackExistsListeners) { + for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) { + if (mDockedStackExistsListeners.get(i) == listener) { + mDockedStackExistsListeners.remove(i); + } + } + } + } + + @Override public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { synchronized (mBoundsChangedListeners) { mBoundsChangedListeners.add(new WeakReference<>(listener)); @@ -481,9 +512,7 @@ public class SplitScreenController implements SplitScreen, } // Note: The set of running tasks from the system is ordered by recency. final RunningTaskInfo topRunningTask = runningTasks.get(0); - - final int activityType = topRunningTask.configuration.windowConfiguration - .getActivityType(); + final int activityType = topRunningTask.getActivityType(); if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java index 64e9d6618390..5b2b38ba8189 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.ShellTaskOrganizer.getWindowingMode; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager.RunningTaskInfo; @@ -42,6 +41,7 @@ import com.android.wm.shell.Transitions; import com.android.wm.shell.common.SyncTransactionQueue; import java.io.PrintWriter; +import java.util.ArrayList; class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = "SplitScreenTaskListener"; @@ -106,7 +106,7 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { return; } - final int winMode = getWindowingMode(taskInfo); + final int winMode = taskInfo.getWindowingMode(); if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Primary taskId=%d", TAG, taskInfo.taskId); @@ -283,6 +283,21 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { mSplitScreenController.startEnterSplit(); } } else if (secondaryImpliesMinimize) { + // Workaround for b/172686383, we can't rely on the sync bounds change transaction for + // the home task to finish before the last updateChildTaskSurface() call even if it's + // queued on the sync transaction queue, so ensure that the home task surface is updated + // again before we minimize + final ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); + mSplitScreenController.getWmProxy().getHomeAndRecentsTasks(tasks, + mSplitScreenController.getSecondaryRoot()); + for (int i = 0; i < tasks.size(); i++) { + final RunningTaskInfo taskInfo = tasks.get(i); + final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); + if (leash != null) { + updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */); + } + } + // Both splits are populated but the secondary split has a home/recents stack on top, // so enter minimized mode. mSplitScreenController.ensureMinimizedSplit(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java index c51bbeb7b6c2..0307206e2def 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java @@ -120,7 +120,7 @@ class WindowManagerProxy { new WindowOrganizer().applyTransaction(t); } - private boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out, + boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out, WindowContainerToken parent) { boolean resizable = false; List<ActivityManager.RunningTaskInfo> rootTasks = parent == null @@ -209,8 +209,7 @@ class WindowManagerProxy { continue; } // Only move fullscreen tasks to split secondary. - if (rootTask.configuration.windowConfiguration.getWindowingMode() - != WINDOWING_MODE_FULLSCREEN) { + if (rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { continue; } // Since this iterates from bottom to top, update topHomeTask for every fullscreen task @@ -232,7 +231,7 @@ class WindowManagerProxy { } boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) { - final int atype = ti.configuration.windowConfiguration.getActivityType(); + final int atype = ti.getActivityType(); return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java new file mode 100644 index 000000000000..884287209d53 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 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.common; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.IActivityTaskManager; +import android.content.ComponentName; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.wm.shell.common.TaskStackListenerImpl}. + */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class TaskStackListenerImplTest { + + @Mock + private IActivityTaskManager mActivityTaskManager; + + @Mock + private TaskStackListenerCallback mCallback; + + @Mock + private TaskStackListenerCallback mOtherCallback; + + private TaskStackListenerImpl mImpl; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mImpl = new TaskStackListenerImpl(mActivityTaskManager); + mImpl.setHandler(new ProxyToListenerImplHandler(mImpl)); + mImpl.addListener(mCallback); + mImpl.addListener(mOtherCallback); + } + + @Test + public void testAddRemoveMultipleListeners_ExpectRegisterUnregisterOnce() + throws RemoteException { + TaskStackListenerImpl impl = new TaskStackListenerImpl(mActivityTaskManager); + impl.setHandler(new ProxyToListenerImplHandler(impl)); + reset(mActivityTaskManager); + impl.addListener(mCallback); + impl.addListener(mOtherCallback); + verify(mActivityTaskManager, times(1)).registerTaskStackListener(any()); + + impl.removeListener(mOtherCallback); + impl.removeListener(mCallback); + verify(mActivityTaskManager, times(1)).unregisterTaskStackListener(any()); + } + + @Test + public void testOnRecentTaskListUpdated() { + mImpl.onRecentTaskListUpdated(); + verify(mCallback).onRecentTaskListUpdated(); + verify(mOtherCallback).onRecentTaskListUpdated(); + } + + @Test + public void testOnRecentTaskListFrozenChanged() { + mImpl.onRecentTaskListFrozenChanged(true); + verify(mCallback).onRecentTaskListFrozenChanged(eq(true)); + verify(mOtherCallback).onRecentTaskListFrozenChanged(eq(true)); + } + + @Test + public void testOnTaskStackChanged() { + mImpl.onTaskStackChanged(); + verify(mCallback).onTaskStackChangedBackground(); + verify(mCallback).onTaskStackChanged(); + verify(mOtherCallback).onTaskStackChangedBackground(); + verify(mOtherCallback).onTaskStackChanged(); + } + + @Test + public void testOnTaskProfileLocked() { + mImpl.onTaskProfileLocked(1, 2); + verify(mCallback).onTaskProfileLocked(eq(1), eq(2)); + verify(mOtherCallback).onTaskProfileLocked(eq(1), eq(2)); + } + + @Test + public void testOnTaskDisplayChanged() { + mImpl.onTaskDisplayChanged(1, 2); + verify(mCallback).onTaskDisplayChanged(eq(1), eq(2)); + verify(mOtherCallback).onTaskDisplayChanged(eq(1), eq(2)); + } + + @Test + public void testOnTaskCreated() { + mImpl.onTaskCreated(1, new ComponentName("a", "b")); + verify(mCallback).onTaskCreated(eq(1), eq(new ComponentName("a", "b"))); + verify(mOtherCallback).onTaskCreated(eq(1), eq(new ComponentName("a", "b"))); + } + + @Test + public void testOnTaskRemoved() { + mImpl.onTaskRemoved(123); + verify(mCallback).onTaskRemoved(eq(123)); + verify(mOtherCallback).onTaskRemoved(eq(123)); + } + + @Test + public void testOnTaskMovedToFront() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onTaskMovedToFront(info); + verify(mCallback).onTaskMovedToFront(eq(info)); + verify(mOtherCallback).onTaskMovedToFront(eq(info)); + } + + @Test + public void testOnTaskDescriptionChanged() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onTaskDescriptionChanged(info); + verify(mCallback).onTaskDescriptionChanged(eq(info)); + verify(mOtherCallback).onTaskDescriptionChanged(eq(info)); + } + + @Test + public void testOnTaskSnapshotChanged() { + ActivityManager.TaskSnapshot snapshot = mock(ActivityManager.TaskSnapshot.class); + mImpl.onTaskSnapshotChanged(123, snapshot); + verify(mCallback).onTaskSnapshotChanged(eq(123), eq(snapshot)); + verify(mOtherCallback).onTaskSnapshotChanged(eq(123), eq(snapshot)); + } + + @Test + public void testOnBackPressedOnTaskRoot() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onBackPressedOnTaskRoot(info); + verify(mCallback).onBackPressedOnTaskRoot(eq(info)); + verify(mOtherCallback).onBackPressedOnTaskRoot(eq(info)); + } + + @Test + public void testOnActivityRestartAttempt() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onActivityRestartAttempt(info, true, true, true); + verify(mCallback).onActivityRestartAttempt(eq(info), eq(true), eq(true), eq(true)); + verify(mOtherCallback).onActivityRestartAttempt(eq(info), eq(true), eq(true), eq(true)); + } + + @Test + public void testOnActivityPinned() { + mImpl.onActivityPinned("abc", 1, 2, 3); + verify(mCallback).onActivityPinned(eq("abc"), eq(1), eq(2), eq(3)); + verify(mOtherCallback).onActivityPinned(eq("abc"), eq(1), eq(2), eq(3)); + } + + @Test + public void testOnActivityUnpinned() { + mImpl.onActivityUnpinned(); + verify(mCallback).onActivityUnpinned(); + verify(mOtherCallback).onActivityUnpinned(); + } + + @Test + public void testOnActivityForcedResizable() { + mImpl.onActivityForcedResizable("abc", 1, 2); + verify(mCallback).onActivityForcedResizable(eq("abc"), eq(1), eq(2)); + verify(mOtherCallback).onActivityForcedResizable(eq("abc"), eq(1), eq(2)); + } + + @Test + public void testOnActivityDismissingDockedStack() { + mImpl.onActivityDismissingDockedStack(); + verify(mCallback).onActivityDismissingDockedStack(); + verify(mOtherCallback).onActivityDismissingDockedStack(); + } + + @Test + public void testOnActivityLaunchOnSecondaryDisplayFailed() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onActivityLaunchOnSecondaryDisplayFailed(info, 1); + verify(mCallback).onActivityLaunchOnSecondaryDisplayFailed(eq(info)); + verify(mOtherCallback).onActivityLaunchOnSecondaryDisplayFailed(eq(info)); + } + + @Test + public void testOnActivityLaunchOnSecondaryDisplayRerouted() { + ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); + mImpl.onActivityLaunchOnSecondaryDisplayRerouted(info, 1); + verify(mCallback).onActivityLaunchOnSecondaryDisplayRerouted(eq(info)); + verify(mOtherCallback).onActivityLaunchOnSecondaryDisplayRerouted(eq(info)); + } + + @Test + public void testOnActivityRequestedOrientationChanged() { + mImpl.onActivityRequestedOrientationChanged(1, 2); + verify(mCallback).onActivityRequestedOrientationChanged(eq(1), eq(2)); + verify(mOtherCallback).onActivityRequestedOrientationChanged(eq(1), eq(2)); + } + + @Test + public void testOnActivityRotation() { + mImpl.onActivityRotation(123); + verify(mCallback).onActivityRotation(eq(123)); + verify(mOtherCallback).onActivityRotation(eq(123)); + } + + @Test + public void testOnSizeCompatModeActivityChanged() { + IBinder b = mock(IBinder.class); + mImpl.onSizeCompatModeActivityChanged(123, b); + verify(mCallback).onSizeCompatModeActivityChanged(eq(123), eq(b)); + verify(mOtherCallback).onSizeCompatModeActivityChanged(eq(123), eq(b)); + } + + /** + * Handler that synchronously calls TaskStackListenerImpl#handleMessage() when it receives a + * message. + */ + private class ProxyToListenerImplHandler extends Handler { + public ProxyToListenerImplHandler(Callback callback) { + super(callback); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + return mImpl.handleMessage(msg); + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index affd7367f472..fad1f057267a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; @@ -68,10 +69,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.function.Consumer; /** * Tests for the drag and drop policy. @@ -124,6 +128,12 @@ public class DragAndDropPolicyTest { doReturn(new Rect(50, 0, 100, 100)).when(divider) .getNonMinimizedSplitScreenSecondaryBounds(); + doAnswer((Answer<Void>) invocation -> { + Consumer<Boolean> callback = invocation.getArgument(0); + callback.accept(true); + return null; + }).when(mSplitScreen).registerInSplitScreenListener(any()); + mPolicy = new DragAndDropPolicy(mContext, mIActivityTaskManager, mSplitScreen, mStarter); mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 3645f1e56f92..8ef077e5f857 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -33,6 +33,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; import org.junit.Ignore; @@ -64,6 +65,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedTimeoutHandler mMockTimeoutHandler; @Mock IOverlayManager mMockOverlayManager; + @Mock + TaskStackListenerImpl mMockTaskStackListener; @Before public void setUp() throws Exception { @@ -76,7 +79,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockTouchHandler, mMockTutorialHandler, mMockGestureHandler, - mMockOverlayManager); + mMockOverlayManager, + mMockTaskStackListener); mOneHandedController = Mockito.spy(oneHandedController); mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index 3341c9cbacb9..ba8c737924f4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -25,6 +25,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; import org.junit.Test; @@ -47,6 +48,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock IOverlayManager mMockOverlayManager; + @Mock + TaskStackListenerImpl mMockTaskStackListener; @Before public void setUp() { @@ -60,7 +63,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mTouchHandler, mTutorialHandler, mGestureHandler, - mMockOverlayManager); + mMockOverlayManager, + mMockTaskStackListener); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index a00a3b6a654e..745d18804f0b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -39,6 +39,7 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; @@ -68,6 +69,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private PipTouchHandler mMockPipTouchHandler; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; @Mock private PipBoundsState mMockPipBoundsState; + @Mock private TaskStackListenerImpl mMockTaskStackListener; @Mock private ShellExecutor mMockExecutor; @Before @@ -76,7 +78,8 @@ public class PipControllerTest extends ShellTestCase { mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, + mMockExecutor); doAnswer(invocation -> { ((Runnable) invocation.getArgument(0)).run(); return null; @@ -108,7 +111,8 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor)); + mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, + mMockExecutor)); } @Test diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java index 27e4c85e1e02..bf23a49df2c3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java @@ -36,6 +36,7 @@ import java.io.PrintWriter; /** * Manages the input consumer that allows the SystemUI to directly receive input. + * TODO: Refactor this for the gesture nav case */ public class InputConsumerController { @@ -99,14 +100,6 @@ public class InputConsumerController { } /** - * @return A controller for the pip input consumer. - */ - public static InputConsumerController getPipInputConsumer() { - return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), - INPUT_CONSUMER_PIP); - } - - /** * @return A controller for the recents animation input consumer. */ public static InputConsumerController getRecentsAnimationInputConsumer() { @@ -155,7 +148,6 @@ public class InputConsumerController { if (mInputEventReceiver == null) { final InputChannel inputChannel = new InputChannel(); try { - // TODO(b/113087003): Support Picture-in-picture in multi-display. mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel); } catch (RemoteException e) { @@ -175,7 +167,6 @@ public class InputConsumerController { public void unregisterInputConsumer() { if (mInputEventReceiver != null) { try { - // TODO(b/113087003): Support Picture-in-picture in multi-display. mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); } catch (RemoteException e) { Log.e(TAG, "Failed to destroy input consumer", e); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 17bb40e69e27..f073ced0bcee 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -118,10 +118,7 @@ public class SystemUIFactory { .setBubbles(Optional.ofNullable(null)) .setShellDump(Optional.ofNullable(null)); } - mSysUIComponent = builder - .setInputConsumerController(mWMComponent.getInputConsumerController()) - .setShellTaskOrganizer(mWMComponent.getShellTaskOrganizer()) - .build(); + mSysUIComponent = builder.build(); if (initializeComponents) { mSysUIComponent.init(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index b94a68b2441d..54aeab50763f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -22,11 +22,9 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; -import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; import com.android.wm.shell.ShellDump; -import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; @@ -68,12 +66,6 @@ public interface SysUIComponent { Builder setBubbles(Optional<Bubbles> b); @BindsInstance - Builder setInputConsumerController(InputConsumerController i); - - @BindsInstance - Builder setShellTaskOrganizer(ShellTaskOrganizer s); - - @BindsInstance Builder setShellDump(Optional<ShellDump> shellDump); SysUIComponent build(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 8f3d8eaac2d3..9154ddba5d88 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,11 +16,9 @@ package com.android.systemui.dagger; -import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.wmshell.WMShellModule; import com.android.wm.shell.ShellDump; import com.android.wm.shell.ShellInit; -import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; @@ -60,14 +58,6 @@ public interface WMComponent { @WMSingleton Optional<ShellDump> getShellDump(); - // TODO(b/162923491): Refactor this out so Pip doesn't need to inject this - @WMSingleton - InputConsumerController getInputConsumerController(); - - // TODO(b/162923491): To be removed once Bubbles migrates over to the Shell - @WMSingleton - ShellTaskOrganizer getShellTaskOrganizer(); - // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change // depending on the threading mechanism we go with @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 5caab6ab0e7e..694b4a03fa64 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; @@ -54,6 +55,7 @@ public abstract class TvPipModule { PipTaskOrganizer pipTaskOrganizer, PipMediaController pipMediaController, PipNotification pipNotification, + TaskStackListenerImpl taskStackListener, WindowManagerShellWrapper windowManagerShellWrapper) { return Optional.of( new PipController( @@ -63,6 +65,7 @@ public abstract class TvPipModule { pipTaskOrganizer, pipMediaController, pipNotification, + taskStackListener, windowManagerShellWrapper)); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index 294c749a2abe..f88bedd88d9f 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -27,6 +27,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -57,8 +58,9 @@ public class TvWMShellModule { DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, @Main Handler handler, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue) { + SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener) { return new SplitScreenController(context, displayController, systemWindows, - displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue); + displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue, + taskStackListener); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index f896891c5039..7a244389a3bc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -55,7 +55,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.shared.tracing.ProtoTraceable; @@ -71,7 +70,6 @@ import com.android.wm.shell.onehanded.OneHandedEvents; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogImpl; import com.android.wm.shell.splitscreen.SplitScreen; @@ -100,9 +98,7 @@ public final class WMShell extends SystemUI private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; - private final InputConsumerController mInputConsumerController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TaskStackChangeListeners mTaskStackChangeListeners; private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; @@ -120,9 +116,7 @@ public final class WMShell extends SystemUI @Inject public WMShell(Context context, CommandQueue commandQueue, ConfigurationController configurationController, - InputConsumerController inputConsumerController, KeyguardUpdateMonitor keyguardUpdateMonitor, - TaskStackChangeListeners taskStackChangeListeners, NavigationModeController navigationModeController, ScreenLifecycle screenLifecycle, SysUiState sysUiState, @@ -134,9 +128,7 @@ public final class WMShell extends SystemUI super(context); mCommandQueue = commandQueue; mConfigurationController = configurationController; - mInputConsumerController = inputConsumerController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTaskStackChangeListeners = taskStackChangeListeners; mNavigationModeController = navigationModeController; mScreenLifecycle = screenLifecycle; mSysUiState = sysUiState; @@ -192,58 +184,6 @@ public final class WMShell extends SystemUI } }); - // TODO: Move this into the shell - // Handle for system task stack changes. - mTaskStackChangeListeners.registerTaskStackListener( - new TaskStackChangeListener() { - @Override - public void onTaskStackChanged() { - pip.onTaskStackChanged(); - } - - @Override - public void onActivityPinned(String packageName, int userId, int taskId, - int stackId) { - pip.onActivityPinned(packageName); - mInputConsumerController.registerInputConsumer(true /* withSfVsync */); - } - - @Override - public void onActivityUnpinned() { - final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPipActivity(mContext); - final ComponentName topActivity = topPipActivityInfo.first; - pip.onActivityUnpinned(topActivity); - mInputConsumerController.unregisterInputConsumer(); - } - - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - pip.onActivityRestartAttempt(task, clearedTask); - } - }); - - try { - RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (taskInfo != null) { - // If SystemUI restart, and it already existed a pinned stack, - // register the pip input consumer to ensure touch can send to it. - mInputConsumerController.registerInputConsumer(true /* withSfVsync */); - } - } catch (RemoteException | UnsupportedOperationException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); - e.printStackTrace(); - } - - // Register the listener for input consumer touch events. Only for Phone - if (pip.getPipTouchHandler() != null) { - mInputConsumerController.setInputListener(pip.getPipTouchHandler()::handleTouchEvent); - mInputConsumerController.setRegistrationListener( - pip.getPipTouchHandler()::onRegistrationChanged); - } - // The media session listener needs to be re-registered when switching users UserInfoController userInfoController = Dependency.get(UserInfoController.class); userInfoController.addCallback((String name, Drawable picture, String userAccount) -> @@ -263,39 +203,6 @@ public final class WMShell extends SystemUI } }; mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback); - - mTaskStackChangeListeners.registerTaskStackListener( - new TaskStackChangeListener() { - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode() - != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || !splitScreen.isSplitScreenSupported()) { - return; - } - - if (splitScreen.isMinimized()) { - splitScreen.onUndockingTask(); - } - } - - @Override - public void onActivityForcedResizable(String packageName, int taskId, - int reason) { - splitScreen.onActivityForcedResizable(packageName, taskId, reason); - } - - @Override - public void onActivityDismissingDockedStack() { - splitScreen.onActivityDismissingSplitScreen(); - } - - @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { - splitScreen.onActivityLaunchOnSecondaryDisplayFailed(); - } - }); } @VisibleForTesting @@ -375,21 +282,6 @@ public final class WMShell extends SystemUI } } }); - - mTaskStackChangeListeners.registerTaskStackListener( - new TaskStackChangeListener() { - @Override - public void onTaskCreated(int taskId, ComponentName componentName) { - oneHanded.stopOneHanded( - OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - } - - @Override - public void onTaskMovedToFront(int taskId) { - oneHanded.stopOneHanded( - OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - } - }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index bdca503f40c1..6546ed5577b1 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -43,6 +43,7 @@ import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHanded; @@ -116,12 +117,6 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static InputConsumerController provideInputConsumerController() { - return InputConsumerController.getPipInputConsumer(); - } - - @WMSingleton - @Provides static FloatingContentCoordinator provideFloatingContentCoordinator() { return new FloatingContentCoordinator(); } @@ -181,6 +176,12 @@ public abstract class WMShellBaseModule { mainExecutor, AnimationThread.instance().getExecutor()); } + @WMSingleton + @Provides + static TaskStackListenerImpl providerTaskStackListenerImpl(@Main Handler handler) { + return new TaskStackListenerImpl(handler); + } + @BindsOptionalOf abstract SplitScreen optionalSplitScreen(); @@ -203,8 +204,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<OneHanded> provideOneHandedController(Context context, - DisplayController displayController) { - return Optional.ofNullable(OneHandedController.create(context, displayController)); + DisplayController displayController, TaskStackListenerImpl taskStackListener) { + return Optional.ofNullable(OneHandedController.create(context, displayController, + taskStackListener)); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 25ca7ade6077..1cdec27af870 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -30,6 +30,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; @@ -72,9 +73,10 @@ public class WMShellModule { DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, @Main Handler handler, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue) { + SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener) { return new SplitScreenController(context, displayController, systemWindows, - displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue); + displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue, + taskStackListener); } @WMSingleton @@ -84,11 +86,11 @@ public class WMShellModule { PipBoundsState pipBoundsState, PipMediaController pipMediaController, PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, - ShellExecutor mainExecutor) { + TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper, mainExecutor)); + windowManagerShellWrapper, taskStackListener, mainExecutor)); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index d0bf281f343f..6a303a941960 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -62,7 +62,6 @@ public class WMShellTest extends SysuiTestCase { @Mock CommandQueue mCommandQueue; @Mock ConfigurationController mConfigurationController; @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock TaskStackChangeListeners mTaskStackChangeListeners; @Mock InputConsumerController mMockInputConsumerController; @Mock NavigationModeController mNavigationModeController; @Mock ScreenLifecycle mScreenLifecycle; @@ -77,13 +76,11 @@ public class WMShellTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mInputConsumerController = InputConsumerController.getPipInputConsumer(); mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController, - mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners, - mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip), - Optional.of(mSplitScreen), Optional.of(mOneHanded), mProtoTracer, - Optional.of(mShellDump)); + mKeyguardUpdateMonitor, mNavigationModeController, + mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen), + Optional.of(mOneHanded), mProtoTracer, Optional.of(mShellDump)); when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler); } @@ -100,8 +97,6 @@ public class WMShellTest extends SysuiTestCase { mWMShell.initSplitScreen(mSplitScreen); verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); - verify(mTaskStackChangeListeners).registerTaskStackListener( - any(TaskStackChangeListener.class)); } @Test @@ -113,8 +108,6 @@ public class WMShellTest extends SysuiTestCase { verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class)); verify(mNavigationModeController).addListener( any(NavigationModeController.ModeChangedListener.class)); - verify(mTaskStackChangeListeners).registerTaskStackListener( - any(TaskStackChangeListener.class)); verify(mOneHanded).registerGestureCallback(any( OneHandedGestureHandler.OneHandedGestureEventCallback.class)); diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 3eac1be3ebee..627af9149fe5 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -49,6 +49,7 @@ class DragDropController { static final int MSG_DRAG_END_TIMEOUT = 0; static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 1; static final int MSG_ANIMATION_END = 2; + static final int MSG_REMOVE_DRAG_SURFACE_TIMEOUT = 3; /** * Drag state per operation. @@ -384,6 +385,14 @@ class DragDropController { } break; } + + case MSG_REMOVE_DRAG_SURFACE_TIMEOUT: { + synchronized (mService.mGlobalLock) { + mService.mTransactionFactory.get() + .reparent((SurfaceControl) msg.obj, null).apply(); + } + break; + } } } } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 8c80205a9e45..e7d8ad690bc4 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -23,6 +23,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.wm.DragDropController.MSG_ANIMATION_END; import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT; +import static com.android.server.wm.DragDropController.MSG_REMOVE_DRAG_SURFACE_TIMEOUT; import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -59,6 +60,7 @@ import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; +import com.android.internal.os.SomeArgs; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.IDragAndDropPermissions; import com.android.server.LocalServices; @@ -248,6 +250,9 @@ class DragState { if (mSurfaceControl != null) { if (!mRelinquishDragSurface) { mTransaction.reparent(mSurfaceControl, null).apply(); + } else { + mDragDropController.sendTimeoutMessage(MSG_REMOVE_DRAG_SURFACE_TIMEOUT, + mSurfaceControl); } mSurfaceControl = null; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1255bb2a02e9..86f1cf71e2e0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4071,6 +4071,10 @@ class Task extends WindowContainer<WindowContainer> { info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription()); info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode(); info.configuration.setTo(getConfiguration()); + // Update to the task's current activity type and windowing mode which may differ from the + // window configuration + info.configuration.windowConfiguration.setActivityType(getActivityType()); + info.configuration.windowConfiguration.setWindowingMode(getWindowingMode()); info.token = mRemoteToken.toWindowContainerToken(); //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child |