Merge "Rename WindowDecorModelView to reflect purposes" into tm-qpr-dev

This commit is contained in:
Maryam Dehaini
2022-12-01 21:17:36 +00:00
committed by Android (Google) Code Review
8 changed files with 408 additions and 86 deletions

View File

@@ -82,7 +82,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
mTasks.put(taskInfo.taskId, state); mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) { if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.Transaction t = new SurfaceControl.Transaction();
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t); mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t);
t.apply(); t.apply();
} }

View File

@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
mWindowDecorViewModel.createWindowDecoration( mWindowDecorViewModel.onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT); change.getTaskInfo(), change.getLeash(), startT, finishT);
} }
@@ -128,32 +128,24 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
mWindowDecorViewModel.setupWindowDecorationForTransition( mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
change.getTaskInfo(), startT, finishT);
} }
private void onChangeTransitionReady( private void onChangeTransitionReady(
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
mWindowDecorViewModel.setupWindowDecorationForTransition( mWindowDecorViewModel.onTaskChanging(
change.getTaskInfo(), startT, finishT); change.getTaskInfo(), change.getLeash(), startT, finishT);
} }
private void onToFrontTransitionReady( private void onToFrontTransitionReady(
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition( mWindowDecorViewModel.onTaskChanging(
change.getTaskInfo(),
startT,
finishT);
if (!exists) {
// Window caption does not exist, create it
mWindowDecorViewModel.createWindowDecoration(
change.getTaskInfo(), change.getLeash(), startT, finishT); change.getTaskInfo(), change.getLeash(), startT, finishT);
} }
}
@Override @Override
public void onTransitionStarting(@NonNull IBinder transition) {} public void onTransitionStarting(@NonNull IBinder transition) {}

View File

