Merge "Add double-tap to quick resize desktop tasks" into udc-qpr-dev am: 6b8fc86902

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23595716

Change-Id: If095852acc3d77f0e11a1ecea31d7e2644c97d3f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jorge Gil
2023-06-09 16:40:37 +00:00
committed by Automerger Merge Worker
12 changed files with 368 additions and 56 deletions

View File

@@ -57,6 +57,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -690,6 +691,7 @@ public abstract class WMShellModule {
Transitions transitions,
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
@ShellMainThread ShellExecutor mainExecutor
@@ -697,7 +699,8 @@ public abstract class WMShellModule {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
desktopModeTaskRepository, launchAdjacentController, mainExecutor);
toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
launchAdjacentController, mainExecutor);
}
@WMSingleton
@@ -707,6 +710,13 @@ public abstract class WMShellModule {
return new EnterDesktopTaskTransitionHandler(transitions);
}
@WMSingleton
@Provides
static ToggleResizeDesktopTaskTransitionHandler provideToggleResizeDesktopTaskTransitionHandler(
Transitions transitions) {
return new ToggleResizeDesktopTaskTransitionHandler(transitions);
}
@WMSingleton
@Provides
static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(

View File

@@ -31,6 +31,7 @@ import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
import android.util.DisplayMetrics.DENSITY_DEFAULT
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
@@ -78,6 +79,8 @@ class DesktopTasksController(
private val transitions: Transitions,
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
private val toggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
@ShellMainThread private val mainExecutor: ShellExecutor
@@ -382,6 +385,49 @@ class DesktopTasksController(
}
}
/** Quick-resizes a desktop task, toggling between the stable bounds and the default bounds. */
fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo, windowDecor: DesktopModeWindowDecoration) {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
val stableBounds = Rect()
displayLayout.getStableBounds(stableBounds)
val destinationBounds = Rect()
if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
// The desktop task is currently occupying the whole stable bounds, toggle to the
// default bounds.
getDefaultDesktopTaskBounds(
density = taskInfo.configuration.densityDpi.toFloat() / DENSITY_DEFAULT,
stableBounds = stableBounds,
outBounds = destinationBounds
)
} else {
// Toggle to the stable bounds.
destinationBounds.set(stableBounds)
}
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
toggleResizeDesktopTaskTransitionHandler.startTransition(
wct,
taskInfo.taskId,
windowDecor
)
} else {
shellTaskOrganizer.applyTransaction(wct)
}
}
private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) {
val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt()
val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt()
outBounds.set(0, 0, width, height)
// Center the task in stable bounds
outBounds.offset(
stableBounds.centerX() - outBounds.centerX(),
stableBounds.centerY() - outBounds.centerY()
)
}
/**
* Get windowing move for a given `taskId`
*
@@ -883,6 +929,14 @@ class DesktopTasksController(
SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0)
private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
// Override default freeform task width when desktop mode is enabled. In dips.
private val DESKTOP_MODE_DEFAULT_WIDTH_DP =
SystemProperties.getInt("persist.wm.debug.desktop_mode.default_width", 840)
// Override default freeform task height when desktop mode is enabled. In dips.
private val DESKTOP_MODE_DEFAULT_HEIGHT_DP =
SystemProperties.getInt("persist.wm.debug.desktop_mode.default_height", 630)
/**
* Check if desktop density override is enabled
*/

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2023 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.desktopmode
import android.animation.Animator
import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.graphics.Rect
import android.os.IBinder
import android.util.SparseArray
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.core.animation.addListener
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import java.util.function.Supplier
/** Handles the animation of quick resizing of desktop tasks. */
class ToggleResizeDesktopTaskTransitionHandler(
private val transitions: Transitions,
private val transactionSupplier: Supplier<SurfaceControl.Transaction>
) : Transitions.TransitionHandler {
private val rectEvaluator = RectEvaluator(Rect())
private val taskToDecorationMap = SparseArray<DesktopModeWindowDecoration>()
private var boundsAnimator: Animator? = null
constructor(
transitions: Transitions
) : this(transitions, Supplier { SurfaceControl.Transaction() })
/** Starts a quick resize transition. */
fun startTransition(
wct: WindowContainerTransaction,
taskId: Int,
windowDecoration: DesktopModeWindowDecoration
) {
// Pause relayout until the transition animation finishes.
windowDecoration.incrementRelayoutBlock()
transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this)
taskToDecorationMap.put(taskId, windowDecoration)
}
override fun startAnimation(
transition: IBinder,
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
finishCallback: Transitions.TransitionFinishCallback
): Boolean {
val change = findRelevantChange(info)
val leash = change.leash
val taskId = change.taskInfo.taskId
val startBounds = change.startAbsBounds
val endBounds = change.endAbsBounds
val windowDecor =
taskToDecorationMap.removeReturnOld(taskId)
?: throw IllegalStateException("Window decoration not found for task $taskId")
val tx = transactionSupplier.get()
boundsAnimator?.cancel()
boundsAnimator =
ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds)
.setDuration(RESIZE_DURATION_MS)
.apply {
addListener(
onStart = {
startTransaction
.setPosition(
leash,
startBounds.left.toFloat(),
startBounds.top.toFloat()
)
.setWindowCrop(leash, startBounds.width(), startBounds.height())
.show(leash)
windowDecor.showResizeVeil(startTransaction, startBounds)
},
onEnd = {
finishTransaction
.setPosition(
leash,
endBounds.left.toFloat(),
endBounds.top.toFloat()
)
.setWindowCrop(leash, endBounds.width(), endBounds.height())
.show(leash)
windowDecor.hideResizeVeil()
finishCallback.onTransitionFinished(null, null)
boundsAnimator = null
}
)
addUpdateListener { anim ->
val rect = anim.animatedValue as Rect
tx.setPosition(leash, rect.left.toFloat(), rect.top.toFloat())
.setWindowCrop(leash, rect.width(), rect.height())
.show(leash)
windowDecor.updateResizeVeil(tx, rect)
}
start()
}
return true
}
override fun handleRequest(
transition: IBinder,
request: TransitionRequestInfo
): WindowContainerTransaction? {
return null
}
private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
val matchingChanges =
info.changes.filter { c ->
!isWallpaper(c) && isValidTaskChange(c) && c.mode == TRANSIT_CHANGE
}
if (matchingChanges.size != 1) {
throw IllegalStateException(
"Expected 1 relevant change but found: ${matchingChanges.size}"
)
}
return matchingChanges.first()
}
private fun isWallpaper(change: TransitionInfo.Change): Boolean {
return (change.flags and TransitionInfo.FLAG_IS_WALLPAPER) != 0
}
private fun isValidTaskChange(change: TransitionInfo.Change): Boolean {
return change.taskInfo != null && change.taskInfo?.taskId != -1
}
companion object {
private const val RESIZE_DURATION_MS = 300L
}
}

