1 2 /* 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.launcher3.dragndrop; 19 20 import static android.view.View.MeasureSpec.EXACTLY; 21 import static android.view.View.MeasureSpec.getMode; 22 import static android.view.View.MeasureSpec.getSize; 23 24 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; 25 26 import android.animation.Animator; 27 import android.animation.AnimatorListenerAdapter; 28 import android.animation.TimeInterpolator; 29 import android.animation.ValueAnimator; 30 import android.animation.ValueAnimator.AnimatorUpdateListener; 31 import android.content.Context; 32 import android.content.res.Resources; 33 import android.graphics.Canvas; 34 import android.graphics.Rect; 35 import android.util.AttributeSet; 36 import android.view.Gravity; 37 import android.view.KeyEvent; 38 import android.view.MotionEvent; 39 import android.view.View; 40 import android.view.accessibility.AccessibilityEvent; 41 import android.view.accessibility.AccessibilityManager; 42 import android.view.animation.Interpolator; 43 import android.widget.FrameLayout; 44 import android.widget.TextView; 45 46 import com.android.launcher3.AbstractFloatingView; 47 import com.android.launcher3.CellLayout; 48 import com.android.launcher3.DropTargetBar; 49 import com.android.launcher3.Launcher; 50 import com.android.launcher3.R; 51 import com.android.launcher3.ShortcutAndWidgetContainer; 52 import com.android.launcher3.Workspace; 53 import com.android.launcher3.anim.Interpolators; 54 import com.android.launcher3.folder.Folder; 55 import com.android.launcher3.folder.FolderIcon; 56 import com.android.launcher3.graphics.OverviewScrim; 57 import com.android.launcher3.graphics.RotationMode; 58 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; 59 import com.android.launcher3.keyboard.ViewGroupFocusHelper; 60 import com.android.launcher3.uioverrides.UiFactory; 61 import com.android.launcher3.util.Thunk; 62 import com.android.launcher3.views.BaseDragLayer; 63 import com.android.launcher3.views.Transposable; 64 65 import java.util.ArrayList; 66 67 /** 68 * A ViewGroup that coordinates dragging across its descendants 69 */ 70 public class DragLayer extends BaseDragLayer<Launcher> { 71 72 public static final int ALPHA_INDEX_OVERLAY = 0; 73 public static final int ALPHA_INDEX_LAUNCHER_LOAD = 1; 74 public static final int ALPHA_INDEX_TRANSITIONS = 2; 75 private static final int ALPHA_CHANNEL_COUNT = 3; 76 77 public static final int ANIMATION_END_DISAPPEAR = 0; 78 public static final int ANIMATION_END_REMAIN_VISIBLE = 2; 79 80 @Thunk DragController mDragController; 81 82 // Variables relating to animation of views after drop 83 private ValueAnimator mDropAnim = null; 84 private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5; 85 @Thunk DragView mDropView = null; 86 @Thunk int mAnchorViewInitialScrollX = 0; 87 @Thunk View mAnchorView = null; 88 89 private boolean mHoverPointClosesFolder = false; 90 91 private int mTopViewIndex; 92 private int mChildCountOnLastUpdate = -1; 93 94 // Related to adjacent page hints 95 private final ViewGroupFocusHelper mFocusIndicatorHelper; 96 private final WorkspaceAndHotseatScrim mWorkspaceScrim; 97 private final OverviewScrim mOverviewScrim; 98 99 /** 100 * Used to create a new DragLayer from XML. 101 * 102 * @param context The application's context. 103 * @param attrs The attributes set containing the Workspace's customization values. 104 */ DragLayer(Context context, AttributeSet attrs)105 public DragLayer(Context context, AttributeSet attrs) { 106 super(context, attrs, ALPHA_CHANNEL_COUNT); 107 108 // Disable multitouch across the workspace/all apps/customize tray 109 setMotionEventSplittingEnabled(false); 110 setChildrenDrawingOrderEnabled(true); 111 112 mFocusIndicatorHelper = new ViewGroupFocusHelper(this); 113 mWorkspaceScrim = new WorkspaceAndHotseatScrim(this); 114 mOverviewScrim = new OverviewScrim(this); 115 } 116 setup(DragController dragController, Workspace workspace)117 public void setup(DragController dragController, Workspace workspace) { 118 mDragController = dragController; 119 mWorkspaceScrim.setWorkspace(workspace); 120 recreateControllers(); 121 } 122 recreateControllers()123 public void recreateControllers() { 124 mControllers = UiFactory.createTouchControllers(mActivity); 125 } 126 getFocusIndicatorHelper()127 public ViewGroupFocusHelper getFocusIndicatorHelper() { 128 return mFocusIndicatorHelper; 129 } 130 131 @Override dispatchKeyEvent(KeyEvent event)132 public boolean dispatchKeyEvent(KeyEvent event) { 133 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); 134 } 135 isEventOverAccessibleDropTargetBar(MotionEvent ev)136 private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) { 137 return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev); 138 } 139 140 @Override onInterceptHoverEvent(MotionEvent ev)141 public boolean onInterceptHoverEvent(MotionEvent ev) { 142 if (mActivity == null || mActivity.getWorkspace() == null) { 143 return false; 144 } 145 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); 146 if (!(topView instanceof Folder)) { 147 return false; 148 } else { 149 AccessibilityManager accessibilityManager = (AccessibilityManager) 150 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 151 if (accessibilityManager.isTouchExplorationEnabled()) { 152 Folder currentFolder = (Folder) topView; 153 final int action = ev.getAction(); 154 boolean isOverFolderOrSearchBar; 155 switch (action) { 156 case MotionEvent.ACTION_HOVER_ENTER: 157 isOverFolderOrSearchBar = isEventOverView(topView, ev) || 158 isEventOverAccessibleDropTargetBar(ev); 159 if (!isOverFolderOrSearchBar) { 160 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); 161 mHoverPointClosesFolder = true; 162 return true; 163 } 164 mHoverPointClosesFolder = false; 165 break; 166 case MotionEvent.ACTION_HOVER_MOVE: 167 isOverFolderOrSearchBar = isEventOverView(topView, ev) || 168 isEventOverAccessibleDropTargetBar(ev); 169 if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) { 170 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); 171 mHoverPointClosesFolder = true; 172 return true; 173 } else if (!isOverFolderOrSearchBar) { 174 return true; 175 } 176 mHoverPointClosesFolder = false; 177 } 178 } 179 } 180 return false; 181 } 182 sendTapOutsideFolderAccessibilityEvent(boolean isEditingName)183 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) { 184 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close; 185 sendCustomAccessibilityEvent( 186 this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId)); 187 } 188 189 @Override onHoverEvent(MotionEvent ev)190 public boolean onHoverEvent(MotionEvent ev) { 191 // If we've received this, we've already done the necessary handling 192 // in onInterceptHoverEvent. Return true to consume the event. 193 return false; 194 } 195 196 isInAccessibleDrag()197 private boolean isInAccessibleDrag() { 198 return mActivity.getAccessibilityDelegate().isInAccessibleDrag(); 199 } 200 201 @Override onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)202 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { 203 if (isInAccessibleDrag() && child instanceof DropTargetBar) { 204 return true; 205 } 206 return super.onRequestSendAccessibilityEvent(child, event); 207 } 208 209 @Override addChildrenForAccessibility(ArrayList<View> childrenForAccessibility)210 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) { 211 View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity, 212 AbstractFloatingView.TYPE_ACCESSIBLE); 213 if (topView != null) { 214 addAccessibleChildToList(topView, childrenForAccessibility); 215 if (isInAccessibleDrag()) { 216 addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility); 217 } 218 } else { 219 super.addChildrenForAccessibility(childrenForAccessibility); 220 } 221 } 222 223 @Override dispatchUnhandledMove(View focused, int direction)224 public boolean dispatchUnhandledMove(View focused, int direction) { 225 return super.dispatchUnhandledMove(focused, direction) 226 || mDragController.dispatchUnhandledMove(focused, direction); 227 } 228 229 @Override dispatchTouchEvent(MotionEvent ev)230 public boolean dispatchTouchEvent(MotionEvent ev) { 231 ev.offsetLocation(getTranslationX(), 0); 232 try { 233 return super.dispatchTouchEvent(ev); 234 } finally { 235 ev.offsetLocation(-getTranslationX(), 0); 236 } 237 } 238 animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration)239 public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, 240 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, 241 int duration) { 242 Rect r = new Rect(); 243 getViewRectRelativeToSelf(dragView, r); 244 final int fromX = r.left; 245 final int fromY = r.top; 246 247 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY, 248 onFinishRunnable, animationEndStyle, duration, null); 249 } 250 animateViewIntoPosition(DragView dragView, final View child, View anchorView)251 public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) { 252 animateViewIntoPosition(dragView, child, -1, anchorView); 253 } 254 animateViewIntoPosition(DragView dragView, final View child, int duration, View anchorView)255 public void animateViewIntoPosition(DragView dragView, final View child, int duration, 256 View anchorView) { 257 ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent(); 258 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 259 parentChildren.measureChild(child); 260 261 Rect r = new Rect(); 262 getViewRectRelativeToSelf(dragView, r); 263 264 float coord[] = new float[2]; 265 float childScale = child.getScaleX(); 266 coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2); 267 coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2); 268 269 // Since the child hasn't necessarily been laid out, we force the lp to be updated with 270 // the correct coordinates (above) and use these to determine the final location 271 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord); 272 // We need to account for the scale of the child itself, as the above only accounts for 273 // for the scale in parents. 274 scale *= childScale; 275 int toX = Math.round(coord[0]); 276 int toY = Math.round(coord[1]); 277 float toScale = scale; 278 if (child instanceof TextView) { 279 TextView tv = (TextView) child; 280 // Account for the source scale of the icon (ie. from AllApps to Workspace, in which 281 // the workspace may have smaller icon bounds). 282 toScale = scale / dragView.getIntrinsicIconScaleFactor(); 283 284 // The child may be scaled (always about the center of the view) so to account for it, 285 // we have to offset the position by the scaled size. Once we do that, we can center 286 // the drag view about the scaled child view. 287 // padding will remain constant (does not scale with size) 288 toY += tv.getPaddingTop(); 289 toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2; 290 if (dragView.getDragVisualizeOffset() != null) { 291 toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y); 292 } 293 294 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 295 } else if (child instanceof FolderIcon) { 296 // Account for holographic blur padding on the drag view 297 toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop())); 298 toY -= scale * dragView.getBlurSizeOutline() / 2; 299 toY -= (1 - scale) * dragView.getMeasuredHeight() / 2; 300 // Center in the x coordinate about the target's drawable 301 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 302 } else { 303 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2; 304 toX -= (Math.round(scale * (dragView.getMeasuredWidth() 305 - child.getMeasuredWidth()))) / 2; 306 } 307 308 final int fromX = r.left; 309 final int fromY = r.top; 310 child.setVisibility(INVISIBLE); 311 Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE); 312 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale, 313 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView); 314 } 315 animateViewIntoPosition(final DragView view, final int fromX, final int fromY, final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, int animationEndStyle, int duration, View anchorView)316 public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY, 317 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, 318 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, 319 int animationEndStyle, int duration, View anchorView) { 320 Rect from = new Rect(fromX, fromY, fromX + 321 view.getMeasuredWidth(), fromY + view.getMeasuredHeight()); 322 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight()); 323 animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration, 324 null, null, onCompleteRunnable, animationEndStyle, anchorView); 325 } 326 327 /** 328 * This method animates a view at the end of a drag and drop animation. 329 * 330 * @param view The view to be animated. This view is drawn directly into DragLayer, and so 331 * doesn't need to be a child of DragLayer. 332 * @param from The initial location of the view. Only the left and top parameters are used. 333 * @param to The final location of the view. Only the left and top parameters are used. This 334 * location doesn't account for scaling, and so should be centered about the desired 335 * final location (including scaling). 336 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates. 337 * @param finalScaleX The final scale of the view. The view is scaled about its center. 338 * @param finalScaleY The final scale of the view. The view is scaled about its center. 339 * @param duration The duration of the animation. 340 * @param motionInterpolator The interpolator to use for the location of the view. 341 * @param alphaInterpolator The interpolator to use for the alpha of the view. 342 * @param onCompleteRunnable Optional runnable to run on animation completion. 343 * @param animationEndStyle Whether or not to fade out the view once the animation completes. 344 * {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}. 345 * @param anchorView If not null, this represents the view which the animated view stays 346 * anchored to in case scrolling is currently taking place. Note: currently this is 347 * only used for the X dimension for the case of the workspace. 348 */ animateView(final DragView view, final Rect from, final Rect to, final float finalAlpha, final float initScaleX, final float initScaleY, final float finalScaleX, final float finalScaleY, int duration, final Interpolator motionInterpolator, final Interpolator alphaInterpolator, final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView)349 public void animateView(final DragView view, final Rect from, final Rect to, 350 final float finalAlpha, final float initScaleX, final float initScaleY, 351 final float finalScaleX, final float finalScaleY, int duration, 352 final Interpolator motionInterpolator, final Interpolator alphaInterpolator, 353 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) { 354 355 // Calculate the duration of the animation based on the object's distance 356 final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top); 357 final Resources res = getResources(); 358 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); 359 360 // If duration < 0, this is a cue to compute the duration based on the distance 361 if (duration < 0) { 362 duration = res.getInteger(R.integer.config_dropAnimMaxDuration); 363 if (dist < maxDist) { 364 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist); 365 } 366 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration)); 367 } 368 369 // Fall back to cubic ease out interpolator for the animation if none is specified 370 TimeInterpolator interpolator = null; 371 if (alphaInterpolator == null || motionInterpolator == null) { 372 interpolator = mCubicEaseOutInterpolator; 373 } 374 375 // Animate the view 376 final float initAlpha = view.getAlpha(); 377 final float dropViewScale = view.getScaleX(); 378 AnimatorUpdateListener updateCb = new AnimatorUpdateListener() { 379 @Override 380 public void onAnimationUpdate(ValueAnimator animation) { 381 final float percent = (Float) animation.getAnimatedValue(); 382 final int width = view.getMeasuredWidth(); 383 final int height = view.getMeasuredHeight(); 384 385 float alphaPercent = alphaInterpolator == null ? percent : 386 alphaInterpolator.getInterpolation(percent); 387 float motionPercent = motionInterpolator == null ? percent : 388 motionInterpolator.getInterpolation(percent); 389 390 float initialScaleX = initScaleX * dropViewScale; 391 float initialScaleY = initScaleY * dropViewScale; 392 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent); 393 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent); 394 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent); 395 396 float fromLeft = from.left + (initialScaleX - 1f) * width / 2; 397 float fromTop = from.top + (initialScaleY - 1f) * height / 2; 398 399 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent))); 400 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent))); 401 402 int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() * 403 (mAnchorViewInitialScrollX - mAnchorView.getScrollX())); 404 405 int xPos = x - mDropView.getScrollX() + anchorAdjust; 406 int yPos = y - mDropView.getScrollY(); 407 408 mDropView.setTranslationX(xPos); 409 mDropView.setTranslationY(yPos); 410 mDropView.setScaleX(scaleX); 411 mDropView.setScaleY(scaleY); 412 mDropView.setAlpha(alpha); 413 } 414 }; 415 animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle, 416 anchorView); 417 } 418 animateView(final DragView view, AnimatorUpdateListener updateCb, int duration, TimeInterpolator interpolator, final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView)419 public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration, 420 TimeInterpolator interpolator, final Runnable onCompleteRunnable, 421 final int animationEndStyle, View anchorView) { 422 // Clean up the previous animations 423 if (mDropAnim != null) mDropAnim.cancel(); 424 425 // Show the drop view if it was previously hidden 426 mDropView = view; 427 mDropView.cancelAnimation(); 428 mDropView.requestLayout(); 429 430 // Set the anchor view if the page is scrolling 431 if (anchorView != null) { 432 mAnchorViewInitialScrollX = anchorView.getScrollX(); 433 } 434 mAnchorView = anchorView; 435 436 // Create and start the animation 437 mDropAnim = new ValueAnimator(); 438 mDropAnim.setInterpolator(interpolator); 439 mDropAnim.setDuration(duration); 440 mDropAnim.setFloatValues(0f, 1f); 441 mDropAnim.addUpdateListener(updateCb); 442 mDropAnim.addListener(new AnimatorListenerAdapter() { 443 public void onAnimationEnd(Animator animation) { 444 if (onCompleteRunnable != null) { 445 onCompleteRunnable.run(); 446 } 447 switch (animationEndStyle) { 448 case ANIMATION_END_DISAPPEAR: 449 clearAnimatedView(); 450 break; 451 case ANIMATION_END_REMAIN_VISIBLE: 452 break; 453 } 454 mDropAnim = null; 455 } 456 }); 457 mDropAnim.start(); 458 } 459 clearAnimatedView()460 public void clearAnimatedView() { 461 if (mDropAnim != null) { 462 mDropAnim.cancel(); 463 } 464 mDropAnim = null; 465 if (mDropView != null) { 466 mDragController.onDeferredEndDrag(mDropView); 467 } 468 mDropView = null; 469 invalidate(); 470 } 471 getAnimatedView()472 public View getAnimatedView() { 473 return mDropView; 474 } 475 476 @Override onViewAdded(View child)477 public void onViewAdded(View child) { 478 super.onViewAdded(child); 479 updateChildIndices(); 480 UiFactory.onLauncherStateOrFocusChanged(mActivity); 481 } 482 483 @Override onViewRemoved(View child)484 public void onViewRemoved(View child) { 485 super.onViewRemoved(child); 486 updateChildIndices(); 487 UiFactory.onLauncherStateOrFocusChanged(mActivity); 488 } 489 490 @Override bringChildToFront(View child)491 public void bringChildToFront(View child) { 492 super.bringChildToFront(child); 493 updateChildIndices(); 494 } 495 updateChildIndices()496 private void updateChildIndices() { 497 mTopViewIndex = -1; 498 int childCount = getChildCount(); 499 for (int i = 0; i < childCount; i++) { 500 if (getChildAt(i) instanceof DragView) { 501 mTopViewIndex = i; 502 } 503 } 504 mChildCountOnLastUpdate = childCount; 505 } 506 507 @Override getChildDrawingOrder(int childCount, int i)508 protected int getChildDrawingOrder(int childCount, int i) { 509 if (mChildCountOnLastUpdate != childCount) { 510 // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed. 511 // Pre-18, the child was not added / removed by the time of those callbacks. We need to 512 // force update our representation of things here to avoid crashing on pre-18 devices 513 // in certain instances. 514 updateChildIndices(); 515 } 516 517 // i represents the current draw iteration 518 if (mTopViewIndex == -1) { 519 // in general we do nothing 520 return i; 521 } else if (i == childCount - 1) { 522 // if we have a top index, we return it when drawing last item (highest z-order) 523 return mTopViewIndex; 524 } else if (i < mTopViewIndex) { 525 return i; 526 } else { 527 // for indexes greater than the top index, we fetch one item above to shift for the 528 // displacement of the top index 529 return i + 1; 530 } 531 } 532 533 @Override dispatchDraw(Canvas canvas)534 protected void dispatchDraw(Canvas canvas) { 535 // Draw the background below children. 536 mWorkspaceScrim.draw(canvas); 537 mOverviewScrim.updateCurrentScrimmedView(this); 538 mFocusIndicatorHelper.draw(canvas); 539 super.dispatchDraw(canvas); 540 } 541 542 @Override drawChild(Canvas canvas, View child, long drawingTime)543 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 544 if (child == mOverviewScrim.getScrimmedView()) { 545 mOverviewScrim.draw(canvas); 546 } 547 return super.drawChild(canvas, child, drawingTime); 548 } 549 550 @Override onSizeChanged(int w, int h, int oldw, int oldh)551 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 552 super.onSizeChanged(w, h, oldw, oldh); 553 mWorkspaceScrim.setSize(w, h); 554 } 555 556 @Override setInsets(Rect insets)557 public void setInsets(Rect insets) { 558 super.setInsets(insets); 559 mWorkspaceScrim.onInsetsChanged(insets, mAllowSysuiScrims); 560 mOverviewScrim.onInsetsChanged(insets); 561 } 562 getScrim()563 public WorkspaceAndHotseatScrim getScrim() { 564 return mWorkspaceScrim; 565 } 566 getOverviewScrim()567 public OverviewScrim getOverviewScrim() { 568 return mOverviewScrim; 569 } 570 571 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)572 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 573 RotationMode rotation = mActivity.getRotationMode(); 574 int count = getChildCount(); 575 576 if (!rotation.isTransposed 577 || getMode(widthMeasureSpec) != EXACTLY 578 || getMode(heightMeasureSpec) != EXACTLY) { 579 580 for (int i = 0; i < count; i++) { 581 final View child = getChildAt(i); 582 child.setRotation(rotation.surfaceRotation); 583 } 584 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 585 } else { 586 587 for (int i = 0; i < count; i++) { 588 final View child = getChildAt(i); 589 if (child.getVisibility() == GONE) { 590 continue; 591 } 592 if (!(child instanceof Transposable)) { 593 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); 594 } else { 595 measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0); 596 597 child.setPivotX(child.getMeasuredWidth() / 2); 598 child.setPivotY(child.getMeasuredHeight() / 2); 599 child.setRotation(rotation.surfaceRotation); 600 } 601 } 602 setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec)); 603 } 604 } 605 606 @Override onLayout(boolean changed, int left, int top, int right, int bottom)607 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 608 RotationMode rotation = mActivity.getRotationMode(); 609 if (!rotation.isTransposed) { 610 super.onLayout(changed, left, top, right, bottom); 611 return; 612 } 613 614 final int count = getChildCount(); 615 616 final int parentWidth = right - left; 617 final int parentHeight = bottom - top; 618 619 for (int i = 0; i < count; i++) { 620 final View child = getChildAt(i); 621 if (child.getVisibility() == GONE) { 622 continue; 623 } 624 625 final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); 626 627 if (lp instanceof LayoutParams) { 628 final LayoutParams dlp = (LayoutParams) lp; 629 if (dlp.customPosition) { 630 child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height); 631 continue; 632 } 633 } 634 635 final int width = child.getMeasuredWidth(); 636 final int height = child.getMeasuredHeight(); 637 638 int childLeft; 639 int childTop; 640 641 int gravity = lp.gravity; 642 if (gravity == -1) { 643 gravity = Gravity.TOP | Gravity.START; 644 } 645 646 final int layoutDirection = getLayoutDirection(); 647 648 int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 649 650 if (child instanceof Transposable) { 651 absoluteGravity = rotation.toNaturalGravity(absoluteGravity); 652 653 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 654 case Gravity.CENTER_HORIZONTAL: 655 childTop = (parentHeight - height) / 2 + 656 lp.topMargin - lp.bottomMargin; 657 break; 658 case Gravity.RIGHT: 659 childTop = width / 2 + lp.rightMargin - height / 2; 660 break; 661 case Gravity.LEFT: 662 default: 663 childTop = parentHeight - lp.leftMargin - width / 2 - height / 2; 664 } 665 666 switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { 667 case Gravity.CENTER_VERTICAL: 668 childLeft = (parentWidth - width) / 2 + 669 lp.leftMargin - lp.rightMargin; 670 break; 671 case Gravity.BOTTOM: 672 childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin; 673 break; 674 case Gravity.TOP: 675 default: 676 childLeft = height / 2 - width / 2 + lp.topMargin; 677 } 678 } else { 679 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 680 case Gravity.CENTER_HORIZONTAL: 681 childLeft = (parentWidth - width) / 2 + 682 lp.leftMargin - lp.rightMargin; 683 break; 684 case Gravity.RIGHT: 685 childLeft = parentWidth - width - lp.rightMargin; 686 break; 687 case Gravity.LEFT: 688 default: 689 childLeft = lp.leftMargin; 690 } 691 692 switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { 693 case Gravity.TOP: 694 childTop = lp.topMargin; 695 break; 696 case Gravity.CENTER_VERTICAL: 697 childTop = (parentHeight - height) / 2 + 698 lp.topMargin - lp.bottomMargin; 699 break; 700 case Gravity.BOTTOM: 701 childTop = parentHeight - height - lp.bottomMargin; 702 break; 703 default: 704 childTop = lp.topMargin; 705 } 706 } 707 708 child.layout(childLeft, childTop, childLeft + width, childTop + height); 709 } 710 } 711 } 712