Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2011 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 
     17 package com.android.launcher3;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Resources;
     25 import android.content.res.TypedArray;
     26 import android.graphics.*;
     27 import android.graphics.drawable.Drawable;
     28 import android.util.AttributeSet;
     29 import android.util.DisplayMetrics;
     30 import android.view.FocusFinder;
     31 import android.view.MotionEvent;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 import android.view.animation.AccelerateInterpolator;
     35 import android.widget.FrameLayout;
     36 import android.widget.TextView;
     37 
     38 public class Cling extends FrameLayout implements Insettable, View.OnClickListener,
     39         View.OnLongClickListener, View.OnTouchListener {
     40 
     41     private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
     42     private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
     43 
     44     private static String WORKSPACE_PORTRAIT = "workspace_portrait";
     45     private static String WORKSPACE_LANDSCAPE = "workspace_landscape";
     46     private static String WORKSPACE_LARGE = "workspace_large";
     47     private static String WORKSPACE_CUSTOM = "workspace_custom";
     48 
     49     private static String MIGRATION_PORTRAIT = "migration_portrait";
     50     private static String MIGRATION_LANDSCAPE = "migration_landscape";
     51 
     52     private static String MIGRATION_WORKSPACE_PORTRAIT = "migration_workspace_portrait";
     53     private static String MIGRATION_WORKSPACE_LARGE_PORTRAIT = "migration_workspace_large_portrait";
     54     private static String MIGRATION_WORKSPACE_LANDSCAPE = "migration_workspace_landscape";
     55 
     56     private static String FOLDER_PORTRAIT = "folder_portrait";
     57     private static String FOLDER_LANDSCAPE = "folder_landscape";
     58     private static String FOLDER_LARGE = "folder_large";
     59 
     60     private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 60;
     61     private static float FIRST_RUN_MAX_CIRCLE_RADIUS_DPS = 180;
     62     private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
     63     private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
     64     private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
     65     private static float MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 42;
     66     private static float MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 46;
     67 
     68     private Launcher mLauncher;
     69     private boolean mIsInitialized;
     70     private String mDrawIdentifier;
     71     private Drawable mBackground;
     72 
     73     private int[] mTouchDownPt = new int[2];
     74 
     75     private Drawable mFocusedHotseatApp;
     76     private ComponentName mFocusedHotseatAppComponent;
     77     private Rect mFocusedHotseatAppBounds;
     78 
     79     private Paint mErasePaint;
     80     private Paint mBorderPaint;
     81     private Paint mBubblePaint;
     82     private Paint mDotPaint;
     83 
     84     private View mScrimView;
     85     private int mBackgroundColor;
     86 
     87     private final Rect mInsets = new Rect();
     88 
     89     public Cling(Context context) {
     90         this(context, null, 0);
     91     }
     92 
     93     public Cling(Context context, AttributeSet attrs) {
     94         this(context, attrs, 0);
     95     }
     96 
     97     public Cling(Context context, AttributeSet attrs, int defStyle) {
     98         super(context, attrs, defStyle);
     99 
    100         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0);
    101         mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier);
    102         a.recycle();
    103 
    104         setClickable(true);
    105 
    106     }
    107 
    108     void init(Launcher l, View scrim) {
    109         if (!mIsInitialized) {
    110             mLauncher = l;
    111             mScrimView = scrim;
    112             mBackgroundColor = 0xcc000000;
    113             setOnLongClickListener(this);
    114             setOnClickListener(this);
    115             setOnTouchListener(this);
    116 
    117             mErasePaint = new Paint();
    118             mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    119             mErasePaint.setColor(0xFFFFFF);
    120             mErasePaint.setAlpha(0);
    121             mErasePaint.setAntiAlias(true);
    122 
    123             mBorderPaint = new Paint();
    124             mBorderPaint.setColor(0xFFFFFFFF);
    125             mBorderPaint.setAntiAlias(true);
    126 
    127             int circleColor = getResources().getColor(
    128                     R.color.first_run_cling_circle_background_color);
    129             mBubblePaint = new Paint();
    130             mBubblePaint.setColor(circleColor);
    131             mBubblePaint.setAntiAlias(true);
    132 
    133             mDotPaint = new Paint();
    134             mDotPaint.setColor(0x72BBED);
    135             mDotPaint.setAntiAlias(true);
    136 
    137             mIsInitialized = true;
    138         }
    139     }
    140 
    141     void setFocusedHotseatApp(int drawableId, int appRank, ComponentName cn, String title,
    142                               String description) {
    143         // Get the app to draw
    144         Resources r = getResources();
    145         int appIconId = drawableId;
    146         Hotseat hotseat = mLauncher.getHotseat();
    147         // Skip the focused app in the large layouts
    148         if (!mDrawIdentifier.equals(WORKSPACE_LARGE) &&
    149                 hotseat != null && appIconId > -1 && appRank > -1 && !title.isEmpty() &&
    150                 !description.isEmpty()) {
    151             // Set the app bounds
    152             int x = hotseat.getCellXFromOrder(appRank);
    153             int y = hotseat.getCellYFromOrder(appRank);
    154             Rect pos = hotseat.getCellCoordinates(x, y);
    155             LauncherAppState app = LauncherAppState.getInstance();
    156             DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    157             mFocusedHotseatApp = getResources().getDrawable(appIconId);
    158             mFocusedHotseatAppComponent = cn;
    159             mFocusedHotseatAppBounds = new Rect(pos.left, pos.top,
    160                     pos.left + Utilities.sIconTextureWidth,
    161                     pos.top + Utilities.sIconTextureHeight);
    162             Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds,
    163                     ((float) grid.hotseatIconSizePx / grid.iconSizePx));
    164 
    165             // Set the title
    166             TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title);
    167             if (v != null) {
    168                 v.setText(title);
    169             }
    170 
    171             // Set the description
    172             v = (TextView) findViewById(R.id.focused_hotseat_app_description);
    173             if (v != null) {
    174                 v.setText(description);
    175             }
    176 
    177             // Show the bubble
    178             View bubble = findViewById(R.id.focused_hotseat_app_bubble);
    179             bubble.setVisibility(View.VISIBLE);
    180         }
    181     }
    182 
    183     void setOpenFolderRect(Rect r) {
    184         if (mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
    185             mDrawIdentifier.equals(FOLDER_LARGE)) {
    186             ViewGroup vg = (ViewGroup) findViewById(R.id.folder_bubble);
    187             ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) vg.getLayoutParams();
    188             lp.topMargin = r.top - mInsets.bottom;
    189             lp.leftMargin = r.right;
    190             vg.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
    191             vg.requestLayout();
    192         }
    193     }
    194 
    195     void updateMigrationWorkspaceBubblePosition() {
    196         DisplayMetrics metrics = new DisplayMetrics();
    197         mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    198 
    199         // Get the page indicator bounds
    200         LauncherAppState app = LauncherAppState.getInstance();
    201         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    202         Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
    203 
    204         if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT)) {
    205             View bubble = findViewById(R.id.migration_workspace_cling_bubble);
    206             ViewGroup.MarginLayoutParams lp =
    207                     (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
    208             lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top;
    209             bubble.requestLayout();
    210         } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT)) {
    211             View bubble = findViewById(R.id.content);
    212             ViewGroup.MarginLayoutParams lp =
    213                     (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
    214             lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top;
    215             bubble.requestLayout();
    216         } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
    217             View bubble = findViewById(R.id.content);
    218             ViewGroup.MarginLayoutParams lp =
    219                     (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
    220             if (grid.isLayoutRtl) {
    221                 lp.leftMargin = pageIndicatorBounds.right;
    222             } else {
    223                 lp.rightMargin = (grid.widthPx - pageIndicatorBounds.left);
    224             }
    225             bubble.requestLayout();
    226         }
    227     }
    228 
    229     void updateWorkspaceBubblePosition() {
    230         DisplayMetrics metrics = new DisplayMetrics();
    231         mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    232 
    233         // Get the cut-out bounds
    234         LauncherAppState app = LauncherAppState.getInstance();
    235         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    236         Rect cutOutBounds = getWorkspaceCutOutBounds(metrics);
    237 
    238         if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
    239             View bubble = findViewById(R.id.workspace_cling_bubble);
    240             ViewGroup.MarginLayoutParams lp =
    241                     (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
    242             lp.bottomMargin = grid.heightPx - cutOutBounds.top - mInsets.bottom;
    243             bubble.requestLayout();
    244         }
    245     }
    246 
    247     private Rect getWorkspaceCutOutBounds(DisplayMetrics metrics) {
    248         int halfWidth = metrics.widthPixels / 2;
    249         int halfHeight = metrics.heightPixels / 2;
    250         int yOffset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
    251         if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
    252             yOffset = 0;
    253         }
    254         int radius = DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics);
    255         return new Rect(halfWidth - radius, halfHeight - yOffset - radius, halfWidth + radius,
    256                 halfHeight - yOffset + radius);
    257     }
    258 
    259     void show(boolean animate, int duration) {
    260         setVisibility(View.VISIBLE);
    261         setLayerType(View.LAYER_TYPE_HARDWARE, null);
    262         if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
    263                 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
    264                 mDrawIdentifier.equals(WORKSPACE_LARGE) ||
    265                 mDrawIdentifier.equals(WORKSPACE_CUSTOM) ||
    266                 mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
    267                 mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
    268                 mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
    269             View content = getContent();
    270             content.setAlpha(0f);
    271             content.animate()
    272                     .alpha(1f)
    273                     .setDuration(duration)
    274                     .setListener(null)
    275                     .start();
    276             setAlpha(1f);
    277         } else {
    278             if (animate) {
    279                 buildLayer();
    280                 setAlpha(0f);
    281                 animate()
    282                     .alpha(1f)
    283                     .setInterpolator(new AccelerateInterpolator())
    284                     .setDuration(duration)
    285                     .setListener(null)
    286                     .start();
    287             } else {
    288                 setAlpha(1f);
    289             }
    290         }
    291 
    292         // Show the scrim if necessary
    293         if (mScrimView != null) {
    294             mScrimView.setVisibility(View.VISIBLE);
    295             mScrimView.setAlpha(0f);
    296             mScrimView.animate()
    297                     .alpha(1f)
    298                     .setDuration(duration)
    299                     .setListener(null)
    300                     .start();
    301         }
    302 
    303         setFocusableInTouchMode(true);
    304         post(new Runnable() {
    305             public void run() {
    306                 setFocusable(true);
    307                 requestFocus();
    308             }
    309         });
    310     }
    311 
    312     void hide(final int duration, final Runnable postCb) {
    313         if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
    314                 mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE) ||
    315                 mDrawIdentifier.equals(MIGRATION_PORTRAIT) ||
    316                 mDrawIdentifier.equals(MIGRATION_LANDSCAPE)) {
    317             View content = getContent();
    318             content.animate()
    319                 .alpha(0f)
    320                 .setDuration(duration)
    321                 .setListener(new AnimatorListenerAdapter() {
    322                     public void onAnimationEnd(Animator animation) {
    323                         // We are about to trigger the workspace cling, so don't do anything else
    324                         setVisibility(View.GONE);
    325                         postCb.run();
    326                     };
    327                 })
    328                 .start();
    329         } else {
    330             animate()
    331                 .alpha(0f)
    332                 .setDuration(duration)
    333                 .setListener(new AnimatorListenerAdapter() {
    334                     public void onAnimationEnd(Animator animation) {
    335                         // We are about to trigger the workspace cling, so don't do anything else
    336                         setVisibility(View.GONE);
    337                         postCb.run();
    338                     };
    339                 })
    340                 .start();
    341         }
    342 
    343         // Show the scrim if necessary
    344         if (mScrimView != null) {
    345             mScrimView.animate()
    346                 .alpha(0f)
    347                 .setDuration(duration)
    348                 .setListener(new AnimatorListenerAdapter() {
    349                     public void onAnimationEnd(Animator animation) {
    350                         mScrimView.setVisibility(View.GONE);
    351                     };
    352                 })
    353                 .start();
    354         }
    355     }
    356 
    357     void cleanup() {
    358         mBackground = null;
    359         mIsInitialized = false;
    360     }
    361 
    362     void bringScrimToFront() {
    363         if (mScrimView != null) {
    364             mScrimView.bringToFront();
    365         }
    366     }
    367 
    368     @Override
    369     public void setInsets(Rect insets) {
    370         mInsets.set(insets);
    371         setPadding(insets.left, insets.top, insets.right, insets.bottom);
    372     }
    373 
    374     View getContent() {
    375         return findViewById(R.id.content);
    376     }
    377 
    378     String getDrawIdentifier() {
    379         return mDrawIdentifier;
    380     }
    381 
    382     @Override
    383     public View focusSearch(int direction) {
    384         return this.focusSearch(this, direction);
    385     }
    386 
    387     @Override
    388     public View focusSearch(View focused, int direction) {
    389         return FocusFinder.getInstance().findNextFocus(this, focused, direction);
    390     }
    391 
    392     @Override
    393     public boolean onHoverEvent(MotionEvent event) {
    394         return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)
    395                 || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)
    396                 || mDrawIdentifier.equals(WORKSPACE_LARGE)
    397                 || mDrawIdentifier.equals(WORKSPACE_CUSTOM));
    398     }
    399 
    400     @Override
    401     public boolean onTouchEvent(android.view.MotionEvent event) {
    402         if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
    403                    mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
    404                    mDrawIdentifier.equals(FOLDER_LARGE)) {
    405             Folder f = mLauncher.getWorkspace().getOpenFolder();
    406             if (f != null) {
    407                 Rect r = new Rect();
    408                 f.getHitRect(r);
    409                 if (r.contains((int) event.getX(), (int) event.getY())) {
    410                     return false;
    411                 }
    412             }
    413         }
    414         return super.onTouchEvent(event);
    415     };
    416 
    417     @Override
    418     public boolean onTouch(View v, MotionEvent ev) {
    419         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    420             mTouchDownPt[0] = (int) ev.getX();
    421             mTouchDownPt[1] = (int) ev.getY();
    422         }
    423         return false;
    424     }
    425 
    426     @Override
    427     public void onClick(View v) {
    428         if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
    429                 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
    430                 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
    431             if (mFocusedHotseatAppBounds != null &&
    432                 mFocusedHotseatAppBounds.contains(mTouchDownPt[0], mTouchDownPt[1])) {
    433                 // Launch the activity that is being highlighted
    434                 Intent intent = new Intent(Intent.ACTION_MAIN);
    435                 intent.setComponent(mFocusedHotseatAppComponent);
    436                 intent.addCategory(Intent.CATEGORY_LAUNCHER);
    437                 mLauncher.startActivity(intent, null);
    438                 mLauncher.getLauncherClings().dismissWorkspaceCling(this);
    439             }
    440         }
    441     }
    442 
    443     @Override
    444     public boolean onLongClick(View v) {
    445         if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
    446                 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
    447                 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
    448             mLauncher.getLauncherClings().dismissWorkspaceCling(null);
    449             return true;
    450         } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
    451                 mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
    452                 mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
    453             mLauncher.getLauncherClings().dismissMigrationWorkspaceCling(null);
    454             return true;
    455         }
    456         return false;
    457     }
    458 
    459     @Override
    460     protected void dispatchDraw(Canvas canvas) {
    461         if (mIsInitialized) {
    462             canvas.save();
    463 
    464             // Get the page indicator bounds
    465             LauncherAppState app = LauncherAppState.getInstance();
    466             DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    467             Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
    468 
    469             // Get the background override if there is one
    470             if (mBackground == null) {
    471                 if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
    472                     mBackground = getResources().getDrawable(R.drawable.bg_cling5);
    473                 }
    474             }
    475             // Draw the background
    476             Bitmap eraseBg = null;
    477             Canvas eraseCanvas = null;
    478             if (mScrimView != null) {
    479                 // Skip drawing the background
    480                 mScrimView.setBackgroundColor(mBackgroundColor);
    481             } else if (mBackground != null) {
    482                 mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
    483                 mBackground.draw(canvas);
    484             } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
    485                     mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
    486                     mDrawIdentifier.equals(WORKSPACE_LARGE) ||
    487                     mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
    488                     mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
    489                     mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
    490                 // Initialize the draw buffer (to allow punching through)
    491                 eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
    492                         Bitmap.Config.ARGB_8888);
    493                 eraseCanvas = new Canvas(eraseBg);
    494                 eraseCanvas.drawColor(mBackgroundColor);
    495             } else {
    496                 canvas.drawColor(mBackgroundColor);
    497             }
    498 
    499             // Draw everything else
    500             DisplayMetrics metrics = new DisplayMetrics();
    501             mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    502             float alpha = getAlpha();
    503             View content = getContent();
    504             if (content != null) {
    505                 alpha *= content.getAlpha();
    506             }
    507             if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
    508                     mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
    509                 // Draw the circle
    510                 View bubbleContent = findViewById(R.id.bubble_content);
    511                 Rect bubbleRect = new Rect();
    512                 bubbleContent.getGlobalVisibleRect(bubbleRect);
    513                 mBubblePaint.setAlpha((int) (255 * alpha));
    514                 float buffer = DynamicGrid.pxFromDp(FIRST_RUN_CIRCLE_BUFFER_DPS, metrics);
    515                 float maxRadius = DynamicGrid.pxFromDp(FIRST_RUN_MAX_CIRCLE_RADIUS_DPS, metrics);
    516                 float radius = Math.min(maxRadius, (bubbleContent.getMeasuredWidth() + buffer) / 2);
    517                 canvas.drawCircle(metrics.widthPixels / 2,
    518                         bubbleRect.centerY(), radius,
    519                         mBubblePaint);
    520             } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
    521                     mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
    522                     mDrawIdentifier.equals(WORKSPACE_LARGE)) {
    523                 Rect cutOutBounds = getWorkspaceCutOutBounds(metrics);
    524                 // Draw the outer circle
    525                 mErasePaint.setAlpha(128);
    526                 eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(),
    527                         DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
    528                         mErasePaint);
    529                 // Draw the inner circle
    530                 mErasePaint.setAlpha(0);
    531                 eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(),
    532                         DynamicGrid.pxFromDp(WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
    533                         mErasePaint);
    534                 canvas.drawBitmap(eraseBg, 0, 0, null);
    535                 eraseCanvas.setBitmap(null);
    536                 eraseBg = null;
    537 
    538                 // Draw the focused hotseat app icon
    539                 if (mFocusedHotseatAppBounds != null && mFocusedHotseatApp != null) {
    540                     mFocusedHotseatApp.setBounds(mFocusedHotseatAppBounds.left,
    541                             mFocusedHotseatAppBounds.top, mFocusedHotseatAppBounds.right,
    542                             mFocusedHotseatAppBounds.bottom);
    543                     mFocusedHotseatApp.setAlpha((int) (255 * alpha));
    544                     mFocusedHotseatApp.draw(canvas);
    545                 }
    546             } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
    547                     mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
    548                     mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
    549                 int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
    550                 // Draw the outer circle
    551                 eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
    552                         pageIndicatorBounds.centerY(),
    553                         DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
    554                         mBorderPaint);
    555                 // Draw the inner circle
    556                 mErasePaint.setAlpha(0);
    557                 eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
    558                         pageIndicatorBounds.centerY(),
    559                         DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
    560                         mErasePaint);
    561                 canvas.drawBitmap(eraseBg, 0, 0, null);
    562                 eraseCanvas.setBitmap(null);
    563                 eraseBg = null;
    564             }
    565             canvas.restore();
    566         }
    567 
    568         // Draw the rest of the cling
    569         super.dispatchDraw(canvas);
    570     };
    571 }
    572