diff options
| author | 2023-06-09 16:32:13 +0000 | |
|---|---|---|
| committer | 2023-06-09 16:32:13 +0000 | |
| commit | 6b8fc869022777acd015cbd408a767e8634f1a2c (patch) | |
| tree | 5cf060df3ec4aea822c20db9339befd815cff28f | |
| parent | aae87b9485629fd876903bbabba6443bae21fc65 (diff) | |
| parent | 2145b82abf16fe8f6d7ec35a577bbdcee080455b (diff) | |
Merge "Add double-tap to quick resize desktop tasks" into udc-qpr-dev
12 files changed, 368 insertions, 56 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 70e9079dc572..ec4024418860 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -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 @@ -709,6 +712,13 @@ public abstract class WMShellModule { @WMSingleton @Provides + static ToggleResizeDesktopTaskTransitionHandler provideToggleResizeDesktopTaskTransitionHandler( + Transitions transitions) { + return new ToggleResizeDesktopTaskTransitionHandler(transitions); + } + + @WMSingleton + @Provides static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler( Transitions transitions, Context context diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 739d253424af..d016e73cda96 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -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 */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt new file mode 100644 index 000000000000..94788e45e2b6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt @@ -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 + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index cdc82eadcd90..e42c6623cea0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index b4a1274f3e82..92c2a7c03ee4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -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; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 4e61aafa59a5..c93a11d85f7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 5cb7d4836a57..3ccb938867af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -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(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java index 58644b23ce12..da268988bac7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java @@ -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); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 287d86187288..05a75887dfa0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -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. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java index 82771095cd82..bfce72bcadf0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java @@ -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,22 +121,36 @@ 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); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 4cdc37d2d2b1..1477cf7415cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -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 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt index 8f84008e8d2d..3fbab0f9e2bb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt @@ -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 }) } |