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:
@@ -57,6 +57,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
|||||||
import com.android.wm.shell.desktopmode.DesktopTasksController;
|
import com.android.wm.shell.desktopmode.DesktopTasksController;
|
||||||
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
|
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
|
||||||
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
|
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.draganddrop.DragAndDropController;
|
||||||
import com.android.wm.shell.freeform.FreeformComponents;
|
import com.android.wm.shell.freeform.FreeformComponents;
|
||||||
import com.android.wm.shell.freeform.FreeformTaskListener;
|
import com.android.wm.shell.freeform.FreeformTaskListener;
|
||||||
@@ -690,6 +691,7 @@ public abstract class WMShellModule {
|
|||||||
Transitions transitions,
|
Transitions transitions,
|
||||||
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
|
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
|
||||||
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
|
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
|
||||||
|
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
|
||||||
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
|
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
|
||||||
LaunchAdjacentController launchAdjacentController,
|
LaunchAdjacentController launchAdjacentController,
|
||||||
@ShellMainThread ShellExecutor mainExecutor
|
@ShellMainThread ShellExecutor mainExecutor
|
||||||
@@ -697,7 +699,8 @@ public abstract class WMShellModule {
|
|||||||
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
|
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
|
||||||
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
|
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
|
||||||
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
|
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
|
||||||
desktopModeTaskRepository, launchAdjacentController, mainExecutor);
|
toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
|
||||||
|
launchAdjacentController, mainExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@@ -707,6 +710,13 @@ public abstract class WMShellModule {
|
|||||||
return new EnterDesktopTaskTransitionHandler(transitions);
|
return new EnterDesktopTaskTransitionHandler(transitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static ToggleResizeDesktopTaskTransitionHandler provideToggleResizeDesktopTaskTransitionHandler(
|
||||||
|
Transitions transitions) {
|
||||||
|
return new ToggleResizeDesktopTaskTransitionHandler(transitions);
|
||||||
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
|
static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.graphics.Rect
|
|||||||
import android.graphics.Region
|
import android.graphics.Region
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.SystemProperties
|
import android.os.SystemProperties
|
||||||
|
import android.util.DisplayMetrics.DENSITY_DEFAULT
|
||||||
import android.view.SurfaceControl
|
import android.view.SurfaceControl
|
||||||
import android.view.WindowManager.TRANSIT_CHANGE
|
import android.view.WindowManager.TRANSIT_CHANGE
|
||||||
import android.view.WindowManager.TRANSIT_NONE
|
import android.view.WindowManager.TRANSIT_NONE
|
||||||
@@ -78,6 +79,8 @@ class DesktopTasksController(
|
|||||||
private val transitions: Transitions,
|
private val transitions: Transitions,
|
||||||
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
|
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
|
||||||
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
|
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
|
||||||
|
private val toggleResizeDesktopTaskTransitionHandler:
|
||||||
|
ToggleResizeDesktopTaskTransitionHandler,
|
||||||
private val desktopModeTaskRepository: DesktopModeTaskRepository,
|
private val desktopModeTaskRepository: DesktopModeTaskRepository,
|
||||||
private val launchAdjacentController: LaunchAdjacentController,
|
private val launchAdjacentController: LaunchAdjacentController,
|
||||||
@ShellMainThread private val mainExecutor: ShellExecutor
|
@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`
|
* Get windowing move for a given `taskId`
|
||||||
*
|
*
|
||||||
@@ -883,6 +929,14 @@ class DesktopTasksController(
|
|||||||
SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0)
|
SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0)
|
||||||
private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
|
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
|
* Check if desktop density override is enabled
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,6 +161,10 @@ public class Transitions implements RemoteCallable<Transitions>,
|
|||||||
public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
|
public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
|
||||||
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
|
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 WindowOrganizer mOrganizer;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ShellExecutor mMainExecutor;
|
private final ShellExecutor mMainExecutor;
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import android.window.TransitionInfo;
|
|||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
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
|
* @return {@code true} if a drag is happening; or {@code false} if it is not
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean handleMotionEvent(MotionEvent e) {
|
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
|
||||||
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import android.os.IBinder;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
|
import android.view.GestureDetector;
|
||||||
import android.view.InputChannel;
|
import android.view.InputChannel;
|
||||||
import android.view.InputEvent;
|
import android.view.InputEvent;
|
||||||
import android.view.InputEventReceiver;
|
import android.view.InputEventReceiver;
|
||||||
@@ -198,7 +199,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
if (change.getMode() == WindowManager.TRANSIT_CHANGE
|
if (change.getMode() == WindowManager.TRANSIT_CHANGE
|
||||||
&& (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE
|
&& (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE
|
||||||
|| info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_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)
|
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
|
||||||
.addTransitionPausingRelayout(transition);
|
.addTransitionPausingRelayout(transition);
|
||||||
}
|
}
|
||||||
@@ -279,15 +281,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DesktopModeTouchEventListener implements
|
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
|
||||||
View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
|
implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
|
||||||
|
|
||||||
private final int mTaskId;
|
private final int mTaskId;
|
||||||
private final WindowContainerToken mTaskToken;
|
private final WindowContainerToken mTaskToken;
|
||||||
private final DragPositioningCallback mDragPositioningCallback;
|
private final DragPositioningCallback mDragPositioningCallback;
|
||||||
private final DragDetector mDragDetector;
|
private final DragDetector mDragDetector;
|
||||||
|
private final GestureDetector mGestureDetector;
|
||||||
|
|
||||||
private boolean mIsDragging;
|
private boolean mIsDragging;
|
||||||
|
private boolean mShouldClick;
|
||||||
private int mDragPointerId = -1;
|
private int mDragPointerId = -1;
|
||||||
|
|
||||||
private DesktopModeTouchEventListener(
|
private DesktopModeTouchEventListener(
|
||||||
@@ -297,6 +301,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
mTaskToken = taskInfo.token;
|
mTaskToken = taskInfo.token;
|
||||||
mDragPositioningCallback = dragPositioningCallback;
|
mDragPositioningCallback = dragPositioningCallback;
|
||||||
mDragDetector = new DragDetector(this);
|
mDragDetector = new DragDetector(this);
|
||||||
|
mGestureDetector = new GestureDetector(mContext, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -357,7 +362,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
|
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
|
||||||
return mDragDetector.onMotionEvent(e);
|
return mDragDetector.onMotionEvent(v, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveTaskToFront(RunningTaskInfo taskInfo) {
|
private void moveTaskToFront(RunningTaskInfo taskInfo) {
|
||||||
@@ -372,7 +377,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
* @return {@code true} if the motion event is handled.
|
* @return {@code true} if the motion event is handled.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean handleMotionEvent(MotionEvent e) {
|
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
|
||||||
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||||
if (DesktopModeStatus.isProto2Enabled()
|
if (DesktopModeStatus.isProto2Enabled()
|
||||||
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
||||||
@@ -383,6 +388,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
== WINDOWING_MODE_FULLSCREEN) {
|
== WINDOWING_MODE_FULLSCREEN) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (mGestureDetector.onTouchEvent(e)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
switch (e.getActionMasked()) {
|
switch (e.getActionMasked()) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
mDragPointerId = e.getPointerId(0);
|
mDragPointerId = e.getPointerId(0);
|
||||||
@@ -390,7 +398,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
0 /* ctrlType */, e.getRawX(0),
|
0 /* ctrlType */, e.getRawX(0),
|
||||||
e.getRawY(0));
|
e.getRawY(0));
|
||||||
mIsDragging = false;
|
mIsDragging = false;
|
||||||
return false;
|
mShouldClick = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
case MotionEvent.ACTION_MOVE: {
|
case MotionEvent.ACTION_MOVE: {
|
||||||
final DesktopModeWindowDecoration decoration =
|
final DesktopModeWindowDecoration decoration =
|
||||||
@@ -404,10 +413,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
mDragPositioningCallback.onDragPositioningMove(
|
mDragPositioningCallback.onDragPositioningMove(
|
||||||
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
||||||
mIsDragging = true;
|
mIsDragging = true;
|
||||||
|
mShouldClick = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
case MotionEvent.ACTION_CANCEL: {
|
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) {
|
if (e.findPointerIndex(mDragPointerId) == -1) {
|
||||||
mDragPointerId = e.getPointerId(0);
|
mDragPointerId = e.getPointerId(0);
|
||||||
}
|
}
|
||||||
@@ -422,13 +441,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
||||||
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
|
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
|
||||||
position, e.getRawY(), mWindowDecorByTaskId.get(mTaskId)));
|
position, e.getRawY(), mWindowDecorByTaskId.get(mTaskId)));
|
||||||
final boolean wasDragging = mIsDragging;
|
|
||||||
mIsDragging = false;
|
mIsDragging = false;
|
||||||
return wasDragging;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
// InputEventReceiver to listen for touch input outside of caption bounds
|
||||||
@@ -813,6 +840,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
mMainChoreographer,
|
mMainChoreographer,
|
||||||
mSyncQueue);
|
mSyncQueue);
|
||||||
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
|
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
|
||||||
|
windowDecoration.createResizeVeil();
|
||||||
|
|
||||||
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
|
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
|
||||||
windowDecoration, taskInfo);
|
windowDecoration, taskInfo);
|
||||||
@@ -843,7 +871,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
|
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
|
||||||
mTransactionFactory);
|
mTransactionFactory);
|
||||||
} else {
|
} else {
|
||||||
windowDecoration.createResizeVeil();
|
|
||||||
return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
|
return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
|
||||||
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
|
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
|
||||||
mTransitions);
|
mTransitions);
|
||||||
|
|||||||
@@ -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);
|
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
|
* Set new bounds for the resize veil
|
||||||
*/
|
*/
|
||||||
void updateResizeVeil(Rect newBounds) {
|
public void updateResizeVeil(Rect newBounds) {
|
||||||
mResizeVeil.updateResizeVeil(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.
|
* Fade the resize veil out.
|
||||||
*/
|
*/
|
||||||
void hideResizeVeil() {
|
public void hideResizeVeil() {
|
||||||
mResizeVeil.hideVeil();
|
mResizeVeil.hideVeil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import static android.view.MotionEvent.ACTION_UP;
|
|||||||
|
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.view.MotionEvent;
|
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
|
* 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
|
* @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
|
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
|
||||||
*/
|
*/
|
||||||
boolean onMotionEvent(MotionEvent ev) {
|
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 =
|
final boolean isTouchScreen =
|
||||||
(ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
|
(ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
|
||||||
if (!isTouchScreen) {
|
if (!isTouchScreen) {
|
||||||
// Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
|
// Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
|
||||||
// to take the slop threshold into consideration.
|
// to take the slop threshold into consideration.
|
||||||
return mEventHandler.handleMotionEvent(ev);
|
return mEventHandler.handleMotionEvent(v, ev);
|
||||||
}
|
}
|
||||||
switch (ev.getActionMasked()) {
|
switch (ev.getActionMasked()) {
|
||||||
case ACTION_DOWN: {
|
case ACTION_DOWN: {
|
||||||
@@ -69,7 +82,7 @@ class DragDetector {
|
|||||||
float rawX = ev.getRawX(0);
|
float rawX = ev.getRawX(0);
|
||||||
float rawY = ev.getRawY(0);
|
float rawY = ev.getRawY(0);
|
||||||
mInputDownPoint.set(rawX, rawY);
|
mInputDownPoint.set(rawX, rawY);
|
||||||
mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
|
mResultOfDownAction = mEventHandler.handleMotionEvent(v, ev);
|
||||||
return mResultOfDownAction;
|
return mResultOfDownAction;
|
||||||
}
|
}
|
||||||
case ACTION_MOVE: {
|
case ACTION_MOVE: {
|
||||||
@@ -87,7 +100,7 @@ class DragDetector {
|
|||||||
// The event handler should only be notified about 'move' events if a drag has been
|
// The event handler should only be notified about 'move' events if a drag has been
|
||||||
// detected.
|
// detected.
|
||||||
if (mIsDragEvent) {
|
if (mIsDragEvent) {
|
||||||
return mEventHandler.handleMotionEvent(ev);
|
return mEventHandler.handleMotionEvent(v, ev);
|
||||||
} else {
|
} else {
|
||||||
return mResultOfDownAction;
|
return mResultOfDownAction;
|
||||||
}
|
}
|
||||||
@@ -95,10 +108,10 @@ class DragDetector {
|
|||||||
case ACTION_UP:
|
case ACTION_UP:
|
||||||
case ACTION_CANCEL: {
|
case ACTION_CANCEL: {
|
||||||
resetState();
|
resetState();
|
||||||
return mEventHandler.handleMotionEvent(ev);
|
return mEventHandler.handleMotionEvent(v, ev);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return mEventHandler.handleMotionEvent(ev);
|
return mEventHandler.handleMotionEvent(v, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +127,6 @@ class DragDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface MotionEventHandler {
|
interface MotionEventHandler {
|
||||||
boolean handleMotionEvent(MotionEvent ev);
|
boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import android.view.InputEventReceiver;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.PointerIcon;
|
import android.view.PointerIcon;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.WindowManagerGlobal;
|
import android.view.WindowManagerGlobal;
|
||||||
|
|
||||||
@@ -303,7 +304,7 @@ class DragResizeInputListener implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleMotionEvent(MotionEvent e) {
|
public boolean handleMotionEvent(View v, MotionEvent e) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
// Check if this is a touch event vs mouse event.
|
// Check if this is a touch event vs mouse event.
|
||||||
// Touch events are tracked in four corners. Other events are tracked in resize edges.
|
// Touch events are tracked in four corners. Other events are tracked in resize edges.
|
||||||
|
|||||||
@@ -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.
|
// Parent surface can change, ensure it is up to date.
|
||||||
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
|
|
||||||
if (!parentSurface.equals(mParentSurface)) {
|
if (!parentSurface.equals(mParentSurface)) {
|
||||||
t.reparent(mVeilSurface, parentSurface);
|
t.reparent(mVeilSurface, parentSurface);
|
||||||
mParentSurface = parentSurface;
|
mParentSurface = parentSurface;
|
||||||
@@ -115,21 +121,35 @@ public class ResizeVeil {
|
|||||||
int backgroundColorId = getBackgroundColorId();
|
int backgroundColorId = getBackgroundColorId();
|
||||||
mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId));
|
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);
|
relayout(taskBounds, t);
|
||||||
t.show(mVeilSurface)
|
if (fadeIn) {
|
||||||
.addTransactionCommittedListener(mContext.getMainExecutor(), () -> animator.start())
|
final ValueAnimator animator = new ValueAnimator();
|
||||||
.setAlpha(mVeilSurface, 0);
|
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);
|
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.
|
* Update veil bounds to match bounds changes.
|
||||||
* @param newBounds bounds to update veil to.
|
* @param newBounds bounds to update veil to.
|
||||||
@@ -147,6 +167,16 @@ public class ResizeVeil {
|
|||||||
*/
|
*/
|
||||||
public void updateResizeVeil(Rect newBounds) {
|
public void updateResizeVeil(Rect newBounds) {
|
||||||
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
|
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);
|
relayout(newBounds, t);
|
||||||
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
|
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
|
|||||||
@Mock lateinit var transitions: Transitions
|
@Mock lateinit var transitions: Transitions
|
||||||
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
|
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
|
||||||
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
|
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
|
||||||
|
@Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
|
||||||
|
ToggleResizeDesktopTaskTransitionHandler
|
||||||
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
|
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
|
||||||
|
|
||||||
private lateinit var mockitoSession: StaticMockitoSession
|
private lateinit var mockitoSession: StaticMockitoSession
|
||||||
@@ -129,6 +131,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
|
|||||||
transitions,
|
transitions,
|
||||||
enterDesktopTransitionHandler,
|
enterDesktopTransitionHandler,
|
||||||
exitDesktopTransitionHandler,
|
exitDesktopTransitionHandler,
|
||||||
|
mToggleResizeDesktopTaskTransitionHandler,
|
||||||
desktopModeTaskRepository,
|
desktopModeTaskRepository,
|
||||||
launchAdjacentController,
|
launchAdjacentController,
|
||||||
shellExecutor
|
shellExecutor
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class DragDetectorTest {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
`when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
|
`when`(eventHandler.handleMotionEvent(any(), any())).thenReturn(true)
|
||||||
|
|
||||||
dragDetector = DragDetector(eventHandler)
|
dragDetector = DragDetector(eventHandler)
|
||||||
dragDetector.setTouchSlop(SLOP)
|
dragDetector.setTouchSlop(SLOP)
|
||||||
@@ -72,13 +72,13 @@ class DragDetectorTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testNoMove_passesDownAndUp() {
|
fun testNoMove_passesDownAndUp() {
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
|
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
@@ -86,12 +86,12 @@ class DragDetectorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMoveInSlop_touch_passesDownAndUp() {
|
fun testMoveInSlop_touch_passesDownAndUp() {
|
||||||
`when`(eventHandler.handleMotionEvent(argThat {
|
`when`(eventHandler.handleMotionEvent(any(), argThat {
|
||||||
return@argThat it.action == MotionEvent.ACTION_DOWN
|
return@argThat it.action == MotionEvent.ACTION_DOWN
|
||||||
})).thenReturn(false)
|
})).thenReturn(false)
|
||||||
|
|
||||||
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
@@ -99,12 +99,12 @@ class DragDetectorTest {
|
|||||||
val newX = X + SLOP - 1
|
val newX = X + SLOP - 1
|
||||||
assertFalse(
|
assertFalse(
|
||||||
dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
|
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
|
return@argThat it.action == MotionEvent.ACTION_MOVE
|
||||||
})
|
})
|
||||||
|
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
@@ -112,13 +112,13 @@ class DragDetectorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMoveInSlop_mouse_passesDownMoveAndUp() {
|
fun testMoveInSlop_mouse_passesDownMoveAndUp() {
|
||||||
`when`(eventHandler.handleMotionEvent(argThat {
|
`when`(eventHandler.handleMotionEvent(any(), argThat {
|
||||||
it.action == MotionEvent.ACTION_DOWN
|
it.action == MotionEvent.ACTION_DOWN
|
||||||
})).thenReturn(false)
|
})).thenReturn(false)
|
||||||
|
|
||||||
assertFalse(dragDetector.onMotionEvent(
|
assertFalse(dragDetector.onMotionEvent(
|
||||||
createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_MOUSE
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
})
|
})
|
||||||
@@ -126,14 +126,14 @@ class DragDetectorTest {
|
|||||||
val newX = X + SLOP - 1
|
val newX = X + SLOP - 1
|
||||||
assertTrue(dragDetector.onMotionEvent(
|
assertTrue(dragDetector.onMotionEvent(
|
||||||
createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_MOUSE
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
})
|
})
|
||||||
|
|
||||||
assertTrue(dragDetector.onMotionEvent(
|
assertTrue(dragDetector.onMotionEvent(
|
||||||
createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_MOUSE
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
})
|
})
|
||||||
@@ -141,25 +141,25 @@ class DragDetectorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMoveBeyondSlop_passesDownMoveAndUp() {
|
fun testMoveBeyondSlop_passesDownMoveAndUp() {
|
||||||
`when`(eventHandler.handleMotionEvent(argThat {
|
`when`(eventHandler.handleMotionEvent(any(), argThat {
|
||||||
it.action == MotionEvent.ACTION_DOWN
|
it.action == MotionEvent.ACTION_DOWN
|
||||||
})).thenReturn(false)
|
})).thenReturn(false)
|
||||||
|
|
||||||
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
|
|
||||||
val newX = X + SLOP + 1
|
val newX = X + SLOP + 1
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
|
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
|
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 &&
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
})
|
})
|
||||||
@@ -167,12 +167,12 @@ class DragDetectorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPassesHoverEnter() {
|
fun testPassesHoverEnter() {
|
||||||
`when`(eventHandler.handleMotionEvent(argThat {
|
`when`(eventHandler.handleMotionEvent(any(), argThat {
|
||||||
it.action == MotionEvent.ACTION_HOVER_ENTER
|
it.action == MotionEvent.ACTION_HOVER_ENTER
|
||||||
})).thenReturn(false)
|
})).thenReturn(false)
|
||||||
|
|
||||||
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
|
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
|
return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ class DragDetectorTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testPassesHoverMove() {
|
fun testPassesHoverMove() {
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
|
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
|
return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ class DragDetectorTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testPassesHoverExit() {
|
fun testPassesHoverExit() {
|
||||||
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
|
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
|
return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user