| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.graphics.*; |
| import android.graphics.drawable.Drawable; |
| import android.util.AttributeSet; |
| import android.util.DisplayMetrics; |
| import android.view.FocusFinder; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.animation.AccelerateInterpolator; |
| import android.widget.FrameLayout; |
| import android.widget.TextView; |
| |
| public class Cling extends FrameLayout implements Insettable, View.OnClickListener, |
| View.OnLongClickListener, View.OnTouchListener { |
| |
| private static String FIRST_RUN_PORTRAIT = "first_run_portrait"; |
| private static String FIRST_RUN_LANDSCAPE = "first_run_landscape"; |
| |
| private static String WORKSPACE_PORTRAIT = "workspace_portrait"; |
| private static String WORKSPACE_LANDSCAPE = "workspace_landscape"; |
| private static String WORKSPACE_LARGE = "workspace_large"; |
| private static String WORKSPACE_CUSTOM = "workspace_custom"; |
| |
| private static String MIGRATION_PORTRAIT = "migration_portrait"; |
| private static String MIGRATION_LANDSCAPE = "migration_landscape"; |
| |
| private static String MIGRATION_WORKSPACE_PORTRAIT = "migration_workspace_portrait"; |
| private static String MIGRATION_WORKSPACE_LARGE_PORTRAIT = "migration_workspace_large_portrait"; |
| private static String MIGRATION_WORKSPACE_LANDSCAPE = "migration_workspace_landscape"; |
| |
| private static String FOLDER_PORTRAIT = "folder_portrait"; |
| private static String FOLDER_LANDSCAPE = "folder_landscape"; |
| private static String FOLDER_LARGE = "folder_large"; |
| |
| private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 60; |
| private static float FIRST_RUN_MAX_CIRCLE_RADIUS_DPS = 180; |
| private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50; |
| private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60; |
| private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30; |
| private static float MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 42; |
| private static float MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 46; |
| |
| private Launcher mLauncher; |
| private boolean mIsInitialized; |
| private String mDrawIdentifier; |
| private Drawable mBackground; |
| |
| private int[] mTouchDownPt = new int[2]; |
| |
| private Drawable mFocusedHotseatApp; |
| private ComponentName mFocusedHotseatAppComponent; |
| private Rect mFocusedHotseatAppBounds; |
| |
| private Paint mErasePaint; |
| private Paint mBorderPaint; |
| private Paint mBubblePaint; |
| private Paint mDotPaint; |
| |
| private View mScrimView; |
| private int mBackgroundColor; |
| |
| private final Rect mInsets = new Rect(); |
| |
| public Cling(Context context) { |
| this(context, null, 0); |
| } |
| |
| public Cling(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public Cling(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| |
| TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0); |
| mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier); |
| a.recycle(); |
| |
| setClickable(true); |
| |
| } |
| |
| void init(Launcher l, View scrim) { |
| if (!mIsInitialized) { |
| mLauncher = l; |
| mScrimView = scrim; |
| mBackgroundColor = 0xcc000000; |
| setOnLongClickListener(this); |
| setOnClickListener(this); |
| setOnTouchListener(this); |
| |
| mErasePaint = new Paint(); |
| mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); |
| mErasePaint.setColor(0xFFFFFF); |
| mErasePaint.setAlpha(0); |
| mErasePaint.setAntiAlias(true); |
| |
| mBorderPaint = new Paint(); |
| mBorderPaint.setColor(0xFFFFFFFF); |
| mBorderPaint.setAntiAlias(true); |
| |
| int circleColor = getResources().getColor( |
| R.color.first_run_cling_circle_background_color); |
| mBubblePaint = new Paint(); |
| mBubblePaint.setColor(circleColor); |
| mBubblePaint.setAntiAlias(true); |
| |
| mDotPaint = new Paint(); |
| mDotPaint.setColor(0x72BBED); |
| mDotPaint.setAntiAlias(true); |
| |
| mIsInitialized = true; |
| } |
| } |
| |
| void setFocusedHotseatApp(int drawableId, int appRank, ComponentName cn, String title, |
| String description) { |
| // Get the app to draw |
| Resources r = getResources(); |
| int appIconId = drawableId; |
| Hotseat hotseat = mLauncher.getHotseat(); |
| // Skip the focused app in the large layouts |
| if (!mDrawIdentifier.equals(WORKSPACE_LARGE) && |
| hotseat != null && appIconId > -1 && appRank > -1 && !title.isEmpty() && |
| !description.isEmpty()) { |
| // Set the app bounds |
| int x = hotseat.getCellXFromOrder(appRank); |
| int y = hotseat.getCellYFromOrder(appRank); |
| Rect pos = hotseat.getCellCoordinates(x, y); |
| LauncherAppState app = LauncherAppState.getInstance(); |
| DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); |
| mFocusedHotseatApp = getResources().getDrawable(appIconId); |
| mFocusedHotseatAppComponent = cn; |
| mFocusedHotseatAppBounds = new Rect(pos.left, pos.top, |
| pos.left + Utilities.sIconTextureWidth, |
| pos.top + Utilities.sIconTextureHeight); |
| Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds, |
| ((float) grid.hotseatIconSizePx / grid.iconSizePx)); |
| |
| // Set the title |
| TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title); |
| if (v != null) { |
| v.setText(title); |
| } |
| |
| // Set the description |
| v = (TextView) findViewById(R.id.focused_hotseat_app_description); |
| if (v != null) { |
| v.setText(description); |
| } |
| |
| // Show the bubble |
| View bubble = findViewById(R.id.focused_hotseat_app_bubble); |
| bubble.setVisibility(View.VISIBLE); |
| } |
| } |
| |
| void setOpenFolderRect(Rect r) { |
| if (mDrawIdentifier.equals(FOLDER_LANDSCAPE) || |
| mDrawIdentifier.equals(FOLDER_LARGE)) { |
| ViewGroup vg = (ViewGroup) findViewById(R.id.folder_bubble); |
| ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) vg.getLayoutParams(); |
| lp.topMargin = r.top - mInsets.bottom; |
| lp.leftMargin = r.right; |
| vg.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); |
| vg.requestLayout(); |
| } |
| } |
| |
| void updateMigrationWorkspaceBubblePosition() { |
| DisplayMetrics metrics = new DisplayMetrics(); |
| mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics); |
| |
| // Get the page indicator bounds |
| LauncherAppState app = LauncherAppState.getInstance(); |
| DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); |
| Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets); |
| |
| if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT)) { |
| View bubble = findViewById(R.id.migration_workspace_cling_bubble); |
| ViewGroup.MarginLayoutParams lp = |
| (ViewGroup.MarginLayoutParams) bubble.getLayoutParams(); |
| lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top; |
| bubble.requestLayout(); |
| } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT)) { |
| View bubble = findViewById(R.id.content); |
| ViewGroup.MarginLayoutParams lp = |
| (ViewGroup.MarginLayoutParams) bubble.getLayoutParams(); |
| lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top; |
| bubble.requestLayout(); |
| } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) { |
| View bubble = findViewById(R.id.content); |
| ViewGroup.MarginLayoutParams lp = |
| (ViewGroup.MarginLayoutParams) bubble.getLayoutParams(); |
| if (grid.isLayoutRtl) { |
| lp.leftMargin = pageIndicatorBounds.right; |
| } else { |
| lp.rightMargin = (grid.widthPx - pageIndicatorBounds.left); |
| } |
| bubble.requestLayout(); |
| } |
| } |
| |
| void updateWorkspaceBubblePosition() { |
| DisplayMetrics metrics = new DisplayMetrics(); |
| mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics); |
| |
| // Get the cut-out bounds |
| LauncherAppState app = LauncherAppState.getInstance(); |
| DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); |
| Rect cutOutBounds = getWorkspaceCutOutBounds(metrics); |
| |
| if (mDrawIdentifier.equals(WORKSPACE_LARGE)) { |
| View bubble = findViewById(R.id.workspace_cling_bubble); |
| ViewGroup.MarginLayoutParams lp = |
| (ViewGroup.MarginLayoutParams) bubble.getLayoutParams(); |
| lp.bottomMargin = grid.heightPx - cutOutBounds.top - mInsets.bottom; |
| bubble.requestLayout(); |
| } |
| } |
| |
| private Rect getWorkspaceCutOutBounds(DisplayMetrics metrics) { |
| int halfWidth = metrics.widthPixels / 2; |
| int halfHeight = metrics.heightPixels / 2; |
| int yOffset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics); |
| if (mDrawIdentifier.equals(WORKSPACE_LARGE)) { |
| yOffset = 0; |
| } |
| int radius = DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics); |
| return new Rect(halfWidth - radius, halfHeight - yOffset - radius, halfWidth + radius, |
| halfHeight - yOffset + radius); |
| } |
| |
| void show(boolean animate, int duration) { |
| setVisibility(View.VISIBLE); |
| setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || |
| mDrawIdentifier.equals(WORKSPACE_LARGE) || |
| mDrawIdentifier.equals(WORKSPACE_CUSTOM) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) { |
| View content = getContent(); |
| content.setAlpha(0f); |
| content.animate() |
| .alpha(1f) |
| .setDuration(duration) |
| .setListener(null) |
| .start(); |
| setAlpha(1f); |
| } else { |
| if (animate) { |
| buildLayer(); |
| setAlpha(0f); |
| animate() |
| .alpha(1f) |
| .setInterpolator(new AccelerateInterpolator()) |
| .setDuration(duration) |
| .setListener(null) |
| .start(); |
| } else { |
| setAlpha(1f); |
| } |
| } |
| |
| // Show the scrim if necessary |
| if (mScrimView != null) { |
| mScrimView.setVisibility(View.VISIBLE); |
| mScrimView.setAlpha(0f); |
| mScrimView.animate() |
| .alpha(1f) |
| .setDuration(duration) |
| .setListener(null) |
| .start(); |
| } |
| |
| setFocusableInTouchMode(true); |
| post(new Runnable() { |
| public void run() { |
| setFocusable(true); |
| requestFocus(); |
| } |
| }); |
| } |
| |
| void hide(final int duration, final Runnable postCb) { |
| if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) || |
| mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE) || |
| mDrawIdentifier.equals(MIGRATION_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_LANDSCAPE)) { |
| View content = getContent(); |
| content.animate() |
| .alpha(0f) |
| .setDuration(duration) |
| .setListener(new AnimatorListenerAdapter() { |
| public void onAnimationEnd(Animator animation) { |
| // We are about to trigger the workspace cling, so don't do anything else |
| setVisibility(View.GONE); |
| postCb.run(); |
| }; |
| }) |
| .start(); |
| } else { |
| animate() |
| .alpha(0f) |
| .setDuration(duration) |
| .setListener(new AnimatorListenerAdapter() { |
| public void onAnimationEnd(Animator animation) { |
| // We are about to trigger the workspace cling, so don't do anything else |
| setVisibility(View.GONE); |
| postCb.run(); |
| }; |
| }) |
| .start(); |
| } |
| |
| // Show the scrim if necessary |
| if (mScrimView != null) { |
| mScrimView.animate() |
| .alpha(0f) |
| .setDuration(duration) |
| .setListener(new AnimatorListenerAdapter() { |
| public void onAnimationEnd(Animator animation) { |
| mScrimView.setVisibility(View.GONE); |
| }; |
| }) |
| .start(); |
| } |
| } |
| |
| void cleanup() { |
| mBackground = null; |
| mIsInitialized = false; |
| } |
| |
| void bringScrimToFront() { |
| if (mScrimView != null) { |
| mScrimView.bringToFront(); |
| } |
| } |
| |
| @Override |
| public void setInsets(Rect insets) { |
| mInsets.set(insets); |
| setPadding(insets.left, insets.top, insets.right, insets.bottom); |
| } |
| |
| View getContent() { |
| return findViewById(R.id.content); |
| } |
| |
| String getDrawIdentifier() { |
| return mDrawIdentifier; |
| } |
| |
| @Override |
| public View focusSearch(int direction) { |
| return this.focusSearch(this, direction); |
| } |
| |
| @Override |
| public View focusSearch(View focused, int direction) { |
| return FocusFinder.getInstance().findNextFocus(this, focused, direction); |
| } |
| |
| @Override |
| public boolean onHoverEvent(MotionEvent event) { |
| return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) |
| || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) |
| || mDrawIdentifier.equals(WORKSPACE_LARGE) |
| || mDrawIdentifier.equals(WORKSPACE_CUSTOM)); |
| } |
| |
| @Override |
| public boolean onTouchEvent(android.view.MotionEvent event) { |
| if (mDrawIdentifier.equals(FOLDER_PORTRAIT) || |
| mDrawIdentifier.equals(FOLDER_LANDSCAPE) || |
| mDrawIdentifier.equals(FOLDER_LARGE)) { |
| Folder f = mLauncher.getWorkspace().getOpenFolder(); |
| if (f != null) { |
| Rect r = new Rect(); |
| f.getHitRect(r); |
| if (r.contains((int) event.getX(), (int) event.getY())) { |
| return false; |
| } |
| } |
| } |
| return super.onTouchEvent(event); |
| }; |
| |
| @Override |
| public boolean onTouch(View v, MotionEvent ev) { |
| if (ev.getAction() == MotionEvent.ACTION_DOWN) { |
| mTouchDownPt[0] = (int) ev.getX(); |
| mTouchDownPt[1] = (int) ev.getY(); |
| } |
| return false; |
| } |
| |
| @Override |
| public void onClick(View v) { |
| if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || |
| mDrawIdentifier.equals(WORKSPACE_LARGE)) { |
| if (mFocusedHotseatAppBounds != null && |
| mFocusedHotseatAppBounds.contains(mTouchDownPt[0], mTouchDownPt[1])) { |
| // Launch the activity that is being highlighted |
| Intent intent = new Intent(Intent.ACTION_MAIN); |
| intent.setComponent(mFocusedHotseatAppComponent); |
| intent.addCategory(Intent.CATEGORY_LAUNCHER); |
| mLauncher.startActivity(intent, null); |
| mLauncher.getLauncherClings().dismissWorkspaceCling(this); |
| } |
| } |
| } |
| |
| @Override |
| public boolean onLongClick(View v) { |
| if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || |
| mDrawIdentifier.equals(WORKSPACE_LARGE)) { |
| mLauncher.getLauncherClings().dismissWorkspaceCling(null); |
| return true; |
| } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) { |
| mLauncher.getLauncherClings().dismissMigrationWorkspaceCling(null); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected void dispatchDraw(Canvas canvas) { |
| if (mIsInitialized) { |
| canvas.save(); |
| |
| // Get the page indicator bounds |
| LauncherAppState app = LauncherAppState.getInstance(); |
| DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); |
| Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets); |
| |
| // Get the background override if there is one |
| if (mBackground == null) { |
| if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) { |
| mBackground = getResources().getDrawable(R.drawable.bg_cling5); |
| } |
| } |
| // Draw the background |
| Bitmap eraseBg = null; |
| Canvas eraseCanvas = null; |
| if (mScrimView != null) { |
| // Skip drawing the background |
| mScrimView.setBackgroundColor(mBackgroundColor); |
| } else if (mBackground != null) { |
| mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); |
| mBackground.draw(canvas); |
| } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || |
| mDrawIdentifier.equals(WORKSPACE_LARGE) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) { |
| // Initialize the draw buffer (to allow punching through) |
| eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), |
| Bitmap.Config.ARGB_8888); |
| eraseCanvas = new Canvas(eraseBg); |
| eraseCanvas.drawColor(mBackgroundColor); |
| } else { |
| canvas.drawColor(mBackgroundColor); |
| } |
| |
| // Draw everything else |
| DisplayMetrics metrics = new DisplayMetrics(); |
| mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics); |
| float alpha = getAlpha(); |
| View content = getContent(); |
| if (content != null) { |
| alpha *= content.getAlpha(); |
| } |
| if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) || |
| mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) { |
| // Draw the circle |
| View bubbleContent = findViewById(R.id.bubble_content); |
| Rect bubbleRect = new Rect(); |
| bubbleContent.getGlobalVisibleRect(bubbleRect); |
| mBubblePaint.setAlpha((int) (255 * alpha)); |
| float buffer = DynamicGrid.pxFromDp(FIRST_RUN_CIRCLE_BUFFER_DPS, metrics); |
| float maxRadius = DynamicGrid.pxFromDp(FIRST_RUN_MAX_CIRCLE_RADIUS_DPS, metrics); |
| float radius = Math.min(maxRadius, (bubbleContent.getMeasuredWidth() + buffer) / 2); |
| canvas.drawCircle(metrics.widthPixels / 2, |
| bubbleRect.centerY(), radius, |
| mBubblePaint); |
| } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) || |
| mDrawIdentifier.equals(WORKSPACE_LARGE)) { |
| Rect cutOutBounds = getWorkspaceCutOutBounds(metrics); |
| // Draw the outer circle |
| mErasePaint.setAlpha(128); |
| eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(), |
| DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics), |
| mErasePaint); |
| // Draw the inner circle |
| mErasePaint.setAlpha(0); |
| eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(), |
| DynamicGrid.pxFromDp(WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics), |
| mErasePaint); |
| canvas.drawBitmap(eraseBg, 0, 0, null); |
| eraseCanvas.setBitmap(null); |
| eraseBg = null; |
| |
| // Draw the focused hotseat app icon |
| if (mFocusedHotseatAppBounds != null && mFocusedHotseatApp != null) { |
| mFocusedHotseatApp.setBounds(mFocusedHotseatAppBounds.left, |
| mFocusedHotseatAppBounds.top, mFocusedHotseatAppBounds.right, |
| mFocusedHotseatAppBounds.bottom); |
| mFocusedHotseatApp.setAlpha((int) (255 * alpha)); |
| mFocusedHotseatApp.draw(canvas); |
| } |
| } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) || |
| mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) { |
| int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics); |
| // Draw the outer circle |
| eraseCanvas.drawCircle(pageIndicatorBounds.centerX(), |
| pageIndicatorBounds.centerY(), |
| DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics), |
| mBorderPaint); |
| // Draw the inner circle |
| mErasePaint.setAlpha(0); |
| eraseCanvas.drawCircle(pageIndicatorBounds.centerX(), |
| pageIndicatorBounds.centerY(), |
| DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics), |
| mErasePaint); |
| canvas.drawBitmap(eraseBg, 0, 0, null); |
| eraseCanvas.setBitmap(null); |
| eraseBg = null; |
| } |
| canvas.restore(); |
| } |
| |
| // Draw the rest of the cling |
| super.dispatchDraw(canvas); |
| }; |
| } |