Home | History | Annotate | Download | only in dragndrop
      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 com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
     21 
     22 import android.animation.Animator;
     23 import android.animation.AnimatorListenerAdapter;
     24 import android.animation.TimeInterpolator;
     25 import android.animation.ValueAnimator;
     26 import android.animation.ValueAnimator.AnimatorUpdateListener;
     27 import android.content.Context;
     28 import android.content.res.Resources;
     29 import android.graphics.Canvas;
     30 import android.graphics.Rect;
     31 import android.util.AttributeSet;
     32 import android.view.KeyEvent;
     33 import android.view.MotionEvent;
     34 import android.view.View;
     35 import android.view.accessibility.AccessibilityEvent;
     36 import android.view.accessibility.AccessibilityManager;
     37 import android.view.animation.Interpolator;
     38 import android.widget.TextView;
     39 
     40 import com.android.launcher3.AbstractFloatingView;
     41 import com.android.launcher3.CellLayout;
     42 import com.android.launcher3.DropTargetBar;
     43 import com.android.launcher3.Launcher;
     44 import com.android.launcher3.R;
     45 import com.android.launcher3.ShortcutAndWidgetContainer;
     46 import com.android.launcher3.Workspace;
     47 import com.android.launcher3.anim.Interpolators;
     48 import com.android.launcher3.folder.Folder;
     49 import com.android.launcher3.folder.FolderIcon;
     50 import com.android.launcher3.graphics.ViewScrim;
     51 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
     52 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
     53 import com.android.launcher3.uioverrides.UiFactory;
     54 import com.android.launcher3.util.Thunk;
     55 import com.android.launcher3.views.BaseDragLayer;
     56 
     57 import java.util.ArrayList;
     58 
     59 /**
     60  * A ViewGroup that coordinates dragging across its descendants
     61  */
     62 public class DragLayer extends BaseDragLayer<Launcher> {
     63 
     64     public static final int ALPHA_INDEX_OVERLAY = 0;
     65     public static final int ALPHA_INDEX_LAUNCHER_LOAD = 1;
     66     public static final int ALPHA_INDEX_TRANSITIONS = 2;
     67     public static final int ALPHA_INDEX_SWIPE_UP = 3;
     68     private static final int ALPHA_CHANNEL_COUNT = 4;
     69 
     70     public static final int ANIMATION_END_DISAPPEAR = 0;
     71     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
     72 
     73     @Thunk DragController mDragController;
     74 
     75     // Variables relating to animation of views after drop
     76     private ValueAnimator mDropAnim = null;
     77     private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
     78     @Thunk DragView mDropView = null;
     79     @Thunk int mAnchorViewInitialScrollX = 0;
     80     @Thunk View mAnchorView = null;
     81 
     82     private boolean mHoverPointClosesFolder = false;
     83 
     84     private int mTopViewIndex;
     85     private int mChildCountOnLastUpdate = -1;
     86 
     87     // Related to adjacent page hints
     88     private final ViewGroupFocusHelper mFocusIndicatorHelper;
     89     private final WorkspaceAndHotseatScrim mScrim;
     90 
     91     /**
     92      * Used to create a new DragLayer from XML.
     93      *
     94      * @param context The application's context.
     95      * @param attrs The attributes set containing the Workspace's customization values.
     96      */
     97     public DragLayer(Context context, AttributeSet attrs) {
     98         super(context, attrs, ALPHA_CHANNEL_COUNT);
     99 
    100         // Disable multitouch across the workspace/all apps/customize tray
    101         setMotionEventSplittingEnabled(false);
    102         setChildrenDrawingOrderEnabled(true);
    103 
    104         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
    105         mScrim = new WorkspaceAndHotseatScrim(this);
    106     }
    107 
    108     public void setup(DragController dragController, Workspace workspace) {
    109         mDragController = dragController;
    110         mScrim.setWorkspace(workspace);
    111         recreateControllers();
    112     }
    113 
    114     public void recreateControllers() {
    115         mControllers = UiFactory.createTouchControllers(mActivity);
    116     }
    117 
    118     public ViewGroupFocusHelper getFocusIndicatorHelper() {
    119         return mFocusIndicatorHelper;
    120     }
    121 
    122     @Override
    123     public boolean dispatchKeyEvent(KeyEvent event) {
    124         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
    125     }
    126 
    127     @Override
    128     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    129         ViewScrim scrim = ViewScrim.get(child);
    130         if (scrim != null) {
    131             scrim.draw(canvas, getWidth(), getHeight());
    132         }
    133         return super.drawChild(canvas, child, drawingTime);
    134     }
    135 
    136     @Override
    137     protected boolean findActiveController(MotionEvent ev) {
    138         if (mActivity.getStateManager().getState().disableInteraction) {
    139             // You Shall Not Pass!!!
    140             mActiveController = null;
    141             return true;
    142         }
    143         return super.findActiveController(ev);
    144     }
    145 
    146     private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
    147         return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
    148     }
    149 
    150     @Override
    151     public boolean onInterceptHoverEvent(MotionEvent ev) {
    152         if (mActivity == null || mActivity.getWorkspace() == null) {
    153             return false;
    154         }
    155         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
    156         if (!(topView instanceof Folder)) {
    157             return false;
    158         } else {
    159             AccessibilityManager accessibilityManager = (AccessibilityManager)
    160                     getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
    161             if (accessibilityManager.isTouchExplorationEnabled()) {
    162                 Folder currentFolder = (Folder) topView;
    163                 final int action = ev.getAction();
    164                 boolean isOverFolderOrSearchBar;
    165                 switch (action) {
    166                     case MotionEvent.ACTION_HOVER_ENTER:
    167                         isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
    168                                 isEventOverAccessibleDropTargetBar(ev);
    169                         if (!isOverFolderOrSearchBar) {
    170                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
    171                             mHoverPointClosesFolder = true;
    172                             return true;
    173                         }
    174                         mHoverPointClosesFolder = false;
    175                         break;
    176                     case MotionEvent.ACTION_HOVER_MOVE:
    177                         isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
    178                                 isEventOverAccessibleDropTargetBar(ev);
    179                         if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
    180                             sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
    181                             mHoverPointClosesFolder = true;
    182                             return true;
    183                         } else if (!isOverFolderOrSearchBar) {
    184                             return true;
    185                         }
    186                         mHoverPointClosesFolder = false;
    187                 }
    188             }
    189         }
    190         return false;
    191     }
    192 
    193     private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
    194         int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
    195         sendCustomAccessibilityEvent(
    196                 this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
    197     }
    198 
    199     @Override
    200     public boolean onHoverEvent(MotionEvent ev) {
    201         // If we've received this, we've already done the necessary handling
    202         // in onInterceptHoverEvent. Return true to consume the event.
    203         return false;
    204     }
    205 
    206 
    207     private boolean isInAccessibleDrag() {
    208         return mActivity.getAccessibilityDelegate().isInAccessibleDrag();
    209     }
    210 
    211     @Override
    212     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
    213         if (isInAccessibleDrag() && child instanceof DropTargetBar) {
    214             return true;
    215         }
    216         return super.onRequestSendAccessibilityEvent(child, event);
    217     }
    218 
    219     @Override
    220     public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
    221         View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity,
    222                 AbstractFloatingView.TYPE_ACCESSIBLE);
    223         if (topView != null) {
    224             addAccessibleChildToList(topView, childrenForAccessibility);
    225             if (isInAccessibleDrag()) {
    226                 addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility);
    227             }
    228         } else {
    229             super.addChildrenForAccessibility(childrenForAccessibility);
    230         }
    231     }
    232 
    233     @Override
    234     public boolean dispatchUnhandledMove(View focused, int direction) {
    235         return super.dispatchUnhandledMove(focused, direction)
    236                 || mDragController.dispatchUnhandledMove(focused, direction);
    237     }
    238 
    239     @Override
    240     public boolean dispatchTouchEvent(MotionEvent ev) {
    241         ev.offsetLocation(getTranslationX(), 0);
    242         try {
    243             return super.dispatchTouchEvent(ev);
    244         } finally {
    245             ev.offsetLocation(-getTranslationX(), 0);
    246         }
    247     }
    248 
    249     public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
    250             float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
    251             int duration) {
    252         Rect r = new Rect();
    253         getViewRectRelativeToSelf(dragView, r);
    254         final int fromX = r.left;
    255         final int fromY = r.top;
    256 
    257         animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
    258                 onFinishRunnable, animationEndStyle, duration, null);
    259     }
    260 
    261     public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) {
    262         animateViewIntoPosition(dragView, child, -1, anchorView);
    263     }
    264 
    265     public void animateViewIntoPosition(DragView dragView, final View child, int duration,
    266             View anchorView) {
    267         ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
    268         CellLayout.LayoutParams lp =  (CellLayout.LayoutParams) child.getLayoutParams();
    269         parentChildren.measureChild(child);
    270 
    271         Rect r = new Rect();
    272         getViewRectRelativeToSelf(dragView, r);
    273 
    274         int coord[] = new int[2];
    275         float childScale = child.getScaleX();
    276         coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
    277         coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
    278 
    279         // Since the child hasn't necessarily been laid out, we force the lp to be updated with
    280         // the correct coordinates (above) and use these to determine the final location
    281         float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
    282         // We need to account for the scale of the child itself, as the above only accounts for
    283         // for the scale in parents.
    284         scale *= childScale;
    285         int toX = coord[0];
    286         int toY = coord[1];
    287         float toScale = scale;
    288         if (child instanceof TextView) {
    289             TextView tv = (TextView) child;
    290             // Account for the source scale of the icon (ie. from AllApps to Workspace, in which
    291             // the workspace may have smaller icon bounds).
    292             toScale = scale / dragView.getIntrinsicIconScaleFactor();
    293 
    294             // The child may be scaled (always about the center of the view) so to account for it,
    295             // we have to offset the position by the scaled size.  Once we do that, we can center
    296             // the drag view about the scaled child view.
    297             toY += Math.round(toScale * tv.getPaddingTop());
    298             toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
    299             if (dragView.getDragVisualizeOffset() != null) {
    300                 toY -=  Math.round(toScale * dragView.getDragVisualizeOffset().y);
    301             }
    302 
    303             toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
    304         } else if (child instanceof FolderIcon) {
    305             // Account for holographic blur padding on the drag view
    306             toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop()));
    307             toY -= scale * dragView.getBlurSizeOutline() / 2;
    308             toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
    309             // Center in the x coordinate about the target's drawable
    310             toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
    311         } else {
    312             toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
    313             toX -= (Math.round(scale * (dragView.getMeasuredWidth()
    314                     - child.getMeasuredWidth()))) / 2;
    315         }
    316 
    317         final int fromX = r.left;
    318         final int fromY = r.top;
    319         child.setVisibility(INVISIBLE);
    320         Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
    321         animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
    322                 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
    323     }
    324 
    325     public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
    326             final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
    327             float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
    328             int animationEndStyle, int duration, View anchorView) {
    329         Rect from = new Rect(fromX, fromY, fromX +
    330                 view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
    331         Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
    332         animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
    333                 null, null, onCompleteRunnable, animationEndStyle, anchorView);
    334     }
    335 
    336     /**
    337      * This method animates a view at the end of a drag and drop animation.
    338      *
    339      * @param view The view to be animated. This view is drawn directly into DragLayer, and so
    340      *        doesn't need to be a child of DragLayer.
    341      * @param from The initial location of the view. Only the left and top parameters are used.
    342      * @param to The final location of the view. Only the left and top parameters are used. This
    343      *        location doesn't account for scaling, and so should be centered about the desired
    344      *        final location (including scaling).
    345      * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
    346      * @param finalScaleX The final scale of the view. The view is scaled about its center.
    347      * @param finalScaleY The final scale of the view. The view is scaled about its center.
    348      * @param duration The duration of the animation.
    349      * @param motionInterpolator The interpolator to use for the location of the view.
    350      * @param alphaInterpolator The interpolator to use for the alpha of the view.
    351      * @param onCompleteRunnable Optional runnable to run on animation completion.
    352      * @param animationEndStyle Whether or not to fade out the view once the animation completes.
    353      *        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
    354      * @param anchorView If not null, this represents the view which the animated view stays
    355      *        anchored to in case scrolling is currently taking place. Note: currently this is
    356      *        only used for the X dimension for the case of the workspace.
    357      */
    358     public void animateView(final DragView view, final Rect from, final Rect to,
    359             final float finalAlpha, final float initScaleX, final float initScaleY,
    360             final float finalScaleX, final float finalScaleY, int duration,
    361             final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
    362             final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
    363 
    364         // Calculate the duration of the animation based on the object's distance
    365         final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top);
    366         final Resources res = getResources();
    367         final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
    368 
    369         // If duration < 0, this is a cue to compute the duration based on the distance
    370         if (duration < 0) {
    371             duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
    372             if (dist < maxDist) {
    373                 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
    374             }
    375             duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
    376         }
    377 
    378         // Fall back to cubic ease out interpolator for the animation if none is specified
    379         TimeInterpolator interpolator = null;
    380         if (alphaInterpolator == null || motionInterpolator == null) {
    381             interpolator = mCubicEaseOutInterpolator;
    382         }
    383 
    384         // Animate the view
    385         final float initAlpha = view.getAlpha();
    386         final float dropViewScale = view.getScaleX();
    387         AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
    388             @Override
    389             public void onAnimationUpdate(ValueAnimator animation) {
    390                 final float percent = (Float) animation.getAnimatedValue();
    391                 final int width = view.getMeasuredWidth();
    392                 final int height = view.getMeasuredHeight();
    393 
    394                 float alphaPercent = alphaInterpolator == null ? percent :
    395                         alphaInterpolator.getInterpolation(percent);
    396                 float motionPercent = motionInterpolator == null ? percent :
    397                         motionInterpolator.getInterpolation(percent);
    398 
    399                 float initialScaleX = initScaleX * dropViewScale;
    400                 float initialScaleY = initScaleY * dropViewScale;
    401                 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
    402                 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
    403                 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
    404 
    405                 float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
    406                 float fromTop = from.top + (initialScaleY - 1f) * height / 2;
    407 
    408                 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
    409                 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
    410 
    411                 int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
    412                     (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
    413 
    414                 int xPos = x - mDropView.getScrollX() + anchorAdjust;
    415                 int yPos = y - mDropView.getScrollY();
    416 
    417                 mDropView.setTranslationX(xPos);
    418                 mDropView.setTranslationY(yPos);
    419                 mDropView.setScaleX(scaleX);
    420                 mDropView.setScaleY(scaleY);
    421                 mDropView.setAlpha(alpha);
    422             }
    423         };
    424         animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
    425                 anchorView);
    426     }
    427 
    428     public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
    429             TimeInterpolator interpolator, final Runnable onCompleteRunnable,
    430             final int animationEndStyle, View anchorView) {
    431         // Clean up the previous animations
    432         if (mDropAnim != null) mDropAnim.cancel();
    433 
    434         // Show the drop view if it was previously hidden
    435         mDropView = view;
    436         mDropView.cancelAnimation();
    437         mDropView.requestLayout();
    438 
    439         // Set the anchor view if the page is scrolling
    440         if (anchorView != null) {
    441             mAnchorViewInitialScrollX = anchorView.getScrollX();
    442         }
    443         mAnchorView = anchorView;
    444 
    445         // Create and start the animation
    446         mDropAnim = new ValueAnimator();
    447         mDropAnim.setInterpolator(interpolator);
    448         mDropAnim.setDuration(duration);
    449         mDropAnim.setFloatValues(0f, 1f);
    450         mDropAnim.addUpdateListener(updateCb);
    451         mDropAnim.addListener(new AnimatorListenerAdapter() {
    452             public void onAnimationEnd(Animator animation) {
    453                 if (onCompleteRunnable != null) {
    454                     onCompleteRunnable.run();
    455                 }
    456                 switch (animationEndStyle) {
    457                 case ANIMATION_END_DISAPPEAR:
    458                     clearAnimatedView();
    459                     break;
    460                 case ANIMATION_END_REMAIN_VISIBLE:
    461                     break;
    462                 }
    463                 mDropAnim = null;
    464             }
    465         });
    466         mDropAnim.start();
    467     }
    468 
    469     public void clearAnimatedView() {
    470         if (mDropAnim != null) {
    471             mDropAnim.cancel();
    472         }
    473         mDropAnim = null;
    474         if (mDropView != null) {
    475             mDragController.onDeferredEndDrag(mDropView);
    476         }
    477         mDropView = null;
    478         invalidate();
    479     }
    480 
    481     public View getAnimatedView() {
    482         return mDropView;
    483     }
    484 
    485     @Override
    486     public void onViewAdded(View child) {
    487         super.onViewAdded(child);
    488         updateChildIndices();
    489         UiFactory.onLauncherStateOrFocusChanged(mActivity);
    490     }
    491 
    492     @Override
    493     public void onViewRemoved(View child) {
    494         super.onViewRemoved(child);
    495         updateChildIndices();
    496         UiFactory.onLauncherStateOrFocusChanged(mActivity);
    497     }
    498 
    499     @Override
    500     public void bringChildToFront(View child) {
    501         super.bringChildToFront(child);
    502         updateChildIndices();
    503     }
    504 
    505     private void updateChildIndices() {
    506         mTopViewIndex = -1;
    507         int childCount = getChildCount();
    508         for (int i = 0; i < childCount; i++) {
    509             if (getChildAt(i) instanceof DragView) {
    510                 mTopViewIndex = i;
    511             }
    512         }
    513         mChildCountOnLastUpdate = childCount;
    514     }
    515 
    516     @Override
    517     protected int getChildDrawingOrder(int childCount, int i) {
    518         if (mChildCountOnLastUpdate != childCount) {
    519             // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
    520             // Pre-18, the child was not added / removed by the time of those callbacks. We need to
    521             // force update our representation of things here to avoid crashing on pre-18 devices
    522             // in certain instances.
    523             updateChildIndices();
    524         }
    525 
    526         // i represents the current draw iteration
    527         if (mTopViewIndex == -1) {
    528             // in general we do nothing
    529             return i;
    530         } else if (i == childCount - 1) {
    531             // if we have a top index, we return it when drawing last item (highest z-order)
    532             return mTopViewIndex;
    533         } else if (i < mTopViewIndex) {
    534             return i;
    535         } else {
    536             // for indexes greater than the top index, we fetch one item above to shift for the
    537             // displacement of the top index
    538             return i + 1;
    539         }
    540     }
    541 
    542     @Override
    543     protected void dispatchDraw(Canvas canvas) {
    544         // Draw the background below children.
    545         mScrim.draw(canvas);
    546         mFocusIndicatorHelper.draw(canvas);
    547         super.dispatchDraw(canvas);
    548     }
    549 
    550     @Override
    551     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    552         super.onSizeChanged(w, h, oldw, oldh);
    553         mScrim.setSize(w, h);
    554     }
    555 
    556     @Override
    557     public void setInsets(Rect insets) {
    558         super.setInsets(insets);
    559         mScrim.onInsetsChanged(insets);
    560     }
    561 
    562     public WorkspaceAndHotseatScrim getScrim() {
    563         return mScrim;
    564     }
    565 }
    566