View File

@@ -161,6 +161,10 @@ public class Transitions implements RemoteCallable<Transitions>,
public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
/** Transition type to animate the toggle resize between the max and default desktop sizes. */
public static final int TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE =
WindowManager.TRANSIT_FIRST_CUSTOM + 14;
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;

View File

@@ -33,6 +33,8 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -258,7 +260,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
* @return {@code true} if a drag is happening; or {@code false} if it is not
*/
@Override
public boolean handleMotionEvent(MotionEvent e) {
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return false;

View File

@@ -43,6 +43,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.GestureDetector;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
@@ -198,7 +199,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (change.getMode() == WindowManager.TRANSIT_CHANGE
&& (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE)) {
|| info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) {
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
.addTransitionPausingRelayout(transition);
}
@@ -279,15 +281,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
private class DesktopModeTouchEventListener implements
View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
private final GestureDetector mGestureDetector;
private boolean mIsDragging;
private boolean mShouldClick;
private int mDragPointerId = -1;
private DesktopModeTouchEventListener(
@@ -297,6 +301,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mTaskToken = taskInfo.token;
mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
mGestureDetector = new GestureDetector(mContext, this);
}
@Override
@@ -357,7 +362,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return false;
}
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
return mDragDetector.onMotionEvent(e);
return mDragDetector.onMotionEvent(v, e);
}
private void moveTaskToFront(RunningTaskInfo taskInfo) {
@@ -372,7 +377,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* @return {@code true} if the motion event is handled.
*/
@Override
public boolean handleMotionEvent(MotionEvent e) {
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
@@ -383,6 +388,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
== WINDOWING_MODE_FULLSCREEN) {
return false;
}
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
@@ -390,7 +398,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
0 /* ctrlType */, e.getRawX(0),
e.getRawY(0));
mIsDragging = false;
return false;
mShouldClick = true;
return true;
}
case MotionEvent.ACTION_MOVE: {
final DesktopModeWindowDecoration decoration =
@@ -404,10 +413,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mIsDragging = true;
mShouldClick = false;
return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
final boolean wasDragging = mIsDragging;
if (!wasDragging) {
if (mShouldClick && v != null) {
v.performClick();
mShouldClick = false;
return true;
}
return false;
}
if (e.findPointerIndex(mDragPointerId) == -1) {
mDragPointerId = e.getPointerId(0);
}
@@ -422,13 +441,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
position, e.getRawY(), mWindowDecorByTaskId.get(mTaskId)));
final boolean wasDragging = mIsDragging;
mIsDragging = false;
return wasDragging;
return true;
}
}
return true;
}
@Override
public boolean onDoubleTap(@NonNull MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
mDesktopTasksController.ifPresent(c -> {
c.toggleDesktopTaskSize(taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId));
});
return true;
}
}
// InputEventReceiver to listen for touch input outside of caption bounds
@@ -813,6 +840,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
windowDecoration.createResizeVeil();
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
windowDecoration, taskInfo);
@@ -843,7 +871,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
mTransactionFactory);
} else {
windowDecoration.createResizeVeil();
return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
mTransitions);