@@ -103,7 +103,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
if (mWindowDecorViewModelOptional.isPresent()) { if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.Transaction t = new SurfaceControl.Transaction();
createdWindowDecor = mWindowDecorViewModelOptional.get() createdWindowDecor = mWindowDecorViewModelOptional.get()
.createWindowDecoration(taskInfo, leash, t, t); .onTaskOpening(taskInfo, leash, t, t);
t.apply(); t.apply();
} }
if (!createdWindowDecor) { if (!createdWindowDecor) {

View File

@@ -55,6 +55,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import java.util.function.Supplier;
/** /**
* View model for the window decoration with a caption and shadows. Works with * View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}. * {@link CaptionWindowDecoration}.
@@ -62,6 +64,8 @@ import com.android.wm.shell.transition.Transitions;
public class CaptionWindowDecorViewModel implements WindowDecorViewModel { public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "CaptionViewModel"; private static final String TAG = "CaptionViewModel";
private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
private final Supplier<InputManager> mInputManagerSupplier;
private final ActivityTaskManager mActivityTaskManager; private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer; private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext; private final Context mContext;
@@ -77,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>(); private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory();
public CaptionWindowDecorViewModel( public CaptionWindowDecorViewModel(
Context context, Context context,
@@ -86,6 +91,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
DisplayController displayController, DisplayController displayController,
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
DesktopModeController desktopModeController) { DesktopModeController desktopModeController) {
this(
context,
mainHandler,
mainChoreographer,
taskOrganizer,
displayController,
syncQueue,
desktopModeController,
new CaptionWindowDecoration.Factory(),
InputManager::getInstance);
}
public CaptionWindowDecorViewModel(
Context context,
Handler mainHandler,
Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
SyncTransactionQueue syncQueue,
DesktopModeController desktopModeController,
CaptionWindowDecoration.Factory captionWindowDecorFactory,
Supplier<InputManager> inputManagerSupplier) {
mContext = context; mContext = context;
mMainHandler = mainHandler; mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer; mMainChoreographer = mainChoreographer;
@@ -94,7 +122,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController; mDisplayController = displayController;
mSyncQueue = syncQueue; mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController; mDesktopModeController = desktopModeController;
mTransitionDragActive = false;
mCaptionWindowDecorFactory = captionWindowDecorFactory;
mInputManagerSupplier = inputManagerSupplier;
}
void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) {
mEventReceiverFactory = eventReceiverFactory;
} }
@Override @Override
@@ -103,42 +137,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} }
@Override @Override
public boolean createWindowDecoration( public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface, SurfaceControl taskSurface,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false; if (!shouldShowWindowDecor(taskInfo)) return false;
CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId); createWindowDecoration(taskInfo, taskSurface, startT, finishT);
if (oldDecoration != null) {
// close the old decoration if it exists to avoid two window decorations being added
oldDecoration.close();
}
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
taskSurface,
mMainHandler,
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration,
mDragStartListener);
CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner,
windowDecoration.getDragDetector());
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
setupWindowDecorationForTransition(taskInfo, startT, finishT);
if (mInputMonitor == null) {
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"caption-touch", mContext.getDisplayId());
mEventReceiver = new EventReceiver(
mInputMonitor.getInputChannel(), Looper.myLooper());
}
return true; return true;
} }
@@ -151,25 +156,45 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} }
@Override @Override
public boolean setupWindowDecorationForTransition( public void onTaskChanging(
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
destroyWindowDecoration(taskInfo);
}
return;
}
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
decoration.relayout(taskInfo, startT, finishT);
}
}
@Override
public void onTaskClosing(
RunningTaskInfo taskInfo, RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return false; if (decoration == null) return;
decoration.relayout(taskInfo, startT, finishT); decoration.relayout(taskInfo, startT, finishT);
return true;
} }
@Override @Override
public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) { public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration = final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId); mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
if (decoration == null) return false; if (decoration == null) return;
decoration.close(); decoration.close();
return true;
} }
private class CaptionTouchEventListener implements private class CaptionTouchEventListener implements
@@ -217,6 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
decoration.setButtonVisibility(); decoration.setButtonVisibility();
} }
} }
private void injectBackKey() { private void injectBackKey() {
sendBackEvent(KeyEvent.ACTION_DOWN); sendBackEvent(KeyEvent.ACTION_DOWN);
sendBackEvent(KeyEvent.ACTION_UP); sendBackEvent(KeyEvent.ACTION_UP);
@@ -302,7 +328,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} }
// InputEventReceiver to listen for touch input outside of caption bounds // InputEventReceiver to listen for touch input outside of caption bounds
private class EventReceiver extends InputEventReceiver { class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) { EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper); super(channel, looper);
} }
@@ -318,8 +344,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} }
} }
class EventReceiverFactory {
EventReceiver create(InputChannel channel, Looper looper) {
return new EventReceiver(channel, looper);
}
}
/** /**
* Handle MotionEvents relevant to focused task's caption that don't directly touch it * Handle MotionEvents relevant to focused task's caption that don't directly touch it
*
* @param ev the {@link MotionEvent} received by {@link EventReceiver} * @param ev the {@link MotionEvent} received by {@link EventReceiver}
*/ */
private void handleReceivedMotionEvent(MotionEvent ev) { private void handleReceivedMotionEvent(MotionEvent ev) {
@@ -401,7 +434,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
return focusedDecor; return focusedDecor;
} }
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED return DesktopModeStatus.IS_SUPPORTED
@@ -410,7 +442,47 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
.getResources().getConfiguration().smallestScreenWidthDp >= 600; .getResources().getConfiguration().smallestScreenWidthDp >= 600;
} }
private class DragStartListenerImpl implements TaskPositioner.DragStartListener{ private void createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (oldDecoration != null) {
// close the old decoration if it exists to avoid two window decorations being added
oldDecoration.close();
}
final CaptionWindowDecoration windowDecoration =
mCaptionWindowDecorFactory.create(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
taskSurface,
mMainHandler,
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
TaskPositioner taskPositioner =
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(
taskInfo, taskPositioner, windowDecoration.getDragDetector());
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
windowDecoration.relayout(taskInfo, startT, finishT);
if (mInputMonitor == null) {
InputManager inputManager = mInputManagerSupplier.get();
mInputMonitor =
inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId());
mEventReceiver =
mEventReceiverFactory.create(
mInputMonitor.getInputChannel(), Looper.myLooper());
}
}
private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
@Override @Override
public void onDragStart(int taskId) { public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu(); mWindowDecorByTaskId.get(taskId).closeHandleMenu();

View File

@@ -42,7 +42,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
/** /**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
* {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button. * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close
* button.
* *
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't. * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/ */
@@ -242,7 +243,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/** /**
* Sets the visibility of buttons and color of caption based on desktop mode status * Sets the visibility of buttons and color of caption based on desktop mode status
*
*/ */
void setButtonVisibility() { void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext); mDesktopActive = DesktopModeStatus.isActive(mContext);
@@ -313,6 +313,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/** /**
* Close an open handle menu if input is outside of menu coordinates * Close an open handle menu if input is outside of menu coordinates
*
* @param ev the tapped point to compare against * @param ev the tapped point to compare against
*/ */
void closeHandleMenuIfNeeded(MotionEvent ev) { void closeHandleMenuIfNeeded(MotionEvent ev) {
@@ -329,6 +330,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/** /**
* Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption * Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
*
* @param ev the {@link MotionEvent} to offset * @param ev the {@link MotionEvent} to offset
* @return the point of the input in local space * @return the point of the input in local space
*/ */
@@ -343,6 +345,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/** /**
* Determine if a passed MotionEvent is in a view in caption * Determine if a passed MotionEvent is in a view in caption
*
* @param ev the {@link MotionEvent} to check * @param ev the {@link MotionEvent} to check
* @param layoutId the id of the view * @param layoutId the id of the view
* @return {@code true} if event is inside the specified view, {@code false} if not * @return {@code true} if event is inside the specified view, {@code false} if not
@@ -363,6 +366,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
* Check a passed MotionEvent if a click has occurred on any button on this caption * Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible * Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer) * (i.e. the button was clicked through status bar layer)
*
* @param ev the MotionEvent to compare * @param ev the MotionEvent to compare
*/ */
void checkClickEvent(MotionEvent ev) { void checkClickEvent(MotionEvent ev) {
@@ -399,4 +403,27 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
closeHandleMenu(); closeHandleMenu();
super.close(); super.close();
} }
static class Factory {
CaptionWindowDecoration create(
Context context,
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Handler handler,
Choreographer choreographer,
SyncTransactionQueue syncQueue) {
return new CaptionWindowDecoration(
context,
displayController,
taskOrganizer,
taskInfo,
taskSurface,
handler,
choreographer,
syncQueue);
}
}
} }

View File

@@ -28,7 +28,6 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
* servers. * servers.
*/ */
public interface WindowDecorViewModel { public interface WindowDecorViewModel {
/** /**
* Sets the transition starter that starts freeform task transitions. * Sets the transition starter that starts freeform task transitions.
* *
@@ -37,8 +36,8 @@ public interface WindowDecorViewModel {
void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter); void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
/** /**
* Creates a window decoration for the given task. * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
* Can be {@code null} for Fullscreen tasks but not Freeform ones. * not Freeform ones.
* *
* @param taskInfo the initial task info of the task * @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task * @param taskSurface the surface of the task
@@ -46,7 +45,7 @@ public interface WindowDecorViewModel {
* @param finishT the finish transaction to restore states after the transition * @param finishT the finish transaction to restore states after the transition
* @return {@code true} if window decoration was created, {@code false} otherwise * @return {@code true} if window decoration was created, {@code false} otherwise
*/ */
boolean createWindowDecoration( boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface, SurfaceControl taskSurface,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
@@ -54,7 +53,7 @@ public interface WindowDecorViewModel {
/** /**
* Notifies a task info update on the given task, with the window decoration created previously * Notifies a task info update on the given task, with the window decoration created previously
* for this task by {@link #createWindowDecoration}. * for this task by {@link #onTaskOpening}.
* *
* @param taskInfo the new task info of the task * @param taskInfo the new task info of the task
*/ */
@@ -62,13 +61,29 @@ public interface WindowDecorViewModel {
/** /**
* Notifies a transition is about to start about the given task to give the window decoration a * Notifies a transition is about to start about the given task to give the window decoration a
* chance to prepare for this transition. * chance to prepare for this transition. Unlike {@link #onTaskInfoChanged}, this method creates
* a window decoration if one does not exist but is required.
* *
* @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition * @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition * @param finishT the finish transaction to restore states after the transition
* @return {@code true} if window decoration exists, {@code false} otherwise
*/ */
boolean setupWindowDecorationForTransition( void onTaskChanging(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
/**
* Notifies that the given task is about to close to give the window decoration a chance to
* prepare for this transition.
*
* @param taskInfo the initial task info of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
*/
void onTaskClosing(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT); SurfaceControl.Transaction finishT);
@@ -77,7 +92,6 @@ public interface WindowDecorViewModel {
* Destroys the window decoration of the give task. * Destroys the window decoration of the give task.
* *
* @param taskInfo the info of the task * @param taskInfo the info of the task
* @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/ */
boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo); void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
} }

View File

@@ -103,7 +103,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
verify(mWindowDecorViewModel).createWindowDecoration( verify(mWindowDecorViewModel).onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT); change.getTaskInfo(), change.getLeash(), startT, finishT);
} }
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
verify(mWindowDecorViewModel).setupWindowDecorationForTransition( verify(mWindowDecorViewModel).onTaskClosing(
change.getTaskInfo(), startT, finishT); change.getTaskInfo(), startT, finishT);
} }

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2022 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.windowdecor;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.view.Choreographer;
import android.view.Display;
import android.view.InputChannel;
import android.view.InputMonitor;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import androidx.test.rule.GrantPermissionRule;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeController;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
/** Tests of {@link CaptionWindowDecorViewModel} */
@SmallTest
public class CaptionWindowDecorViewModelTests extends ShellTestCase {
@Mock private CaptionWindowDecoration mCaptionWindowDecoration;
@Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
@Mock private Handler mMainHandler;
@Mock private Choreographer mMainChoreographer;
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private DesktopModeController mDesktopModeController;
@Mock private InputMonitor mInputMonitor;
@Mock private InputChannel mInputChannel;
@Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory;
@Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver;
@Mock private InputManager mInputManager;
private final List<InputManager> mMockInputManagers = new ArrayList<>();
private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel;
@Before
public void setUp() {
mMockInputManagers.add(mInputManager);
mCaptionWindowDecorViewModel =
new CaptionWindowDecorViewModel(
mContext,
mMainHandler,
mMainChoreographer,
mTaskOrganizer,
mDisplayController,
mSyncQueue,
mDesktopModeController,
mCaptionWindowDecorFactory,
new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
doReturn(mCaptionWindowDecoration)
.when(mCaptionWindowDecorFactory)
.create(any(), any(), any(), any(), any(), any(), any(), any());
when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver);
when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel);
}
@Test
public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
Looper.prepare();
final int taskId = 1;
final ActivityManager.RunningTaskInfo taskInfo =
createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
SurfaceControl surfaceControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT);
mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT);
verify(mCaptionWindowDecorFactory)
.create(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
surfaceControl,
mMainHandler,
mMainChoreographer,
mSyncQueue);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
verify(mCaptionWindowDecoration).close();
}
@Test
public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
final int taskId = 1;
final ActivityManager.RunningTaskInfo taskInfo =
createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED);
SurfaceControl surfaceControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
verify(mCaptionWindowDecorFactory, never())
.create(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
surfaceControl,
mMainHandler,
mMainChoreographer,
mSyncQueue);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
verify(mCaptionWindowDecorFactory)
.create(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
surfaceControl,
mMainHandler,
mMainChoreographer,
mSyncQueue);
}
private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
ActivityManager.RunningTaskInfo taskInfo =
new TestRunningTaskInfoBuilder()
.setDisplayId(Display.DEFAULT_DISPLAY)
.setVisible(true)
.build();
taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
}
private static class MockObjectSupplier<T> implements Supplier<T> {
private final List<T> mObjects;
private final Supplier<T> mDefaultSupplier;
private int mNumOfCalls = 0;
private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
mObjects = objects;
mDefaultSupplier = defaultSupplier;
}
@Override
public T get() {
final T mock =
mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls)
: mDefaultSupplier.get();
++mNumOfCalls;
return mock;
}
}
}