blob: 9082323452c9853554a2a3e68f68a41c316f405f [file] [log] [blame]
/*
* 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.windowdecor;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import java.util.function.Supplier;
/**
* A task positioner that resizes/relocates task contents as it is dragged.
* Utilizes {@link DragPositioningCallbackUtility} to determine new task bounds.
*/
class FluidResizeTaskPositioner implements DragPositioningCallback {
private final ShellTaskOrganizer mTaskOrganizer;
private final WindowDecoration mWindowDecoration;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private DisplayController mDisplayController;
private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
private final Rect mStableBounds = new Rect();
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
// If a task move (not resize) finishes in this region, the positioner will not attempt to
// finalize the bounds there using WCT#setBounds
private final Rect mDisallowedAreaForEndBounds;
private boolean mHasDragResized;
private int mCtrlType;
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds) {
this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
dragStartListener -> {}, SurfaceControl.Transaction::new);
}
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Supplier<SurfaceControl.Transaction> supplier) {
mTaskOrganizer = taskOrganizer;
mWindowDecoration = windowDecoration;
mDisplayController = displayController;
mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds);
mDragStartListener = dragStartListener;
mTransactionSupplier = supplier;
}
@Override
public void onDragPositioningStart(int ctrlType, float x, float y) {
mCtrlType = ctrlType;
mTaskBoundsAtDragStart.set(
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
mRepositionStartPoint.set(x, y);
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
if (mCtrlType != CTRL_TYPE_UNDEFINED && !mWindowDecoration.mTaskInfo.isFocused) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mWindowDecoration.mTaskInfo.token, true);
mTaskOrganizer.applyTransaction(wct);
}
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
}
@Override
public void onDragPositioningMove(float x, float y) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint);
if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
mDisplayController, mWindowDecoration)) {
// The task is being resized, send the |dragResizing| hint to core with the first
// bounds-change wct.
if (!mHasDragResized) {
// This is the first bounds change since drag resize operation started.
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
}
DragPositioningCallbackUtility.applyTaskBoundsChange(wct, mWindowDecoration,
mRepositionTaskBounds, mTaskOrganizer);
mHasDragResized = true;
} else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
final SurfaceControl.Transaction t = mTransactionSupplier.get();
DragPositioningCallbackUtility.setPositionOnDrag(mWindowDecoration,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
t.apply();
}
}
@Override
public void onDragPositioningEnd(float x, float y) {
// If task has been resized or task was dragged into area outside of
// mDisallowedAreaForEndBounds, apply WCT to finish it.
if (isResizing() && mHasDragResized) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
mRepositionStartPoint);
if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mRepositionTaskBounds,
mTaskBoundsAtDragStart, mStableBounds, delta, mDisplayController,
mWindowDecoration)) {
wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
}
mTaskOrganizer.applyTransaction(wct);
} else if (mCtrlType == CTRL_TYPE_UNDEFINED
&& !mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTaskOrganizer.applyTransaction(wct);
}
mTaskBoundsAtDragStart.setEmpty();
mRepositionStartPoint.set(0, 0);
mCtrlType = CTRL_TYPE_UNDEFINED;
mHasDragResized = false;
}
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
}
}