View File

@@ -294,23 +294,37 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
* Fade in the resize veil
* Show the resize veil.
*/
void showResizeVeil(Rect taskBounds) {
public void showResizeVeil(Rect taskBounds) {
mResizeVeil.showVeil(mTaskSurface, taskBounds);
}
/**
* Show the resize veil.
*/
public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) {
mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */);
}
/**
* Set new bounds for the resize veil
*/
void updateResizeVeil(Rect newBounds) {
public void updateResizeVeil(Rect newBounds) {
mResizeVeil.updateResizeVeil(newBounds);
}
/**
* Set new bounds for the resize veil
*/
public void updateResizeVeil(SurfaceControl.Transaction tx, Rect newBounds) {
mResizeVeil.updateResizeVeil(tx, newBounds);
}
/**
* Fade the resize veil out.
*/
void hideResizeVeil() {
public void hideResizeVeil() {
mResizeVeil.hideVeil();
}

View File

@@ -24,6 +24,9 @@ import static android.view.MotionEvent.ACTION_UP;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
/**
* A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
@@ -54,14 +57,24 @@ class DragDetector {
*
* @return the result returned by {@link #mEventHandler}, or the result when
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
*/
*/
boolean onMotionEvent(MotionEvent ev) {
return onMotionEvent(null /* view */, ev);
}
/**
* The receiver of the {@link MotionEvent} flow.
*
* @return the result returned by {@link #mEventHandler}, or the result when
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
*/
boolean onMotionEvent(View v, MotionEvent ev) {
final boolean isTouchScreen =
(ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
if (!isTouchScreen) {
// Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
// to take the slop threshold into consideration.
return mEventHandler.handleMotionEvent(ev);
return mEventHandler.handleMotionEvent(v, ev);
}
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
@@ -69,7 +82,7 @@ class DragDetector {
float rawX = ev.getRawX(0);
float rawY = ev.getRawY(0);
mInputDownPoint.set(rawX, rawY);
mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
mResultOfDownAction = mEventHandler.handleMotionEvent(v, ev);
return mResultOfDownAction;
}
case ACTION_MOVE: {
@@ -87,7 +100,7 @@ class DragDetector {
// The event handler should only be notified about 'move' events if a drag has been
// detected.
if (mIsDragEvent) {
return mEventHandler.handleMotionEvent(ev);
return mEventHandler.handleMotionEvent(v, ev);
} else {
return mResultOfDownAction;
}
@@ -95,10 +108,10 @@ class DragDetector {
case ACTION_UP:
case ACTION_CANCEL: {
resetState();
return mEventHandler.handleMotionEvent(ev);
return mEventHandler.handleMotionEvent(v, ev);
}
default:
return mEventHandler.handleMotionEvent(ev);
return mEventHandler.handleMotionEvent(v, ev);
}
}
@@ -114,6 +127,6 @@ class DragDetector {
}
interface MotionEventHandler {
boolean handleMotionEvent(MotionEvent ev);
boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
}
}

View File

@@ -42,6 +42,7 @@ import android.view.InputEventReceiver;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
@@ -303,7 +304,7 @@ class DragResizeInputListener implements AutoCloseable {
}
@Override
public boolean handleMotionEvent(MotionEvent e) {
public boolean handleMotionEvent(View v, MotionEvent e) {
boolean result = false;
// Check if this is a touch event vs mouse event.
// Touch events are tracked in four corners. Other events are tracked in resize edges.

View File

@@ -102,11 +102,17 @@ public class ResizeVeil {
}
/**
* Animate veil's alpha to 1, fading it in.
* Shows the veil surface/view.
*
* @param t the transaction to apply in sync with the veil draw
* @param parentSurface the surface that the veil should be a child of
* @param taskBounds the bounds of the task that owns the veil
* @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown
* immediately
*/
public void showVeil(SurfaceControl parentSurface, Rect taskBounds) {
public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface,
Rect taskBounds, boolean fadeIn) {
// Parent surface can change, ensure it is up to date.
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
if (!parentSurface.equals(mParentSurface)) {
t.reparent(mVeilSurface, parentSurface);
mParentSurface = parentSurface;
@@ -115,21 +121,35 @@ public class ResizeVeil {
int backgroundColorId = getBackgroundColorId();
mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId));
final ValueAnimator animator = new ValueAnimator();
animator.setFloatValues(0f, 1f);
animator.setDuration(RESIZE_ALPHA_DURATION);
animator.addUpdateListener(animation -> {
t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
t.apply();
});
relayout(taskBounds, t);
t.show(mVeilSurface)
.addTransactionCommittedListener(mContext.getMainExecutor(), () -> animator.start())
.setAlpha(mVeilSurface, 0);
if (fadeIn) {
final ValueAnimator animator = new ValueAnimator();
animator.setFloatValues(0f, 1f);
animator.setDuration(RESIZE_ALPHA_DURATION);
animator.addUpdateListener(animation -> {
t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
t.apply();
});
t.show(mVeilSurface)
.addTransactionCommittedListener(
mContext.getMainExecutor(), () -> animator.start())
.setAlpha(mVeilSurface, 0);
} else {
// Show the veil immediately at full opacity.
t.show(mVeilSurface).setAlpha(mVeilSurface, 1);
}
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}
/**
* Animate veil's alpha to 1, fading it in.
*/
public void showVeil(SurfaceControl parentSurface, Rect taskBounds) {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
showVeil(t, parentSurface, taskBounds, true /* fadeIn */);
}
/**
* Update veil bounds to match bounds changes.
* @param newBounds bounds to update veil to.
@@ -147,6 +167,16 @@ public class ResizeVeil {
*/
public void updateResizeVeil(Rect newBounds) {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
updateResizeVeil(t, newBounds);
}
/**
* Calls relayout to update task and veil bounds.
*
* @param t a transaction to be applied in sync with the veil draw.
* @param newBounds bounds to update veil to.
*/
public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) {
relayout(newBounds, t);
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}

View File

@@ -89,6 +89,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var transitions: Transitions
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
@Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
private lateinit var mockitoSession: StaticMockitoSession
@@ -129,6 +131,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
transitions,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
mToggleResizeDesktopTaskTransitionHandler,
desktopModeTaskRepository,
launchAdjacentController,
shellExecutor

View File

@@ -55,7 +55,7 @@ class DragDetectorTest {
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
`when`(eventHandler.handleMotionEvent(any(), any())).thenReturn(true)
dragDetector = DragDetector(eventHandler)
dragDetector.setTouchSlop(SLOP)
@@ -72,13 +72,13 @@ class DragDetectorTest {
@Test
fun testNoMove_passesDownAndUp() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -86,12 +86,12 @@ class DragDetectorTest {
@Test
fun testMoveInSlop_touch_passesDownAndUp() {
`when`(eventHandler.handleMotionEvent(argThat {
`when`(eventHandler.handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -99,12 +99,12 @@ class DragDetectorTest {
val newX = X + SLOP - 1
assertFalse(
dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
verify(eventHandler, never()).handleMotionEvent(argThat {
verify(eventHandler, never()).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -112,13 +112,13 @@ class DragDetectorTest {
@Test
fun testMoveInSlop_mouse_passesDownMoveAndUp() {
`when`(eventHandler.handleMotionEvent(argThat {
`when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
@@ -126,14 +126,14 @@ class DragDetectorTest {
val newX = X + SLOP - 1
assertTrue(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
assertTrue(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
@@ -141,25 +141,25 @@ class DragDetectorTest {
@Test
fun testMoveBeyondSlop_passesDownMoveAndUp() {
`when`(eventHandler.handleMotionEvent(argThat {
`when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
val newX = X + SLOP + 1
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -167,12 +167,12 @@ class DragDetectorTest {
@Test
fun testPassesHoverEnter() {
`when`(eventHandler.handleMotionEvent(argThat {
`when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_HOVER_ENTER
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
})
}
@@ -180,7 +180,7 @@ class DragDetectorTest {
@Test
fun testPassesHoverMove() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
})
}
@@ -188,7 +188,7 @@ class DragDetectorTest {
@Test
fun testPassesHoverExit() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
verify(eventHandler).handleMotionEvent(argThat {
verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
})
}