Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- int stackId = task.stack.mStackId;
- // We allow the task to scroll instead of resizing if this is a non-resizeable task
- // in crop windows resize mode or if the task size is affected by the docked stack
- // changing size. No need to update configuration.
- if (bounds != null && task.inCropWindowsResizeMode()
- && mStackSupervisor.isStackDockedInEffect(stackId)) {
- mWindowManager.scrollTask(task.taskId, bounds);
- return;
- }
-
// Place the task in the right stack if it isn't there already based on
// the requested bounds.
// The stack transition logic is:
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
+ int stackId = task.stack.mStackId;
if (!StackId.isTaskResizeAllowed(stackId)) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
/** Detect user tapping outside of current focused stack bounds .*/
Region mTouchExcludeRegion = new Region();
- /** Detect user tapping in a non-resizeable task in docked or fullscreen stack .*/
- Region mNonResizeableRegion = new Region();
-
/** Save allocating when calculating rects */
private final Rect mTmpRect = new Rect();
private final Rect mTmpRect2 = new Rect();
mTouchExcludeRegion.set(mBaseDisplayRect);
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
boolean addBackFocusedTask = false;
- mNonResizeableRegion.setEmpty();
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
final ArrayList<Task> tasks = stack.getTasks();
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
- if (task.isTwoFingerScrollMode()) {
- stack.getBounds(mTmpRect);
- mNonResizeableRegion.op(mTmpRect, Region.Op.UNION);
- break;
- }
}
}
// If we removed the focused task above, add it back and only leave its
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
}
if (mTapDetector != null) {
- mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
+ mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
}
}
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
- if (child.isDockedInEffect()) {
- // Adjust to account for non-resizeable tasks that's scrolled
- inputWindowHandle.frameLeft += child.mXOffset;
- inputWindowHandle.frameTop += child.mYOffset;
- inputWindowHandle.frameRight += child.mXOffset;
- inputWindowHandle.frameBottom += child.mYOffset;
- }
-
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
final Rect mPreparedFrozenBounds = new Rect();
final Configuration mPreparedFrozenMergedConfig = new Configuration();
- private Rect mPreScrollBounds = new Rect();
- private boolean mScrollValid;
-
// Bounds used to calculate the insets.
private final Rect mTempInsetBounds = new Rect();
// Can't set to fullscreen if we don't have a display to get bounds from...
return BOUNDS_CHANGE_NONE;
}
- if (mPreScrollBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+ if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
return BOUNDS_CHANGE_NONE;
}
int boundsChange = BOUNDS_CHANGE_NONE;
- if (mPreScrollBounds.left != bounds.left || mPreScrollBounds.top != bounds.top) {
+ if (mBounds.left != bounds.left || mBounds.top != bounds.top) {
boundsChange |= BOUNDS_CHANGE_POSITION;
}
- if (mPreScrollBounds.width() != bounds.width() || mPreScrollBounds.height() != bounds.height()) {
+ if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
boundsChange |= BOUNDS_CHANGE_SIZE;
}
-
- mPreScrollBounds.set(bounds);
-
- resetScrollLocked();
+ mBounds.set(bounds);
mRotation = rotation;
if (displayContent != null) {
resizeLocked(mTmpRect2, mOverrideConfig, false /* forced */);
}
- void resetScrollLocked() {
- if (mScrollValid) {
- mScrollValid = false;
- applyScrollToAllWindows(0, 0);
- }
- mBounds.set(mPreScrollBounds);
- }
-
- void applyScrollToAllWindows(final int xOffset, final int yOffset) {
- for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
- final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
- for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
- final WindowState win = windows.get(winNdx);
- win.mXOffset = xOffset;
- win.mYOffset = yOffset;
- }
- }
- }
-
- void applyScrollToWindowIfNeeded(final WindowState win) {
- if (mScrollValid) {
- win.mXOffset = mBounds.left;
- win.mYOffset = mBounds.top;
- }
- }
-
- boolean scrollLocked(Rect bounds) {
- // shift the task bound if it doesn't fully cover the stack area
- mStack.getDimBounds(mTmpRect);
- if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
- if (bounds.left > mTmpRect.left) {
- bounds.left = mTmpRect.left;
- bounds.right = mTmpRect.left + mBounds.width();
- } else if (bounds.right < mTmpRect.right) {
- bounds.left = mTmpRect.right - mBounds.width();
- bounds.right = mTmpRect.right;
- }
- } else {
- if (bounds.top > mTmpRect.top) {
- bounds.top = mTmpRect.top;
- bounds.bottom = mTmpRect.top + mBounds.height();
- } else if (bounds.bottom < mTmpRect.bottom) {
- bounds.top = mTmpRect.bottom - mBounds.height();
- bounds.bottom = mTmpRect.bottom;
- }
- }
-
- // We can stop here if we're already scrolling and the scrolled bounds not changed.
- if (mScrollValid && bounds.equals(mBounds)) {
- return false;
- }
-
- // Normal setBounds() does not allow non-null bounds for fullscreen apps.
- // We only change bounds for the scrolling case without change it size,
- // on resizing path we should still want the validation.
- mBounds.set(bounds);
- mScrollValid = true;
- applyScrollToAllWindows(bounds.left, bounds.top);
- return true;
- }
-
/** Return true if the current bound can get outputted to the rest of the system as-is. */
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
}
// Device rotation changed.
- // - Reset the bounds to the pre-scroll bounds as whatever scrolling was done is no longer
- // valid.
+ // - We don't want the task to move around on the screen when this happens, so update the
+ // task bounds so it stays in the same place.
// - Rotate the bounds and notify activity manager if the task can be resized independently
- // from its stack. The stack will take care of task rotation for the other case.
- mTmpRect2.set(mPreScrollBounds);
+ // from its stack. The stack will take care of task rotation for the other case.
+ mTmpRect2.set(mBounds);
if (!StackId.isTaskResizeAllowed(mStack.mStackId)) {
setBounds(mTmpRect2, mOverrideConfig);
// call. We do this to prevent a deadlock between window manager lock and activity
// manager lock been held.
mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
- RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mPreScrollBounds).sendToTarget();
+ RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds).sendToTarget();
}
}
return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
- boolean inDockedWorkspace() {
- return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
- }
-
boolean inPinnedWorkspace() {
return mStack != null && mStack.mStackId == PINNED_STACK_ID;
}
- boolean isResizeableByDockedStack() {
- final DisplayContent displayContent = getDisplayContent();
- return displayContent != null && displayContent.getDockedStackLocked() != null
- && mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId);
- }
-
boolean isFloating() {
return StackId.tasksAreFloating(mStack.mStackId);
}
- /**
- * Whether the task should be treated as if it's docked. Returns true if the task
- * is currently in docked workspace, or it's side-by-side to a docked task.
- */
- boolean isDockedInEffect() {
- return inDockedWorkspace() || isResizeableByDockedStack();
- }
-
- boolean isTwoFingerScrollMode() {
- return inCropWindowsResizeMode() && isDockedInEffect();
- }
-
WindowState getTopVisibleAppMainWindow() {
final AppWindowToken token = getTopVisibleAppToken();
return token != null ? token.findMainWindow() : null;
mStartDragX = startX;
mStartDragY = startY;
- if (mTask.isDockedInEffect()) {
- // If this is a docked task or if task size is affected by docked stack changing size,
- // we can only be here if the task is not resizeable and we're handling a two-finger
- // scrolling. Use the original task bounds to position the task, the dim bounds
- // is cropped and doesn't move.
- mTask.getBounds(mTmpRect);
- } else {
- // Use the dim bounds, not the original task bounds. The cursor
- // movement should be calculated relative to the visible bounds.
- // Also, use the dim bounds of the task which accounts for
- // multiple app windows. Don't use any bounds from win itself as it
- // may not be the same size as the task.
- mTask.getDimBounds(mTmpRect);
- }
+ // Use the dim bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ // Also, use the dim bounds of the task which accounts for
+ // multiple app windows. Don't use any bounds from win itself as it
+ // may not be the same size as the task.
+ mTask.getDimBounds(mTmpRect);
if (resize) {
if (startX < mTmpRect.left) {
// This is a moving or scrolling operation.
mTask.mStack.getDimBounds(mTmpRect);
- boolean dragEnded = false;
int nX = (int) x;
int nY = (int) y;
if (!mTmpRect.contains(nX, nY)) {
- if (mTask.isDockedInEffect()) {
- // We end the scrolling operation if position is outside the stack bounds.
- dragEnded = true;
- } else {
- // For a moving operation we allow the pointer to go out of the stack bounds, but
- // use the clamped pointer position for the drag bounds computation.
- nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
- nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
- }
+ // For a moving operation we allow the pointer to go out of the stack bounds, but
+ // use the clamped pointer position for the drag bounds computation.
+ nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
+ nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
}
updateWindowDragBounds(nX, nY, mTmpRect);
updateDimLayerVisibility(nX);
- return dragEnded;
+ return false;
}
private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
final int offsetX = Math.round(x - mStartDragX);
final int offsetY = Math.round(y - mStartDragY);
mWindowDragBounds.set(mWindowOriginalBounds);
- if (mTask.isDockedInEffect()) {
- // Offset the bounds without clamp, the bounds will be shifted later
- // by window manager before applying the scrolling.
- if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
- mWindowDragBounds.offset(offsetX, 0);
- } else {
- mWindowDragBounds.offset(0, offsetY);
- }
- } else {
- // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
- final int maxLeft = stackBounds.right - mMinVisibleWidth;
- final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
-
- // Vertically, the top mMinVisibleHeight of the window should remain visible.
- // (This assumes that the window caption bar is at the top of the window).
- final int minTop = stackBounds.top;
- final int maxTop = stackBounds.bottom - mMinVisibleHeight;
-
- mWindowDragBounds.offsetTo(
- Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
- Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
- }
+ // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
+ final int maxLeft = stackBounds.right - mMinVisibleWidth;
+ final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
+
+ // Vertically, the top mMinVisibleHeight of the window should remain visible.
+ // (This assumes that the window caption bar is at the top of the window).
+ final int minTop = stackBounds.top;
+ final int maxTop = stackBounds.bottom - mMinVisibleHeight;
+
+ mWindowDragBounds.offsetTo(
+ Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
+ Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
+
if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
"updateWindowDragBounds: " + mWindowDragBounds);
}
Configuration config = configs.get(task.mTaskId);
if (config != null) {
Rect bounds = taskBounds.get(task.mTaskId);
- if (task.isTwoFingerScrollMode()) {
- // This is a non-resizeable task that's docked (or side-by-side to the docked
- // stack). It might have been scrolled previously, and after the stack resizing,
- // it might no longer fully cover the stack area.
- // Save the old bounds and re-apply the scroll. This adjusts the bounds to
- // fit the new stack bounds.
- task.resizeLocked(bounds, config, false /* forced */);
- task.getBounds(mTmpRect);
- task.scrollLocked(mTmpRect);
- } else {
- task.resizeLocked(bounds, config, false /* forced */);
- task.setTempInsetBounds(
- taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId)
- : null);
- }
+ task.resizeLocked(bounds, config, false /* forced */);
+ task.setTempInsetBounds(taskTempInsetBounds != null ?
+ taskTempInsetBounds.get(task.mTaskId) : null);
} else {
Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
}
if (mFullscreen) {
return;
}
+
+ final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
+
// Update bounds of containing tasks.
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = mTasks.get(taskNdx);
- if (task.isTwoFingerScrollMode()) {
- // If we're scrolling we don't care about your bounds or configs,
- // they should be null as if we were in fullscreen.
- task.resizeLocked(null, null, false /* forced */);
- task.getBounds(mTmpRect2);
- task.scrollLocked(mTmpRect2);
- } else {
- final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
- task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
- }
+ task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
}
}
if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
"positionTask: task=" + task + " position=" + position);
mTasks.add(position, task);
-
- // If we are moving the task across stacks, the scroll is no longer valid.
- if (task.mStack != this) {
- task.resetScrollLocked();
- }
task.mStack = this;
task.updateDisplayInfo(mDisplayContent);
boolean toTop = position == mTasks.size() - 1;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
-import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Rect mTmpRect = new Rect();
- private final Region mNonResizeableRegion = new Region();
- private boolean mTwoFingerScrolling;
- private boolean mInGestureDetection;
- private GestureDetector mGestureDetector;
private int mPointerIconType = TYPE_NOT_SPECIFIED;
public TaskTapPointerEventListener(WindowManagerService service,
mDisplayContent = displayContent;
}
- // initialize the object, note this must be done outside WindowManagerService
- // ctor, otherwise it may cause recursion as some code in GestureDetector ctor
- // depends on WMS being already created.
- void init() {
- mGestureDetector = new GestureDetector(
- mService.mContext, new TwoFingerScrollListener(), mService.mH);
- }
-
@Override
public void onPointerEvent(MotionEvent motionEvent) {
- doGestureDetection(motionEvent);
-
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
x, y, mDisplayContent).sendToTarget();
}
}
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- if (motionEvent.getPointerCount() != 2) {
- stopTwoFingerScroll();
- }
- break;
}
+ break;
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
InputManager.getInstance().setPointerIconType(mPointerIconType);
}
}
- } break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP: {
- stopTwoFingerScroll();
- break;
- }
- }
- }
-
- private void doGestureDetection(MotionEvent motionEvent) {
- if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
- return;
- }
- final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
- final int x = (int) motionEvent.getX();
- final int y = (int) motionEvent.getY();
- final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
- if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
- // If we receive the following actions, or the pointer goes out of the area
- // we're interested in, stop detecting and cancel the current detection.
- mInGestureDetection = isTouchInside
- && action != MotionEvent.ACTION_UP
- && action != MotionEvent.ACTION_POINTER_UP
- && action != MotionEvent.ACTION_CANCEL;
- if (mInGestureDetection) {
- mGestureDetector.onTouchEvent(motionEvent);
- } else {
- MotionEvent cancelEvent = motionEvent.copy();
- cancelEvent.cancel();
- mGestureDetector.onTouchEvent(cancelEvent);
- stopTwoFingerScroll();
- }
- }
- }
-
- private void onTwoFingerScroll(MotionEvent e) {
- final int x = (int)e.getX(0);
- final int y = (int)e.getY(0);
- if (!mTwoFingerScrolling) {
- mTwoFingerScrolling = true;
- mService.mH.obtainMessage(
- H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
- }
- }
-
- private void stopTwoFingerScroll() {
- if (mTwoFingerScrolling) {
- mTwoFingerScrolling = false;
- mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
- }
- }
-
- private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2,
- float distanceX, float distanceY) {
- if (e2.getPointerCount() == 2) {
- onTwoFingerScroll(e2);
- return true;
}
- stopTwoFingerScroll();
- return false;
+ break;
}
}
- void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
+ void setTouchExcludeRegion(Region newRegion) {
synchronized (this) {
mTouchExcludeRegion.set(newRegion);
- mNonResizeableRegion.set(nonResizeableRegion);
}
}
}
}
}
- // If the window is being added to a task that's docked but non-resizeable,
- // we need to update this new window's scroll position when it's added.
- win.applyScrollIfNeeded();
-
// If the window is being added to a stack that's currently adjusted for IME,
// make sure to apply the same adjust to this new window.
win.applyAdjustForImeIfNeeded();
}
}
- public void scrollTask(int taskId, Rect bounds) {
- synchronized (mWindowMap) {
- Task task = mTaskIdToTask.get(taskId);
- if (task == null) {
- throw new IllegalArgumentException("scrollTask: taskId " + taskId
- + " not found.");
- }
-
- if (task.scrollLocked(bounds)) {
- task.getDisplayContent().layoutNeeded = true;
- mInputMonitor.setUpdateInputWindowsNeededLw();
- mWindowPlacerLocked.performSurfacePlacement();
- }
- }
- }
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
return true;
}
- private void startScrollingTask(DisplayContent displayContent, int startX, int startY) {
- if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM,
- "startScrollingTask: " + "{" + startX + ", " + startY + "}");
-
- Task task = null;
- synchronized (mWindowMap) {
- int taskId = displayContent.taskIdFromPoint(startX, startY);
- if (taskId >= 0) {
- task = mTaskIdToTask.get(taskId);
- }
- if (task == null || !task.isDockedInEffect() || !startPositioningLocked(
- task.getTopVisibleAppMainWindow(), false /*resize*/, startX, startY)) {
- return;
- }
- }
- try {
- mActivityManager.setFocusedTask(task.mTaskId);
- } catch(RemoteException e) {}
- }
-
private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
int taskId = -1;
synchronized (mWindowMap) {
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
displayContent.initializeDisplayBaseInfo();
- if (displayContent.mTapDetector != null) {
- displayContent.mTapDetector.init();
- }
}
}
}
public static final int RESIZE_STACK = 42;
public static final int RESIZE_TASK = 43;
- public static final int TWO_FINGER_SCROLL_START = 44;
-
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
}
break;
- case TWO_FINGER_SCROLL_START: {
- startScrollingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
- }
- break;
-
case TAP_OUTSIDE_TASK: {
handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
}
}
}
- boolean inDockedWorkspace() {
- final Task task = getTask();
- return task != null && task.inDockedWorkspace();
- }
-
// TODO: Strange usage of word workspace here and above.
boolean inPinnedWorkspace() {
final Task task = getTask();
return task != null && task.inPinnedWorkspace();
}
- boolean isDockedInEffect() {
- final Task task = getTask();
- return task != null && task.isDockedInEffect();
- }
-
- void applyScrollIfNeeded() {
- final Task task = getTask();
- if (task != null) {
- task.applyScrollToWindowIfNeeded(this);
- }
- }
-
void applyAdjustForImeIfNeeded() {
final Task task = getTask();
if (task != null && task.mStack != null && task.mStack.isAdjustedForIme()) {