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 0000000000000..87ddb181dcce3 --- /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 0000000000000..0f6dd93f9c163 --- /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 0000000000000..7efacc7b2c1f8 --- /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 mTaskStackListeners = new ArrayList<>(); + private final List 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/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 7d039d4d92f6d..f84936e5f3861 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 8d5da1a5ffcbc..46270cf24f489 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 @@ -80,30 +80,6 @@ public interface Pip { default void movePipToFullscreen() { } - /** - * 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 */ @@ -131,12 +107,6 @@ public interface Pip { default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) { } - /** - * Called when task stack changed. - */ - default void onTaskStackChanged() { - } - /** * Resize the Pip to the appropriate size for the input state. * 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 6e1fc83a87fb7..251f8d4722459 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 mPinnedStackAnimationRecentsCallback; - private WindowManagerShellWrapper mWindowManagerShellWrapper; - private boolean mIsInFixedRotation; + private Consumer 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. @@ -247,7 +258,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); @@ -263,6 +281,57 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } + + 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(); + } + + // 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 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.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_PINNED) { + return; + } + mTouchHandler.getMotionHelper().expandLeavePip( + clearedTask /* skipAnimation */); + } + }); } @Override @@ -272,33 +341,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac }); } - @Override - public void onActivityPinned(String packageName) { - mMainExecutor.execute(() -> { - mTouchHandler.onActivityPinned(); - mMediaController.onActivityPinned(); - mAppOpsListener.onActivityPinned(packageName); - }); - } - - @Override - public void onActivityUnpinned(ComponentName topActivity) { - mMainExecutor.execute(() -> { - mTouchHandler.onActivityUnpinned(topActivity); - mAppOpsListener.onActivityUnpinned(); - }); - } - - @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(() -> { @@ -467,6 +509,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsHandler.dump(pw, innerPrefix); mPipTaskOrganizer.dump(pw, innerPrefix); mPipBoundsState.dump(pw, innerPrefix); + mPipInputConsumer.dump(pw, innerPrefix); } /** @@ -476,17 +519,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 3468b888c06a5..70b5446213298 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,8 +396,7 @@ 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) { @@ -384,8 +408,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 d1176733deb95..cfe9b9157eaa1 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(); 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 341a459902dba..49f11592a7fa6 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,6 +50,8 @@ 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; @@ -107,7 +111,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 +167,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.configuration.windowConfiguration.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() { @@ -172,11 +211,6 @@ public class SplitScreenController implements SplitScreen, mTaskOrganizer.applyTransaction(tct); } - @Override - public boolean isSplitScreenSupported() { - return mSplits.isSplitScreenSupported(); - } - @Override public void onKeyguardVisibilityChanged(boolean showing) { if (!isSplitActive() || mView == null) { @@ -403,21 +437,6 @@ public class SplitScreenController implements SplitScreen, mWindowManager.setTouchable(!mAdjustedForIme); } - @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) { 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 0000000000000..884287209d535 --- /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/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 3645f1e56f923..8ef077e5f8578 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 3341c9cbacb9b..ba8c737924f40 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 a00a3b6a654ed..745d18804f0ba 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 27e4c85e1e025..bf23a49df2c3f 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 { @@ -98,14 +99,6 @@ public class InputConsumerController { mName = name; } - /** - * @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. */ @@ -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 17bb40e69e27a..f073ced0bcee0 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 b94a68b2441de..54aeab50763ff 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; @@ -67,12 +65,6 @@ public interface SysUIComponent { @BindsInstance Builder setBubbles(Optional b); - @BindsInstance - Builder setInputConsumerController(InputConsumerController i); - - @BindsInstance - Builder setShellTaskOrganizer(ShellTaskOrganizer s); - @BindsInstance Builder setShellDump(Optional shellDump); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 8f3d8eaac2d34..9154ddba5d880 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 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 5caab6ab0e7ee..694b4a03fa64e 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 294c749a2abe1..f88bedd88d9f9 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 f896891c5039d..7a244389a3bcf 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 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 bdca503f40c1e..6546ed5577b19 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; @@ -114,12 +115,6 @@ public abstract class WMShellBaseModule { return new DragAndDropController(context, displayController); } - @WMSingleton - @Provides - static InputConsumerController provideInputConsumerController() { - return InputConsumerController.getPipInputConsumer(); - } - @WMSingleton @Provides static FloatingContentCoordinator provideFloatingContentCoordinator() { @@ -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 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 25ca7ade60776..1cdec27af8703 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 d0bf281f343f9..6a303a9419605 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));