Merge changes Ib0324ec0,I15b019cb

* changes:
  Add TaskViewFactory.
  Migrate touchable inset calulation from BubbleStackView to TaskView.
This commit is contained in:
Yuncheol Heo
2021-01-10 03:05:37 +00:00
committed by Android (Google) Code Review
8 changed files with 226 additions and 84 deletions

View File

@@ -29,11 +29,14 @@ import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
import android.util.CloseGuard;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -44,7 +47,7 @@ import java.util.concurrent.Executor;
* View that can display a task.
*/
public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
ShellTaskOrganizer.TaskListener {
ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener {
/** Callback for listening task state. */
public interface Listener {
@@ -70,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;
@@ -78,16 +82,17 @@ 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();
private final int[] mTmpLocation = new int[2];
public TaskView(Context context, ShellTaskOrganizer organizer) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mExecutor = organizer.getExecutor();
mShellExecutor = organizer.getExecutor();
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
@@ -96,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;
}
/**
@@ -146,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);
@@ -193,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;
}
}
@@ -214,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
@@ -302,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
@@ -326,13 +338,47 @@ 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
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
// TODO(b/176854108): Consider to move the logic into gatherTransparentRegions since this
// is dependent on the order of listener.
// If there are multiple TaskViews, we'll set the touchable area as the root-view, then
// subtract each TaskView from it.
if (inoutInfo.touchableRegion.isEmpty()) {
inoutInfo.setTouchableInsets(
ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
View root = getRootView();
root.getLocationInWindow(mTmpLocation);
mTmpRootRect.set(mTmpLocation[0], mTmpLocation[1], root.getWidth(), root.getHeight());
inoutInfo.touchableRegion.set(mTmpRootRect);
}
getLocationInWindow(mTmpLocation);
mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
}

View File

@@ -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<TaskView> onCreate);
}

View File

@@ -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<TaskView> 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<TaskView> onCreate) {
mShellExecutor.execute(() -> {
TaskViewFactoryController.this.create(context, executor, onCreate);
});
}
}
}

View File

@@ -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();
}

View File

@@ -1201,15 +1201,7 @@ public class BubbleStackView extends FrameLayout
mTempRect.setEmpty();
getTouchableRegion(mTempRect);
if (mIsExpanded && mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null
&& mExpandedBubble.getExpandedView().getTaskView() != null) {
inoutInfo.touchableRegion.set(mTempRect);
mExpandedBubble.getExpandedView().getTaskView().getBoundsOnScreen(mTempRect);
inoutInfo.touchableRegion.op(mTempRect, Region.Op.DIFFERENCE);
} else {
inoutInfo.touchableRegion.set(mTempRect);
}
inoutInfo.touchableRegion.set(mTempRect);
}
@Override

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);