TaskPositioner.java revision f0a60a9c60d9973e10a859dd8c31b8f18adb5a0e
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wm; 18 19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; 20import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 21import static android.app.ActivityManager.RESIZE_MODE_USER; 22import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED; 23import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 24import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 25import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 26import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; 27import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; 28import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 30import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 31import static com.android.server.wm.WindowManagerService.dipToPixel; 32import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; 33import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; 34import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; 35 36import android.annotation.IntDef; 37import android.graphics.Point; 38import android.graphics.Rect; 39import android.os.Looper; 40import android.os.Process; 41import android.os.RemoteException; 42import android.os.Trace; 43import android.util.DisplayMetrics; 44import android.util.Slog; 45import android.view.BatchedInputEventReceiver; 46import android.view.Choreographer; 47import android.view.Display; 48import android.view.DisplayInfo; 49import android.view.InputChannel; 50import android.view.InputDevice; 51import android.view.InputEvent; 52import android.view.MotionEvent; 53import android.view.WindowManager; 54 55import com.android.internal.annotations.VisibleForTesting; 56import com.android.server.input.InputApplicationHandle; 57import com.android.server.input.InputWindowHandle; 58import com.android.server.wm.WindowManagerService.H; 59 60import java.lang.annotation.Retention; 61import java.lang.annotation.RetentionPolicy; 62 63class TaskPositioner implements DimLayer.DimLayerUser { 64 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false; 65 private static final String TAG_LOCAL = "TaskPositioner"; 66 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM; 67 68 // The margin the pointer position has to be within the side of the screen to be 69 // considered at the side of the screen. 70 static final int SIDE_MARGIN_DIP = 100; 71 72 @IntDef(flag = true, 73 value = { 74 CTRL_NONE, 75 CTRL_LEFT, 76 CTRL_RIGHT, 77 CTRL_TOP, 78 CTRL_BOTTOM 79 }) 80 @Retention(RetentionPolicy.SOURCE) 81 @interface CtrlType {} 82 83 private static final int CTRL_NONE = 0x0; 84 private static final int CTRL_LEFT = 0x1; 85 private static final int CTRL_RIGHT = 0x2; 86 private static final int CTRL_TOP = 0x4; 87 private static final int CTRL_BOTTOM = 0x8; 88 89 public static final float RESIZING_HINT_ALPHA = 0.5f; 90 91 public static final int RESIZING_HINT_DURATION_MS = 0; 92 93 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait). 94 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever 95 // aspect he desires. 96 @VisibleForTesting 97 static final float MIN_ASPECT = 1.2f; 98 99 private final WindowManagerService mService; 100 private WindowPositionerEventReceiver mInputEventReceiver; 101 private Display mDisplay; 102 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 103 private DimLayer mDimLayer; 104 @CtrlType 105 private int mCurrentDimSide; 106 private Rect mTmpRect = new Rect(); 107 private int mSideMargin; 108 private int mMinVisibleWidth; 109 private int mMinVisibleHeight; 110 111 private Task mTask; 112 private boolean mResizing; 113 private boolean mPreserveOrientation; 114 private boolean mStartOrientationWasLandscape; 115 private final Rect mWindowOriginalBounds = new Rect(); 116 private final Rect mWindowDragBounds = new Rect(); 117 private final Point mMaxVisibleSize = new Point(); 118 private float mStartDragX; 119 private float mStartDragY; 120 @CtrlType 121 private int mCtrlType = CTRL_NONE; 122 private boolean mDragEnded = false; 123 124 InputChannel mServerChannel; 125 InputChannel mClientChannel; 126 InputApplicationHandle mDragApplicationHandle; 127 InputWindowHandle mDragWindowHandle; 128 129 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver { 130 public WindowPositionerEventReceiver( 131 InputChannel inputChannel, Looper looper, Choreographer choreographer) { 132 super(inputChannel, looper, choreographer); 133 } 134 135 @Override 136 public void onInputEvent(InputEvent event) { 137 if (!(event instanceof MotionEvent) 138 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) { 139 return; 140 } 141 final MotionEvent motionEvent = (MotionEvent) event; 142 boolean handled = false; 143 144 try { 145 if (mDragEnded) { 146 // The drag has ended but the clean-up message has not been processed by 147 // window manager. Drop events that occur after this until window manager 148 // has a chance to clean-up the input handle. 149 handled = true; 150 return; 151 } 152 153 final float newX = motionEvent.getRawX(); 154 final float newY = motionEvent.getRawY(); 155 156 switch (motionEvent.getAction()) { 157 case MotionEvent.ACTION_DOWN: { 158 if (DEBUG_TASK_POSITIONING) { 159 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}"); 160 } 161 } break; 162 163 case MotionEvent.ACTION_MOVE: { 164 if (DEBUG_TASK_POSITIONING){ 165 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}"); 166 } 167 synchronized (mService.mWindowMap) { 168 mDragEnded = notifyMoveLocked(newX, newY); 169 mTask.getDimBounds(mTmpRect); 170 } 171 if (!mTmpRect.equals(mWindowDragBounds)) { 172 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, 173 "wm.TaskPositioner.resizeTask"); 174 try { 175 mService.mActivityManager.resizeTask( 176 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER); 177 } catch (RemoteException e) { 178 } 179 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 180 } 181 } break; 182 183 case MotionEvent.ACTION_UP: { 184 if (DEBUG_TASK_POSITIONING) { 185 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}"); 186 } 187 mDragEnded = true; 188 } break; 189 190 case MotionEvent.ACTION_CANCEL: { 191 if (DEBUG_TASK_POSITIONING) { 192 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}"); 193 } 194 mDragEnded = true; 195 } break; 196 } 197 198 if (mDragEnded) { 199 final boolean wasResizing = mResizing; 200 synchronized (mService.mWindowMap) { 201 endDragLocked(); 202 mTask.getDimBounds(mTmpRect); 203 } 204 try { 205 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) { 206 // We were using fullscreen surface during resizing. Request 207 // resizeTask() one last time to restore surface to window size. 208 mService.mActivityManager.resizeTask( 209 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED); 210 } 211 212 if (mCurrentDimSide != CTRL_NONE) { 213 final int createMode = mCurrentDimSide == CTRL_LEFT 214 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT 215 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; 216 mService.mActivityManager.moveTaskToDockedStack( 217 mTask.mTaskId, createMode, true /*toTop*/, true /* animate */, 218 null /* initialBounds */, false /* moveHomeStackFront */); 219 } 220 } catch(RemoteException e) {} 221 222 // Post back to WM to handle clean-ups. We still need the input 223 // event handler for the last finishInputEvent()! 224 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING); 225 } 226 handled = true; 227 } catch (Exception e) { 228 Slog.e(TAG, "Exception caught by drag handleMotion", e); 229 } finally { 230 finishInputEvent(event, handled); 231 } 232 } 233 } 234 235 TaskPositioner(WindowManagerService service) { 236 mService = service; 237 } 238 239 @VisibleForTesting 240 Rect getWindowDragBounds() { 241 return mWindowDragBounds; 242 } 243 244 /** 245 * @param display The Display that the window being dragged is on. 246 */ 247 void register(Display display) { 248 if (DEBUG_TASK_POSITIONING) { 249 Slog.d(TAG, "Registering task positioner"); 250 } 251 252 if (mClientChannel != null) { 253 Slog.e(TAG, "Task positioner already registered"); 254 return; 255 } 256 257 mDisplay = display; 258 mDisplay.getMetrics(mDisplayMetrics); 259 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG); 260 mServerChannel = channels[0]; 261 mClientChannel = channels[1]; 262 mService.mInputManager.registerInputChannel(mServerChannel, null); 263 264 mInputEventReceiver = new WindowPositionerEventReceiver( 265 mClientChannel, mService.mH.getLooper(), mService.mChoreographer); 266 267 mDragApplicationHandle = new InputApplicationHandle(null); 268 mDragApplicationHandle.name = TAG; 269 mDragApplicationHandle.dispatchingTimeoutNanos = 270 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; 271 272 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, 273 mDisplay.getDisplayId()); 274 mDragWindowHandle.name = TAG; 275 mDragWindowHandle.inputChannel = mServerChannel; 276 mDragWindowHandle.layer = mService.getDragLayerLocked(); 277 mDragWindowHandle.layoutParamsFlags = 0; 278 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; 279 mDragWindowHandle.dispatchingTimeoutNanos = 280 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; 281 mDragWindowHandle.visible = true; 282 mDragWindowHandle.canReceiveKeys = false; 283 mDragWindowHandle.hasFocus = true; 284 mDragWindowHandle.hasWallpaper = false; 285 mDragWindowHandle.paused = false; 286 mDragWindowHandle.ownerPid = Process.myPid(); 287 mDragWindowHandle.ownerUid = Process.myUid(); 288 mDragWindowHandle.inputFeatures = 0; 289 mDragWindowHandle.scaleFactor = 1.0f; 290 291 // The drag window cannot receive new touches. 292 mDragWindowHandle.touchableRegion.setEmpty(); 293 294 // The drag window covers the entire display 295 mDragWindowHandle.frameLeft = 0; 296 mDragWindowHandle.frameTop = 0; 297 final Point p = new Point(); 298 mDisplay.getRealSize(p); 299 mDragWindowHandle.frameRight = p.x; 300 mDragWindowHandle.frameBottom = p.y; 301 302 // Pause rotations before a drag. 303 if (DEBUG_ORIENTATION) { 304 Slog.d(TAG, "Pausing rotation during re-position"); 305 } 306 mService.pauseRotationLocked(); 307 308 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL); 309 mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics); 310 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics); 311 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics); 312 mDisplay.getRealSize(mMaxVisibleSize); 313 314 mDragEnded = false; 315 } 316 317 void unregister() { 318 if (DEBUG_TASK_POSITIONING) { 319 Slog.d(TAG, "Unregistering task positioner"); 320 } 321 322 if (mClientChannel == null) { 323 Slog.e(TAG, "Task positioner not registered"); 324 return; 325 } 326 327 mService.mInputManager.unregisterInputChannel(mServerChannel); 328 329 mInputEventReceiver.dispose(); 330 mInputEventReceiver = null; 331 mClientChannel.dispose(); 332 mServerChannel.dispose(); 333 mClientChannel = null; 334 mServerChannel = null; 335 336 mDragWindowHandle = null; 337 mDragApplicationHandle = null; 338 mDisplay = null; 339 340 if (mDimLayer != null) { 341 mDimLayer.destroySurface(); 342 mDimLayer = null; 343 } 344 mCurrentDimSide = CTRL_NONE; 345 mDragEnded = true; 346 347 // Resume rotations after a drag. 348 if (DEBUG_ORIENTATION) { 349 Slog.d(TAG, "Resuming rotation after re-position"); 350 } 351 mService.resumeRotationLocked(); 352 } 353 354 void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX, 355 float startY) { 356 if (DEBUG_TASK_POSITIONING) { 357 Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize 358 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", " 359 + startY + "}"); 360 } 361 mTask = win.getTask(); 362 // Use the dim bounds, not the original task bounds. The cursor 363 // movement should be calculated relative to the visible bounds. 364 // Also, use the dim bounds of the task which accounts for 365 // multiple app windows. Don't use any bounds from win itself as it 366 // may not be the same size as the task. 367 mTask.getDimBounds(mTmpRect); 368 startDrag(resize, preserveOrientation, startX, startY, mTmpRect); 369 } 370 371 @VisibleForTesting 372 void startDrag(boolean resize, boolean preserveOrientation, 373 float startX, float startY, Rect startBounds) { 374 mCtrlType = CTRL_NONE; 375 mStartDragX = startX; 376 mStartDragY = startY; 377 mPreserveOrientation = preserveOrientation; 378 379 if (resize) { 380 if (startX < startBounds.left) { 381 mCtrlType |= CTRL_LEFT; 382 } 383 if (startX > startBounds.right) { 384 mCtrlType |= CTRL_RIGHT; 385 } 386 if (startY < startBounds.top) { 387 mCtrlType |= CTRL_TOP; 388 } 389 if (startY > startBounds.bottom) { 390 mCtrlType |= CTRL_BOTTOM; 391 } 392 mResizing = mCtrlType != CTRL_NONE; 393 } 394 395 // In case of !isDockedInEffect we are using the union of all task bounds. These might be 396 // made up out of multiple windows which are only partially overlapping. When that happens, 397 // the orientation from the window of interest to the entire stack might diverge. However 398 // for now we treat them as the same. 399 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height(); 400 mWindowOriginalBounds.set(startBounds); 401 402 // Make sure we always have valid drag bounds even if the drag ends before any move events 403 // have been handled. 404 mWindowDragBounds.set(startBounds); 405 } 406 407 private void endDragLocked() { 408 mResizing = false; 409 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM); 410 } 411 412 /** Returns true if the move operation should be ended. */ 413 private boolean notifyMoveLocked(float x, float y) { 414 if (DEBUG_TASK_POSITIONING) { 415 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}"); 416 } 417 418 if (mCtrlType != CTRL_NONE) { 419 resizeDrag(x, y); 420 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM); 421 return false; 422 } 423 424 // This is a moving or scrolling operation. 425 mTask.mStack.getDimBounds(mTmpRect); 426 427 int nX = (int) x; 428 int nY = (int) y; 429 if (!mTmpRect.contains(nX, nY)) { 430 // For a moving operation we allow the pointer to go out of the stack bounds, but 431 // use the clamped pointer position for the drag bounds computation. 432 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right); 433 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom); 434 } 435 436 updateWindowDragBounds(nX, nY, mTmpRect); 437 updateDimLayerVisibility(nX); 438 return false; 439 } 440 441 /** 442 * The user is drag - resizing the window. 443 * 444 * @param x The x coordinate of the current drag coordinate. 445 * @param y the y coordinate of the current drag coordinate. 446 */ 447 @VisibleForTesting 448 void resizeDrag(float x, float y) { 449 // This is a resizing operation. 450 // We need to keep various constraints: 451 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y] 452 // 2. The orientation is kept - if required. 453 final int deltaX = Math.round(x - mStartDragX); 454 final int deltaY = Math.round(y - mStartDragY); 455 int left = mWindowOriginalBounds.left; 456 int top = mWindowOriginalBounds.top; 457 int right = mWindowOriginalBounds.right; 458 int bottom = mWindowOriginalBounds.bottom; 459 460 // The aspect which we have to respect. Note that if the orientation does not need to be 461 // preserved the aspect will be calculated as 1.0 which neutralizes the following 462 // computations. 463 final float minAspect = !mPreserveOrientation 464 ? 1.0f 465 : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT)); 466 // Calculate the resulting width and height of the drag operation. 467 int width = right - left; 468 int height = bottom - top; 469 if ((mCtrlType & CTRL_LEFT) != 0) { 470 width = Math.max(mMinVisibleWidth, width - deltaX); 471 } else if ((mCtrlType & CTRL_RIGHT) != 0) { 472 width = Math.max(mMinVisibleWidth, width + deltaX); 473 } 474 if ((mCtrlType & CTRL_TOP) != 0) { 475 height = Math.max(mMinVisibleHeight, height - deltaY); 476 } else if ((mCtrlType & CTRL_BOTTOM) != 0) { 477 height = Math.max(mMinVisibleHeight, height + deltaY); 478 } 479 480 // If we have to preserve the orientation - check that we are doing so. 481 final float aspect = (float) width / (float) height; 482 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT) 483 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) { 484 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major 485 // drag axis. What ever is producing the bigger rectangle will be chosen. 486 int width1; 487 int width2; 488 int height1; 489 int height2; 490 if (mStartOrientationWasLandscape) { 491 // Assuming that the width is our target we calculate the height. 492 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width)); 493 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT)); 494 if (height1 < mMinVisibleHeight) { 495 // If the resulting height is too small we adjust to the minimal size. 496 height1 = mMinVisibleHeight; 497 width1 = Math.max(mMinVisibleWidth, 498 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT))); 499 } 500 // Assuming that the height is our target we calculate the width. 501 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height)); 502 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT)); 503 if (width2 < mMinVisibleWidth) { 504 // If the resulting width is too small we adjust to the minimal size. 505 width2 = mMinVisibleWidth; 506 height2 = Math.max(mMinVisibleHeight, 507 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT))); 508 } 509 } else { 510 // Assuming that the width is our target we calculate the height. 511 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width)); 512 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT)); 513 if (height1 < mMinVisibleHeight) { 514 // If the resulting height is too small we adjust to the minimal size. 515 height1 = mMinVisibleHeight; 516 width1 = Math.max(mMinVisibleWidth, 517 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT))); 518 } 519 // Assuming that the height is our target we calculate the width. 520 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height)); 521 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT)); 522 if (width2 < mMinVisibleWidth) { 523 // If the resulting width is too small we adjust to the minimal size. 524 width2 = mMinVisibleWidth; 525 height2 = Math.max(mMinVisibleHeight, 526 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT))); 527 } 528 } 529 530 // Use the bigger of the two rectangles if the major change was positive, otherwise 531 // do the opposite. 532 final boolean grows = width > (right - left) || height > (bottom - top); 533 if (grows == (width1 * height1 > width2 * height2)) { 534 width = width1; 535 height = height1; 536 } else { 537 width = width2; 538 height = height2; 539 } 540 } 541 542 // Update mWindowDragBounds to the new drag size. 543 updateDraggedBounds(left, top, right, bottom, width, height); 544 } 545 546 /** 547 * Given the old coordinates and the new width and height, update the mWindowDragBounds. 548 * 549 * @param left The original left bound before the user started dragging. 550 * @param top The original top bound before the user started dragging. 551 * @param right The original right bound before the user started dragging. 552 * @param bottom The original bottom bound before the user started dragging. 553 * @param newWidth The new dragged width. 554 * @param newHeight The new dragged height. 555 */ 556 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth, 557 int newHeight) { 558 // Generate the final bounds by keeping the opposite drag edge constant. 559 if ((mCtrlType & CTRL_LEFT) != 0) { 560 left = right - newWidth; 561 } else { // Note: The right might have changed - if we pulled at the right or not. 562 right = left + newWidth; 563 } 564 if ((mCtrlType & CTRL_TOP) != 0) { 565 top = bottom - newHeight; 566 } else { // Note: The height might have changed - if we pulled at the bottom or not. 567 bottom = top + newHeight; 568 } 569 570 mWindowDragBounds.set(left, top, right, bottom); 571 572 checkBoundsForOrientationViolations(mWindowDragBounds); 573 } 574 575 /** 576 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set). 577 * 578 * @param bounds The bounds to be checked. 579 */ 580 private void checkBoundsForOrientationViolations(Rect bounds) { 581 // When using debug check that we are not violating the given constraints. 582 if (DEBUG_ORIENTATION_VIOLATIONS) { 583 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) { 584 Slog.e(TAG, "Orientation violation detected! should be " 585 + (mStartOrientationWasLandscape ? "landscape" : "portrait") 586 + " but is the other"); 587 } else { 588 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height()); 589 } 590 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) { 591 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth 592 + ", " + bounds.width() + ") Height(min,is)=(" 593 + mMinVisibleHeight + ", " + bounds.height() + ")"); 594 } 595 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) { 596 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x 597 + ", " + bounds.width() + ") Height(min,is)=(" 598 + mMaxVisibleSize.y + ", " + bounds.height() + ")"); 599 } 600 } 601 } 602 603 private void updateWindowDragBounds(int x, int y, Rect stackBounds) { 604 final int offsetX = Math.round(x - mStartDragX); 605 final int offsetY = Math.round(y - mStartDragY); 606 mWindowDragBounds.set(mWindowOriginalBounds); 607 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible. 608 final int maxLeft = stackBounds.right - mMinVisibleWidth; 609 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width(); 610 611 // Vertically, the top mMinVisibleHeight of the window should remain visible. 612 // (This assumes that the window caption bar is at the top of the window). 613 final int minTop = stackBounds.top; 614 final int maxTop = stackBounds.bottom - mMinVisibleHeight; 615 616 mWindowDragBounds.offsetTo( 617 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft), 618 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop)); 619 620 if (DEBUG_TASK_POSITIONING) Slog.d(TAG, 621 "updateWindowDragBounds: " + mWindowDragBounds); 622 } 623 624 private void updateDimLayerVisibility(int x) { 625 @CtrlType 626 int dimSide = getDimSide(x); 627 if (dimSide == mCurrentDimSide) { 628 return; 629 } 630 631 mCurrentDimSide = dimSide; 632 633 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility"); 634 mService.openSurfaceTransaction(); 635 if (mCurrentDimSide == CTRL_NONE) { 636 mDimLayer.hide(); 637 } else { 638 showDimLayer(); 639 } 640 mService.closeSurfaceTransaction(); 641 } 642 643 /** 644 * Returns the side of the screen the dim layer should be shown. 645 * @param x horizontal coordinate used to determine if the dim layer should be shown 646 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the 647 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer 648 * shouldn't be shown. 649 */ 650 private int getDimSide(int x) { 651 if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID 652 || !mTask.mStack.fillsParent() 653 || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) { 654 return CTRL_NONE; 655 } 656 657 mTask.mStack.getDimBounds(mTmpRect); 658 if (x - mSideMargin <= mTmpRect.left) { 659 return CTRL_LEFT; 660 } 661 if (x + mSideMargin >= mTmpRect.right) { 662 return CTRL_RIGHT; 663 } 664 665 return CTRL_NONE; 666 } 667 668 private void showDimLayer() { 669 mTask.mStack.getDimBounds(mTmpRect); 670 if (mCurrentDimSide == CTRL_LEFT) { 671 mTmpRect.right = mTmpRect.centerX(); 672 } else if (mCurrentDimSide == CTRL_RIGHT) { 673 mTmpRect.left = mTmpRect.centerX(); 674 } 675 676 mDimLayer.setBounds(mTmpRect); 677 mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA, 678 RESIZING_HINT_DURATION_MS); 679 } 680 681 @Override /** {@link DimLayer.DimLayerUser} */ 682 public boolean dimFullscreen() { 683 return isFullscreen(); 684 } 685 686 boolean isFullscreen() { 687 return false; 688 } 689 690 @Override /** {@link DimLayer.DimLayerUser} */ 691 public DisplayInfo getDisplayInfo() { 692 return mTask.mStack.getDisplayInfo(); 693 } 694 695 @Override 696 public boolean isAttachedToDisplay() { 697 return mTask != null && mTask.getDisplayContent() != null; 698 } 699 700 @Override 701 public void getDimBounds(Rect out) { 702 // This dim layer user doesn't need this. 703 } 704 705 @Override 706 public String toShortString() { 707 return TAG; 708 } 709} 710