From f3929238b0a95c4573b155e60d964419d0d77848 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Wed, 16 Dec 2020 16:46:39 -0800 Subject: [PATCH] Add TaskViewFactory. - This CL also changes TV.setListener to have a separate Executor. Bug: 165794075 Test: atest TaskViewTest BubblesTest NewNotifPipelineBubblesTest Change-Id: Ib0324ec06d088694556f8cef6cee726d9789ce2c --- .../src/com/android/wm/shell/TaskView.java | 146 ++++++++++-------- .../com/android/wm/shell/TaskViewFactory.java | 32 ++++ .../wm/shell/TaskViewFactoryController.java | 62 ++++++++ .../wm/shell/bubbles/BubbleExpandedView.java | 2 +- .../com/android/wm/shell/TaskViewTest.java | 6 +- .../android/systemui/wmshell/BubblesTest.java | 7 +- .../wmshell/NewNotifPipelineBubblesTest.java | 7 +- 7 files changed, 188 insertions(+), 74 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 73ae3db32891c..8d0e9655f28dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -73,6 +73,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final CloseGuard mGuard = new CloseGuard(); private final ShellTaskOrganizer mTaskOrganizer; + private final Executor mShellExecutor; private ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; @@ -81,7 +82,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private boolean mSurfaceCreated; private boolean mIsInitialized; private Listener mListener; - private Executor mExecutor; + private Executor mListenerExecutor; private final Rect mTmpRect = new Rect(); private final Rect mTmpRootRect = new Rect(); @@ -91,7 +92,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, super(context, null, 0, 0, true /* disableBackgroundLayer */); mTaskOrganizer = organizer; - mExecutor = organizer.getExecutor(); + mShellExecutor = organizer.getExecutor(); setUseAlpha(); getHolder().addCallback(this); mGuard.open("release"); @@ -100,12 +101,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, /** * Only one listener may be set on the view, throws an exception otherwise. */ - public void setListener(Listener listener) { + public void setListener(@NonNull Executor executor, Listener listener) { if (mListener != null) { throw new IllegalStateException( "Trying to set a listener when one has already been set"); } mListener = listener; + mListenerExecutor = executor; } /** @@ -150,7 +152,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private void prepareActivityOptions(ActivityOptions options) { final Binder launchCookie = new Binder(); - mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this); + mShellExecutor.execute(() -> { + mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this); + }); options.setLaunchCookie(launchCookie); options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); options.setTaskAlwaysOnTop(true); @@ -197,11 +201,15 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private void performRelease() { getHolder().removeCallback(this); - mTaskOrganizer.removeListener(this); - resetTaskInfo(); + mShellExecutor.execute(() -> { + mTaskOrganizer.removeListener(this); + resetTaskInfo(); + }); mGuard.close(); if (mListener != null && mIsInitialized) { - mListener.onReleased(); + mListenerExecutor.execute(() -> { + mListener.onReleased(); + }); mIsInitialized = false; } } @@ -218,75 +226,71 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskOrganizer.applyTransaction(wct); // TODO(b/151449487): Only call callback once we enable synchronization if (mListener != null) { - mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated); + mListenerExecutor.execute(() -> { + mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated); + }); } } @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mExecutor == null) return; - mExecutor.execute(() -> { - mTaskInfo = taskInfo; - mTaskToken = taskInfo.token; - mTaskLeash = leash; + mTaskInfo = taskInfo; + mTaskToken = taskInfo.token; + mTaskLeash = leash; - if (mSurfaceCreated) { - // Surface is ready, so just reparent the task to this surface control - mTransaction.reparent(mTaskLeash, getSurfaceControl()) - .show(mTaskLeash) - .apply(); - } else { - // The surface has already been destroyed before the task has appeared, - // so go ahead and hide the task entirely - updateTaskVisibility(); - } - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true); - // TODO: Synchronize show with the resize - onLocationChanged(); - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + if (mSurfaceCreated) { + // Surface is ready, so just reparent the task to this surface control + mTransaction.reparent(mTaskLeash, getSurfaceControl()) + .show(mTaskLeash) + .apply(); + } else { + // The surface has already been destroyed before the task has appeared, + // so go ahead and hide the task entirely + updateTaskVisibility(); + } + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true); + // TODO: Synchronize show with the resize + onLocationChanged(); + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); - if (mListener != null) { + if (mListener != null) { + mListenerExecutor.execute(() -> { mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity); - } - }); + }); + } } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - if (mExecutor == null) return; - mExecutor.execute(() -> { - if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; + if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; - if (mListener != null) { + if (mListener != null) { + mListenerExecutor.execute(() -> { mListener.onTaskRemovalStarted(taskInfo.taskId); - } - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false); + }); + } + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false); - // Unparent the task when this surface is destroyed - mTransaction.reparent(mTaskLeash, null).apply(); - resetTaskInfo(); - }); + // Unparent the task when this surface is destroyed + mTransaction.reparent(mTaskLeash, null).apply(); + resetTaskInfo(); } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - if (mExecutor == null) return; - mExecutor.execute(() -> { - mTaskInfo.taskDescription = taskInfo.taskDescription; - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); - }); + mTaskInfo.taskDescription = taskInfo.taskDescription; + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); } @Override public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { - if (mExecutor == null) return; - mExecutor.execute(() -> { - if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; - if (mListener != null) { + if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; + if (mListener != null) { + mListenerExecutor.execute(() -> { mListener.onBackPressedOnTaskRoot(taskInfo.taskId); - } - }); + }); + } } @Override @@ -306,17 +310,21 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mSurfaceCreated = true; if (mListener != null && !mIsInitialized) { mIsInitialized = true; - mListener.onInitialized(); + mListenerExecutor.execute(() -> { + mListener.onInitialized(); + }); } - if (mTaskToken == null) { - // Nothing to update, task is not yet available - return; - } - // Reparent the task when this surface is created - mTransaction.reparent(mTaskLeash, getSurfaceControl()) - .show(mTaskLeash) - .apply(); - updateTaskVisibility(); + mShellExecutor.execute(() -> { + if (mTaskToken == null) { + // Nothing to update, task is not yet available + return; + } + // Reparent the task when this surface is created + mTransaction.reparent(mTaskLeash, getSurfaceControl()) + .show(mTaskLeash) + .apply(); + updateTaskVisibility(); + }); } @Override @@ -330,14 +338,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void surfaceDestroyed(SurfaceHolder holder) { mSurfaceCreated = false; - if (mTaskToken == null) { - // Nothing to update, task is not yet available - return; - } + mShellExecutor.execute(() -> { + if (mTaskToken == null) { + // Nothing to update, task is not yet available + return; + } - // Unparent the task when this surface is destroyed - mTransaction.reparent(mTaskLeash, null).apply(); - updateTaskVisibility(); + // Unparent the task when this surface is destroyed + mTransaction.reparent(mTaskLeash, null).apply(); + updateTaskVisibility(); + }); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java new file mode 100644 index 0000000000000..a29e7a085a21d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactory.java @@ -0,0 +1,32 @@ +/* + * 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; + +import android.annotation.UiContext; +import android.content.Context; + +import com.android.wm.shell.common.annotations.ExternalThread; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** Interface to create TaskView. */ +@ExternalThread +public interface TaskViewFactory { + /** Creates an {@link TaskView} */ + void create(@UiContext Context context, Executor executor, Consumer onCreate); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java new file mode 100644 index 0000000000000..a5dd79b373bdd --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java @@ -0,0 +1,62 @@ +/* + * 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; + +import android.annotation.UiContext; +import android.content.Context; + +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.common.annotations.ShellMainThread; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** Factory controller which can create {@link TaskView} */ +public class TaskViewFactoryController { + private final ShellTaskOrganizer mTaskOrganizer; + private final ShellExecutor mShellExecutor; + + public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer, + ShellExecutor shellExecutor) { + mTaskOrganizer = taskOrganizer; + mShellExecutor = shellExecutor; + } + + /** Creates an {@link TaskView} */ + @ShellMainThread + public void create(@UiContext Context context, Executor executor, Consumer onCreate) { + TaskView taskView = new TaskView(context, mTaskOrganizer); + executor.execute(() -> { + onCreate.accept(taskView); + }); + } + + public TaskViewFactory getTaskViewFactory() { + return new TaskViewFactoryImpl(); + } + + private class TaskViewFactoryImpl implements TaskViewFactory { + @ExternalThread + public void create(@UiContext Context context, + Executor executor, Consumer onCreate) { + mShellExecutor.execute(() -> { + TaskViewFactoryController.this.create(context, executor, onCreate); + }); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 86da2b502870d..cb6b54321b5e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -314,7 +314,7 @@ public class BubbleExpandedView extends LinearLayout { mTaskView = new TaskView(mContext, mController.getTaskOrganizer()); mExpandedViewContainer.addView(mTaskView); bringChildToFront(mTaskView); - mTaskView.setListener(mTaskViewListener); + mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener); mPositioner = mController.getPositioner(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java index 01b5204860110..eb03ab0a98020 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java @@ -99,7 +99,7 @@ public class TaskViewTest extends ShellTestCase { when(mOrganizer.getExecutor()).thenReturn(mExecutor); mTaskView = new TaskView(mContext, mOrganizer); - mTaskView.setListener(mViewListener); + mTaskView.setListener(mExecutor, mViewListener); } @After @@ -112,9 +112,9 @@ public class TaskViewTest extends ShellTestCase { @Test public void testSetPendingListener_throwsException() { TaskView taskView = new TaskView(mContext, mOrganizer); - taskView.setListener(mViewListener); + taskView.setListener(mExecutor, mViewListener); try { - taskView.setListener(mViewListener); + taskView.setListener(mExecutor, mViewListener); } catch (IllegalStateException e) { // pass return; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index eaef4556bf4d1..ccc2eb328a047 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -89,6 +89,8 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; @@ -204,6 +206,8 @@ public class BubblesTest extends SysuiTestCase { private WindowManagerShellWrapper mWindowManagerShellWrapper; @Mock private BubbleLogger mBubbleLogger; + @Mock + private ShellTaskOrganizer mShellTaskOrganizer; private TestableBubblePositioner mPositioner; @@ -269,6 +273,7 @@ public class BubblesTest extends SysuiTestCase { ); when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); + when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock())); mBubbleController = new TestableBubbleController( mContext, mBubbleData, @@ -279,7 +284,7 @@ public class BubblesTest extends SysuiTestCase { mWindowManagerShellWrapper, mLauncherApps, mBubbleLogger, - mock(ShellTaskOrganizer.class), + mShellTaskOrganizer, mPositioner, mock(ShellExecutor.class)); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 51092c131bfe7..00f4e3a3f1442 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -83,6 +83,8 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.BubbleData; @@ -186,6 +188,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { private WindowManagerShellWrapper mWindowManagerShellWrapper; @Mock private BubbleLogger mBubbleLogger; + @Mock + private ShellTaskOrganizer mShellTaskOrganizer; private TestableBubblePositioner mPositioner; @@ -237,6 +241,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mock(Handler.class) ); when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); + when(mShellTaskOrganizer.getExecutor()).thenReturn(new FakeExecutor(new FakeSystemClock())); mBubbleController = new TestableBubbleController( mContext, mBubbleData, @@ -247,7 +252,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mWindowManagerShellWrapper, mLauncherApps, mBubbleLogger, - mock(ShellTaskOrganizer.class), + mShellTaskOrganizer, mPositioner, mock(ShellExecutor.class)); mBubbleController.setExpandListener(mBubbleExpandListener);