Workspace.java revision 9171e6d8a2b7b5aa136617b9779a8bbadc5259f7
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher2; 18 19import java.util.ArrayList; 20import java.util.HashSet; 21import java.util.List; 22 23import android.R.integer; 24import android.animation.Animator; 25import android.animation.Animator.AnimatorListener; 26import android.animation.AnimatorSet; 27import android.animation.ObjectAnimator; 28import android.animation.PropertyValuesHolder; 29import android.animation.TimeInterpolator; 30import android.animation.ValueAnimator; 31import android.animation.ValueAnimator.AnimatorUpdateListener; 32import android.app.AlertDialog; 33import android.app.WallpaperManager; 34import android.appwidget.AppWidgetManager; 35import android.appwidget.AppWidgetProviderInfo; 36import android.content.ClipData; 37import android.content.ClipDescription; 38import android.content.ComponentName; 39import android.content.Context; 40import android.content.Intent; 41import android.content.pm.PackageManager; 42import android.content.pm.ProviderInfo; 43import android.content.res.Resources; 44import android.content.res.TypedArray; 45import android.graphics.Bitmap; 46import android.graphics.Camera; 47import android.graphics.Canvas; 48import android.graphics.Matrix; 49import android.graphics.Paint; 50import android.graphics.Rect; 51import android.graphics.RectF; 52import android.graphics.Region.Op; 53import android.graphics.drawable.Drawable; 54import android.net.Uri; 55import android.os.IBinder; 56import android.os.Parcelable; 57import android.util.AttributeSet; 58import android.util.Log; 59import android.util.Pair; 60import android.view.Display; 61import android.view.DragEvent; 62import android.view.MotionEvent; 63import android.view.View; 64import android.view.animation.DecelerateInterpolator; 65import android.widget.TextView; 66import android.widget.Toast; 67 68import com.android.launcher.R; 69import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData; 70 71/** 72 * The workspace is a wide area with a wallpaper and a finite number of pages. 73 * Each page contains a number of icons, folders or widgets the user can 74 * interact with. A workspace is meant to be used with a fixed width only. 75 */ 76public class Workspace extends SmoothPagedView 77 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 78 @SuppressWarnings({"UnusedDeclaration"}) 79 private static final String TAG = "Launcher.Workspace"; 80 81 // This is how much the workspace shrinks when we enter all apps or 82 // customization mode 83 private static final float SHRINK_FACTOR = 0.16f; 84 85 // Y rotation to apply to the workspace screens 86 private static final float WORKSPACE_ROTATION = 12.5f; 87 private static final float WORKSPACE_TRANSLATION = 50.0f; 88 89 // These are extra scale factors to apply to the mini home screens 90 // so as to achieve the desired transform 91 private static final float EXTRA_SCALE_FACTOR_0 = 0.972f; 92 private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; 93 private static final float EXTRA_SCALE_FACTOR_2 = 1.10f; 94 95 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 300; 96 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 300; 97 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; 98 99 private static final int BACKGROUND_FADE_OUT_DURATION = 450; 100 private static final int BACKGROUND_FADE_IN_DURATION = 350; 101 102 // These animators are used to fade the children's outlines 103 private ObjectAnimator mChildrenOutlineFadeInAnimation; 104 private ObjectAnimator mChildrenOutlineFadeOutAnimation; 105 private float mChildrenOutlineAlpha = 0; 106 107 // These properties refer to the background protection gradient used for AllApps and Customize 108 private ObjectAnimator mBackgroundFadeInAnimation; 109 private ObjectAnimator mBackgroundFadeOutAnimation; 110 private Drawable mBackground; 111 private float mBackgroundAlpha = 0; 112 113 private final WallpaperManager mWallpaperManager; 114 115 private int mDefaultPage; 116 117 private boolean mPageMoving = false; 118 119 /** 120 * CellInfo for the cell that is currently being dragged 121 */ 122 private CellLayout.CellInfo mDragInfo; 123 124 /** 125 * Target drop area calculated during last acceptDrop call. 126 */ 127 private int[] mTargetCell = null; 128 129 /** 130 * The CellLayout that is currently being dragged over 131 */ 132 private CellLayout mDragTargetLayout = null; 133 134 private Launcher mLauncher; 135 private IconCache mIconCache; 136 private DragController mDragController; 137 138 // These are temporary variables to prevent having to allocate a new object just to 139 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 140 private int[] mTempCell = new int[2]; 141 private int[] mTempEstimate = new int[2]; 142 private float[] mTempOriginXY = new float[2]; 143 private float[] mTempDragCoordinates = new float[2]; 144 private float[] mTempTouchCoordinates = new float[2]; 145 private float[] mTempCellLayoutCenterCoordinates = new float[2]; 146 private float[] mTempDragBottomRightCoordinates = new float[2]; 147 private Matrix mTempInverseMatrix = new Matrix(); 148 149 private static final int DEFAULT_CELL_COUNT_X = 4; 150 private static final int DEFAULT_CELL_COUNT_Y = 4; 151 152 private Drawable mPreviousIndicator; 153 private Drawable mNextIndicator; 154 155 // State variable that indicates whether the pages are small (ie when you're 156 // in all apps or customize mode) 157 private boolean mIsSmall = false; 158 private boolean mIsInUnshrinkAnimation = false; 159 private AnimatorListener mUnshrinkAnimationListener; 160 private enum ShrinkPosition { 161 SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE }; 162 private ShrinkPosition mShrunkenState; 163 private boolean mWaitingToShrink = false; 164 private ShrinkPosition mWaitingToShrinkPosition; 165 private AnimatorSet mAnimator; 166 167 /** Is the user is dragging an item near the edge of a page? */ 168 private boolean mInScrollArea = false; 169 170 /** If mInScrollArea is true, the direction of the scroll. */ 171 private int mPendingScrollDirection = DragController.SCROLL_NONE; 172 173 private boolean mInDragMode = false; 174 175 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper(); 176 private Bitmap mDragOutline = null; 177 private final Rect mTempRect = new Rect(); 178 private final int[] mTempXY = new int[2]; 179 180 private ValueAnimator mDropAnim = null; 181 private TimeInterpolator mQuintEaseOutInterpolator = new DecelerateInterpolator(2.5f); 182 private View mDropView = null; 183 private int[] mDropViewPos = new int[] { -1, -1 }; 184 185 // Paint used to draw external drop outline 186 private final Paint mExternalDragOutlinePaint = new Paint(); 187 188 /** Used to trigger an animation as soon as the workspace stops scrolling. */ 189 private Animator mAnimOnPageEndMoving = null; 190 191 // Camera and Matrix used to determine the final position of a neighboring CellLayout 192 private final Matrix mMatrix = new Matrix(); 193 private final Camera mCamera = new Camera(); 194 private final float mTempFloat2[] = new float[2]; 195 196 /** 197 * Used to inflate the Workspace from XML. 198 * 199 * @param context The application's context. 200 * @param attrs The attributes set containing the Workspace's customization values. 201 */ 202 public Workspace(Context context, AttributeSet attrs) { 203 this(context, attrs, 0); 204 } 205 206 /** 207 * Used to inflate the Workspace from XML. 208 * 209 * @param context The application's context. 210 * @param attrs The attributes set containing the Workspace's customization values. 211 * @param defStyle Unused. 212 */ 213 public Workspace(Context context, AttributeSet attrs, int defStyle) { 214 super(context, attrs, defStyle); 215 mContentIsRefreshable = false; 216 217 if (!LauncherApplication.isScreenXLarge()) { 218 mFadeInAdjacentScreens = false; 219 } 220 221 mWallpaperManager = WallpaperManager.getInstance(context); 222 223 TypedArray a = context.obtainStyledAttributes(attrs, 224 R.styleable.Workspace, defStyle, 0); 225 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 226 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 227 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); 228 a.recycle(); 229 230 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 231 setHapticFeedbackEnabled(false); 232 233 initWorkspace(); 234 } 235 236 /** 237 * Initializes various states for this workspace. 238 */ 239 protected void initWorkspace() { 240 Context context = getContext(); 241 mCurrentPage = mDefaultPage; 242 Launcher.setScreen(mCurrentPage); 243 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 244 mIconCache = app.getIconCache(); 245 mExternalDragOutlinePaint.setAntiAlias(true); 246 setWillNotDraw(false); 247 248 final Resources res = getResources(); 249 mBackground = res.getDrawable(R.drawable.all_apps_bg_gradient); 250 251 mUnshrinkAnimationListener = new LauncherAnimatorListenerAdapter() { 252 @Override 253 public void onAnimationStart(Animator animation) { 254 mIsInUnshrinkAnimation = true; 255 } 256 @Override 257 public void onAnimationEndOrCancel(Animator animation) { 258 mIsInUnshrinkAnimation = false; 259 } 260 }; 261 mSnapVelocity = 600; 262 } 263 264 @Override 265 protected int getScrollMode() { 266 if (LauncherApplication.isScreenXLarge()) { 267 return SmoothPagedView.QUINTIC_MODE; 268 } else { 269 return SmoothPagedView.OVERSHOOT_MODE; 270 } 271 } 272 273 @Override 274 public void addView(View child, int index, LayoutParams params) { 275 if (!(child instanceof CellLayout)) { 276 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 277 } 278 ((CellLayout) child).setOnInterceptTouchListener(this); 279 super.addView(child, index, params); 280 } 281 282 @Override 283 public void addView(View child) { 284 if (!(child instanceof CellLayout)) { 285 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 286 } 287 ((CellLayout) child).setOnInterceptTouchListener(this); 288 super.addView(child); 289 } 290 291 @Override 292 public void addView(View child, int index) { 293 if (!(child instanceof CellLayout)) { 294 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 295 } 296 ((CellLayout) child).setOnInterceptTouchListener(this); 297 super.addView(child, index); 298 } 299 300 @Override 301 public void addView(View child, int width, int height) { 302 if (!(child instanceof CellLayout)) { 303 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 304 } 305 ((CellLayout) child).setOnInterceptTouchListener(this); 306 super.addView(child, width, height); 307 } 308 309 @Override 310 public void addView(View child, LayoutParams params) { 311 if (!(child instanceof CellLayout)) { 312 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 313 } 314 ((CellLayout) child).setOnInterceptTouchListener(this); 315 super.addView(child, params); 316 } 317 318 /** 319 * @return The open folder on the current screen, or null if there is none 320 */ 321 Folder getOpenFolder() { 322 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 323 int count = currentPage.getChildCount(); 324 for (int i = 0; i < count; i++) { 325 View child = currentPage.getChildAt(i); 326 if (child instanceof Folder) { 327 Folder folder = (Folder) child; 328 if (folder.getInfo().opened) 329 return folder; 330 } 331 } 332 return null; 333 } 334 335 ArrayList<Folder> getOpenFolders() { 336 final int screenCount = getChildCount(); 337 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 338 339 for (int screen = 0; screen < screenCount; screen++) { 340 CellLayout currentPage = (CellLayout) getChildAt(screen); 341 int count = currentPage.getChildCount(); 342 for (int i = 0; i < count; i++) { 343 View child = currentPage.getChildAt(i); 344 if (child instanceof Folder) { 345 Folder folder = (Folder) child; 346 if (folder.getInfo().opened) 347 folders.add(folder); 348 break; 349 } 350 } 351 } 352 return folders; 353 } 354 355 boolean isDefaultPageShowing() { 356 return mCurrentPage == mDefaultPage; 357 } 358 359 /** 360 * Sets the current screen. 361 * 362 * @param currentPage 363 */ 364 @Override 365 void setCurrentPage(int currentPage) { 366 super.setCurrentPage(currentPage); 367 updateWallpaperOffset(mScrollX); 368 } 369 370 /** 371 * Adds the specified child in the specified screen. The position and dimension of 372 * the child are defined by x, y, spanX and spanY. 373 * 374 * @param child The child to add in one of the workspace's screens. 375 * @param screen The screen in which to add the child. 376 * @param x The X position of the child in the screen's grid. 377 * @param y The Y position of the child in the screen's grid. 378 * @param spanX The number of cells spanned horizontally by the child. 379 * @param spanY The number of cells spanned vertically by the child. 380 */ 381 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 382 addInScreen(child, screen, x, y, spanX, spanY, false); 383 } 384 385 void addInFullScreen(View child, int screen) { 386 addInScreen(child, screen, 0, 0, -1, -1); 387 } 388 389 /** 390 * Adds the specified child in the specified screen. The position and dimension of 391 * the child are defined by x, y, spanX and spanY. 392 * 393 * @param child The child to add in one of the workspace's screens. 394 * @param screen The screen in which to add the child. 395 * @param x The X position of the child in the screen's grid. 396 * @param y The Y position of the child in the screen's grid. 397 * @param spanX The number of cells spanned horizontally by the child. 398 * @param spanY The number of cells spanned vertically by the child. 399 * @param insert When true, the child is inserted at the beginning of the children list. 400 */ 401 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 402 if (screen < 0 || screen >= getChildCount()) { 403 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 404 + " (was " + screen + "); skipping child"); 405 return; 406 } 407 408 final CellLayout group = (CellLayout) getChildAt(screen); 409 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 410 if (lp == null) { 411 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 412 } else { 413 lp.cellX = x; 414 lp.cellY = y; 415 lp.cellHSpan = spanX; 416 lp.cellVSpan = spanY; 417 } 418 419 // Get the canonical child id to uniquely represent this view in this screen 420 int childId = LauncherModel.getCellLayoutChildId(-1, screen, x, y, spanX, spanY); 421 boolean markCellsAsOccupied = !(child instanceof Folder); 422 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) { 423 // TODO: This branch occurs when the workspace is adding views 424 // outside of the defined grid 425 // maybe we should be deleting these items from the LauncherModel? 426 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 427 } 428 429 if (!(child instanceof Folder)) { 430 child.setHapticFeedbackEnabled(false); 431 child.setOnLongClickListener(mLongClickListener); 432 } 433 if (child instanceof DropTarget) { 434 mDragController.addDropTarget((DropTarget) child); 435 } 436 } 437 438 public boolean onTouch(View v, MotionEvent event) { 439 // this is an intercepted event being forwarded from a cell layout 440 if (mIsSmall || mIsInUnshrinkAnimation) { 441 // Only allow clicks on a CellLayout if it is visible 442 if (mShrunkenState != ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 443 mLauncher.onWorkspaceClick((CellLayout) v); 444 } 445 return true; 446 } else if (!mPageMoving) { 447 if (v == getChildAt(mCurrentPage - 1)) { 448 snapToPage(mCurrentPage - 1); 449 return true; 450 } else if (v == getChildAt(mCurrentPage + 1)) { 451 snapToPage(mCurrentPage + 1); 452 return true; 453 } 454 } 455 return false; 456 } 457 458 protected void onWindowVisibilityChanged (int visibility) { 459 mLauncher.onWindowVisibilityChanged(visibility); 460 } 461 462 @Override 463 public boolean dispatchUnhandledMove(View focused, int direction) { 464 if (mIsSmall || mIsInUnshrinkAnimation) { 465 // when the home screens are shrunken, shouldn't allow side-scrolling 466 return false; 467 } 468 return super.dispatchUnhandledMove(focused, direction); 469 } 470 471 @Override 472 public boolean onInterceptTouchEvent(MotionEvent ev) { 473 if (mIsSmall || mIsInUnshrinkAnimation) { 474 if (mLauncher.isAllAppsVisible() && 475 mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 476 // Intercept this event so we can show the workspace in full view 477 // when it is clicked on and it is small 478 return true; 479 } 480 return false; 481 } 482 return super.onInterceptTouchEvent(ev); 483 } 484 485 @Override 486 protected void determineScrollingStart(MotionEvent ev) { 487 if (!mIsSmall && !mIsInUnshrinkAnimation) super.determineScrollingStart(ev); 488 } 489 490 protected void onPageBeginMoving() { 491 if (mNextPage != INVALID_PAGE) { 492 // we're snapping to a particular screen 493 enableChildrenCache(mCurrentPage, mNextPage); 494 } else { 495 // this is when user is actively dragging a particular screen, they might 496 // swipe it either left or right (but we won't advance by more than one screen) 497 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); 498 } 499 showOutlines(); 500 mPageMoving = true; 501 } 502 503 protected void onPageEndMoving() { 504 clearChildrenCache(); 505 // Hide the outlines, as long as we're not dragging 506 if (!mDragController.dragging()) { 507 hideOutlines(); 508 } 509 // Check for an animation that's waiting to be started 510 if (mAnimOnPageEndMoving != null) { 511 mAnimOnPageEndMoving.start(); 512 mAnimOnPageEndMoving = null; 513 } 514 515 mPageMoving = false; 516 } 517 518 @Override 519 protected void notifyPageSwitchListener() { 520 super.notifyPageSwitchListener(); 521 522 if (mPreviousIndicator != null) { 523 // if we know the next page, we show the indication for it right away; it looks 524 // weird if the indicators are lagging 525 int page = mNextPage; 526 if (page == INVALID_PAGE) { 527 page = mCurrentPage; 528 } 529 mPreviousIndicator.setLevel(page); 530 mNextIndicator.setLevel(page); 531 } 532 Launcher.setScreen(mCurrentPage); 533 }; 534 535 private void updateWallpaperOffset() { 536 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 537 } 538 539 private void updateWallpaperOffset(int scrollRange) { 540 final boolean isStaticWallpaper = (mWallpaperManager != null) && 541 (mWallpaperManager.getWallpaperInfo() == null); 542 if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { 543 IBinder token = getWindowToken(); 544 if (token != null) { 545 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 546 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 547 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 548 } 549 } 550 } 551 552 public void showOutlines() { 553 if (!mIsSmall && !mIsInUnshrinkAnimation) { 554 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 555 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 556 mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f); 557 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION); 558 mChildrenOutlineFadeInAnimation.start(); 559 } 560 } 561 562 public void hideOutlines() { 563 if (!mIsSmall && !mIsInUnshrinkAnimation) { 564 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 565 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 566 mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f); 567 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION); 568 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY); 569 mChildrenOutlineFadeOutAnimation.start(); 570 } 571 } 572 573 public void setChildrenOutlineAlpha(float alpha) { 574 mChildrenOutlineAlpha = alpha; 575 for (int i = 0; i < getChildCount(); i++) { 576 CellLayout cl = (CellLayout) getChildAt(i); 577 cl.setBackgroundAlpha(alpha); 578 } 579 } 580 581 public float getChildrenOutlineAlpha() { 582 return mChildrenOutlineAlpha; 583 } 584 585 public void showBackgroundGradient() { 586 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 587 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 588 mBackgroundFadeInAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 1.0f); 589 mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION); 590 mBackgroundFadeInAnimation.start(); 591 } 592 593 public void hideBackgroundGradient() { 594 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 595 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 596 mBackgroundFadeOutAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 0.0f); 597 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); 598 mBackgroundFadeOutAnimation.start(); 599 } 600 601 public void setBackgroundAlpha(float alpha) { 602 mBackgroundAlpha = alpha; 603 invalidate(); 604 } 605 606 public float getBackgroundAlpha() { 607 return mBackgroundAlpha; 608 } 609 610 /** 611 * Due to 3D transformations, if two CellLayouts are theoretically touching each other, 612 * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived 613 * as being larger. This method computes what offset the rotated view should be translated 614 * in order to minimize this perceived gap. 615 * @param degrees Angle of the view 616 * @param width Width of the view 617 * @param height Height of the view 618 * @return Offset to be used in a View.setTranslationX() call 619 */ 620 private float getOffsetXForRotation(float degrees, int width, int height) { 621 mMatrix.reset(); 622 mCamera.save(); 623 mCamera.rotateY(Math.abs(degrees)); 624 mCamera.getMatrix(mMatrix); 625 mCamera.restore(); 626 627 mMatrix.preTranslate(-width * 0.5f, -height * 0.5f); 628 mMatrix.postTranslate(width * 0.5f, height * 0.5f); 629 mTempFloat2[0] = width; 630 mTempFloat2[1] = height; 631 mMatrix.mapPoints(mTempFloat2); 632 return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f); 633 } 634 635 @Override 636 protected void screenScrolled(int screenCenter) { 637 final int halfScreenSize = getMeasuredWidth() / 2; 638 for (int i = 0; i < getChildCount(); i++) { 639 CellLayout cl = (CellLayout) getChildAt(i); 640 if (cl != null) { 641 int totalDistance = cl.getMeasuredWidth() + mPageSpacing; 642 int delta = screenCenter - (getChildOffset(i) - 643 getRelativeChildOffset(i) + halfScreenSize); 644 645 float scrollProgress = delta / (totalDistance * 1.0f); 646 scrollProgress = Math.min(scrollProgress, 1.0f); 647 scrollProgress = Math.max(scrollProgress, -1.0f); 648 649 cl.setBackgroundAlphaMultiplier(Math.abs(scrollProgress)); 650 651 float rotation = WORKSPACE_ROTATION * scrollProgress; 652 float translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight()); 653 cl.setTranslationX(translationX); 654 cl.setRotationY(rotation); 655 } 656 } 657 } 658 659 protected void onAttachedToWindow() { 660 super.onAttachedToWindow(); 661 computeScroll(); 662 mDragController.setWindowToken(getWindowToken()); 663 } 664 665 @Override 666 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 667 super.onLayout(changed, left, top, right, bottom); 668 669 // if shrinkToBottom() is called on initialization, it has to be deferred 670 // until after the first call to onLayout so that it has the correct width 671 if (mWaitingToShrink) { 672 shrink(mWaitingToShrinkPosition, false); 673 mWaitingToShrink = false; 674 } 675 676 if (LauncherApplication.isInPlaceRotationEnabled()) { 677 // When the device is rotated, the scroll position of the current screen 678 // needs to be refreshed 679 setCurrentPage(getCurrentPage()); 680 } 681 } 682 683 @Override 684 protected void onDraw(Canvas canvas) { 685 // Draw the background gradient if necessary 686 if (mBackgroundAlpha > 0.0f) { 687 mBackground.setAlpha((int) (mBackgroundAlpha * 255)); 688 mBackground.setBounds(mScrollX, 0, mScrollX + getMeasuredWidth(), getMeasuredHeight()); 689 mBackground.draw(canvas); 690 } 691 692 super.onDraw(canvas); 693 } 694 695 @Override 696 protected void dispatchDraw(Canvas canvas) { 697 if (mIsSmall || mIsInUnshrinkAnimation) { 698 // Draw all the workspaces if we're small 699 final int pageCount = getChildCount(); 700 final long drawingTime = getDrawingTime(); 701 for (int i = 0; i < pageCount; i++) { 702 final View page = (View) getChildAt(i); 703 704 drawChild(canvas, page, drawingTime); 705 } 706 } else { 707 super.dispatchDraw(canvas); 708 709 final int width = getWidth(); 710 final int height = getHeight(); 711 712 // In portrait orientation, draw the glowing edge when dragging to adjacent screens 713 if (mInScrollArea && (height > width)) { 714 final int pageHeight = getChildAt(0).getHeight(); 715 716 // This determines the height of the glowing edge: 90% of the page height 717 final int padding = (int) ((height - pageHeight) * 0.5f + pageHeight * 0.1f); 718 719 final CellLayout leftPage = (CellLayout) getChildAt(mCurrentPage - 1); 720 final CellLayout rightPage = (CellLayout) getChildAt(mCurrentPage + 1); 721 722 if (leftPage != null && leftPage.getHover()) { 723 final Drawable d = getResources().getDrawable(R.drawable.page_hover_left); 724 d.setBounds(mScrollX, padding, mScrollX + d.getIntrinsicWidth(), height - padding); 725 d.draw(canvas); 726 } else if (rightPage != null && rightPage.getHover()) { 727 final Drawable d = getResources().getDrawable(R.drawable.page_hover_right); 728 d.setBounds(mScrollX + width - d.getIntrinsicWidth(), padding, mScrollX + width, height - padding); 729 d.draw(canvas); 730 } 731 } 732 733 if (mDropView != null) { 734 // We are animating an item that was just dropped on the home screen. 735 // Render its View in the current animation position. 736 canvas.save(Canvas.MATRIX_SAVE_FLAG); 737 final int xPos = mDropViewPos[0] - mDropView.getScrollX(); 738 final int yPos = mDropViewPos[1] - mDropView.getScrollY(); 739 canvas.translate(xPos, yPos); 740 mDropView.draw(canvas); 741 canvas.restore(); 742 } 743 } 744 } 745 746 @Override 747 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 748 if (!mLauncher.isAllAppsVisible()) { 749 final Folder openFolder = getOpenFolder(); 750 if (openFolder != null) { 751 return openFolder.requestFocus(direction, previouslyFocusedRect); 752 } else { 753 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 754 } 755 } 756 return false; 757 } 758 759 @Override 760 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 761 if (!mLauncher.isAllAppsVisible()) { 762 final Folder openFolder = getOpenFolder(); 763 if (openFolder != null) { 764 openFolder.addFocusables(views, direction); 765 } else { 766 super.addFocusables(views, direction, focusableMode); 767 } 768 } 769 } 770 771 @Override 772 public boolean dispatchTouchEvent(MotionEvent ev) { 773 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 774 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 775 // ie when you click on a mini-screen, it zooms back to that screen) 776 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 777 return false; 778 } 779 } 780 781 return super.dispatchTouchEvent(ev); 782 } 783 784 void enableChildrenCache(int fromPage, int toPage) { 785 if (fromPage > toPage) { 786 final int temp = fromPage; 787 fromPage = toPage; 788 toPage = temp; 789 } 790 791 final int screenCount = getChildCount(); 792 793 fromPage = Math.max(fromPage, 0); 794 toPage = Math.min(toPage, screenCount - 1); 795 796 for (int i = fromPage; i <= toPage; i++) { 797 final CellLayout layout = (CellLayout) getChildAt(i); 798 layout.setChildrenDrawnWithCacheEnabled(true); 799 layout.setChildrenDrawingCacheEnabled(true); 800 } 801 } 802 803 void clearChildrenCache() { 804 final int screenCount = getChildCount(); 805 for (int i = 0; i < screenCount; i++) { 806 final CellLayout layout = (CellLayout) getChildAt(i); 807 layout.setChildrenDrawnWithCacheEnabled(false); 808 } 809 } 810 811 @Override 812 public boolean onTouchEvent(MotionEvent ev) { 813 if (mLauncher.isAllAppsVisible()) { 814 // Cancel any scrolling that is in progress. 815 if (!mScroller.isFinished()) { 816 mScroller.abortAnimation(); 817 } 818 setCurrentPage(mCurrentPage); 819 820 if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 821 mLauncher.showWorkspace(true); 822 // Let the events fall through to the CellLayouts because if they are not 823 // hit, then we get a crash due to a missing ACTION_DOWN touch event 824 } 825 826 return false; // We don't want the events 827 } 828 829 return super.onTouchEvent(ev); 830 } 831 832 public boolean isSmall() { 833 return mIsSmall; 834 } 835 836 void shrinkToTop(boolean animated) { 837 shrink(ShrinkPosition.SHRINK_TO_TOP, animated); 838 } 839 840 void shrinkToMiddle() { 841 shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); 842 } 843 844 void shrinkToBottomHidden() { 845 shrinkToBottomHidden(true); 846 } 847 848 void shrinkToBottomVisible() { 849 shrinkToBottomVisible(true); 850 } 851 852 void shrinkToBottomHidden(boolean animated) { 853 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated); 854 } 855 856 void shrinkToBottomVisible(boolean animated) { 857 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, animated); 858 } 859 860 private float getYScaleForScreen(int screen) { 861 int x = Math.abs(screen - 2); 862 863 // TODO: This should be generalized for use with arbitrary rotation angles. 864 switch(x) { 865 case 0: return EXTRA_SCALE_FACTOR_0; 866 case 1: return EXTRA_SCALE_FACTOR_1; 867 case 2: return EXTRA_SCALE_FACTOR_2; 868 } 869 return 1.0f; 870 } 871 872 // we use this to shrink the workspace for the all apps view and the customize view 873 private void shrink(ShrinkPosition shrinkPosition, boolean animated) { 874 showBackgroundGradient(); 875 876 if (mFirstLayout) { 877 // (mFirstLayout == "first layout has not happened yet") 878 // if we get a call to shrink() as part of our initialization (for example, if 879 // Launcher is started in All Apps mode) then we need to wait for a layout call 880 // to get our width so we can layout the mini-screen views correctly 881 mWaitingToShrink = true; 882 mWaitingToShrinkPosition = shrinkPosition; 883 return; 884 } 885 mIsSmall = true; 886 mShrunkenState = shrinkPosition; 887 888 // Stop any scrolling, move to the current page right away 889 setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage); 890 updateWhichPagesAcceptDrops(mShrunkenState); 891 892 // we intercept and reject all touch events when we're small, so be sure to reset the state 893 mTouchState = TOUCH_STATE_REST; 894 mActivePointerId = INVALID_POINTER; 895 896 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 897 if (currentPage.getBackgroundAlphaMultiplier() < 1.0f) { 898 currentPage.setBackgroundAlpha(0.0f); 899 } 900 currentPage.setBackgroundAlphaMultiplier(1.0f); 901 902 final Resources res = getResources(); 903 final int screenWidth = getWidth(); 904 final int screenHeight = getHeight(); 905 906 // Making the assumption that all pages have the same width as the 0th 907 final int pageWidth = getChildAt(0).getMeasuredWidth(); 908 final int pageHeight = getChildAt(0).getMeasuredHeight(); 909 910 final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); 911 final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); 912 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); 913 914 final int screenCount = getChildCount(); 915 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; 916 917 boolean isPortrait = getMeasuredHeight() > getMeasuredWidth(); 918 float newY = (isPortrait ? 919 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginPortrait) : 920 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginLandscape)); 921 float finalAlpha = 1.0f; 922 float extraShrinkFactor = 1.0f; 923 if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) { 924 newY = screenHeight - newY - scaledPageHeight; 925 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 926 927 // We shrink and disappear to nothing in the case of all apps 928 // (which is when we shrink to the bottom) 929 newY = screenHeight - newY - scaledPageHeight; 930 finalAlpha = 0.0f; 931 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { 932 newY = screenHeight / 2 - scaledPageHeight / 2; 933 finalAlpha = 1.0f; 934 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) { 935 newY = (isPortrait ? 936 getResources().getDimension(R.dimen.customizeSmallScreenVerticalMarginPortrait) : 937 getResources().getDimension(R.dimen.customizeSmallScreenVerticalMarginLandscape)); 938 } 939 940 // We animate all the screens to the centered position in workspace 941 // At the same time, the screens become greyed/dimmed 942 943 // newX is initialized to the left-most position of the centered screens 944 float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; 945 946 // We are going to scale about the center of the view, so we need to adjust the positions 947 // of the views accordingly 948 newX -= (pageWidth - scaledPageWidth) / 2.0f; 949 newY -= (pageHeight - scaledPageHeight) / 2.0f; 950 951 if (mAnimator != null) { 952 mAnimator.cancel(); 953 } 954 mAnimator = new AnimatorSet(); 955 for (int i = 0; i < screenCount; i++) { 956 CellLayout cl = (CellLayout) getChildAt(i); 957 958 float rotation = (-i + 2) * WORKSPACE_ROTATION; 959 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); 960 float rotationScaleY = getYScaleForScreen(i); 961 962 if (animated) { 963 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 964 965 ObjectAnimator animWithInterpolator = ObjectAnimator.ofPropertyValuesHolder(cl, 966 PropertyValuesHolder.ofFloat("x", newX), 967 PropertyValuesHolder.ofFloat("y", newY), 968 PropertyValuesHolder.ofFloat("scaleX", 969 SHRINK_FACTOR * rotationScaleX * extraShrinkFactor), 970 PropertyValuesHolder.ofFloat("scaleY", 971 SHRINK_FACTOR * rotationScaleY * extraShrinkFactor), 972 PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha), 973 PropertyValuesHolder.ofFloat("alpha", finalAlpha), 974 PropertyValuesHolder.ofFloat("rotationY", rotation)); 975 976 animWithInterpolator.setDuration(duration); 977 animWithInterpolator.setInterpolator(mZoomOutInterpolator); 978 mAnimator.playTogether(animWithInterpolator); 979 } else { 980 cl.setX((int)newX); 981 cl.setY((int)newY); 982 cl.setScaleX(SHRINK_FACTOR * rotationScaleX * extraShrinkFactor); 983 cl.setScaleY(SHRINK_FACTOR * rotationScaleY * extraShrinkFactor); 984 cl.setBackgroundAlpha(finalAlpha); 985 cl.setAlpha(finalAlpha); 986 cl.setRotationY(rotation); 987 } 988 // increment newX for the next screen 989 newX += scaledPageWidth + extraScaledSpacing; 990 } 991 if (animated) { 992 mAnimator.start(); 993 } 994 setChildrenDrawnWithCacheEnabled(true); 995 } 996 997 /* 998 * This interpolator emulates the rate at which the perceived scale of an object changes 999 * as its distance from a camera increases. When this interpolator is applied to a scale 1000 * animation on a view, it evokes the sense that the object is shrinking due to moving away 1001 * from the camera. 1002 */ 1003 static class ZInterpolator implements TimeInterpolator { 1004 private float focalLength; 1005 1006 public ZInterpolator(float foc) { 1007 focalLength = foc; 1008 } 1009 1010 public float getInterpolation(float input) { 1011 return (1.0f - focalLength / (focalLength + input)) / 1012 (1.0f - focalLength / (focalLength + 1.0f)); 1013 } 1014 } 1015 1016 /* 1017 * The exact reverse of ZInterpolator. 1018 */ 1019 static class InverseZInterpolator implements TimeInterpolator { 1020 private ZInterpolator zInterpolator; 1021 public InverseZInterpolator(float foc) { 1022 zInterpolator = new ZInterpolator(foc); 1023 } 1024 public float getInterpolation(float input) { 1025 return 1 - zInterpolator.getInterpolation(1 - input); 1026 } 1027 } 1028 1029 /* 1030 * ZInterpolator compounded with an ease-out. 1031 */ 1032 static class ZoomOutInterpolator implements TimeInterpolator { 1033 private final ZInterpolator zInterpolator = new ZInterpolator(0.2f); 1034 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(1.5f); 1035 1036 public float getInterpolation(float input) { 1037 return decelerate.getInterpolation(zInterpolator.getInterpolation(input)); 1038 } 1039 } 1040 1041 /* 1042 * InvereZInterpolator compounded with an ease-out. 1043 */ 1044 static class ZoomInInterpolator implements TimeInterpolator { 1045 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); 1046 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); 1047 1048 public float getInterpolation(float input) { 1049 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); 1050 } 1051 } 1052 1053 private final ZoomOutInterpolator mZoomOutInterpolator = new ZoomOutInterpolator(); 1054 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); 1055 1056 private void updateWhichPagesAcceptDrops(ShrinkPosition state) { 1057 updateWhichPagesAcceptDropsHelper(state, false, 1, 1); 1058 } 1059 1060 private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) { 1061 updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY); 1062 } 1063 1064 private void updateWhichPagesAcceptDropsHelper( 1065 ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) { 1066 final int screenCount = getChildCount(); 1067 for (int i = 0; i < screenCount; i++) { 1068 CellLayout cl = (CellLayout) getChildAt(i); 1069 1070 switch (state) { 1071 case SHRINK_TO_TOP: 1072 if (!isDragHappening) { 1073 boolean showDropHighlight = i == mCurrentPage; 1074 cl.setAcceptsDrops(showDropHighlight); 1075 break; 1076 } 1077 // otherwise, fall through below and mark non-full screens as accepting drops 1078 case SHRINK_TO_BOTTOM_HIDDEN: 1079 case SHRINK_TO_BOTTOM_VISIBLE: 1080 if (!isDragHappening) { 1081 // even if a drag isn't happening, we don't want to show a screen as 1082 // accepting drops if it doesn't have at least one free cell 1083 spanX = 1; 1084 spanY = 1; 1085 } 1086 // the page accepts drops if we can find at least one empty spot 1087 cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY)); 1088 break; 1089 default: 1090 throw new RuntimeException( 1091 "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition"); 1092 } 1093 } 1094 } 1095 1096 /* 1097 * 1098 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we 1099 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace 1100 * 1101 * These methods mark the appropriate pages as accepting drops (which alters their visual 1102 * appearance). 1103 * 1104 */ 1105 public void onDragStartedWithItemSpans(int spanX, int spanY) { 1106 updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY); 1107 } 1108 1109 public void onDragStartedWithItemMinSize(int minWidth, int minHeight) { 1110 int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null); 1111 onDragStartedWithItemSpans(spanXY[0], spanXY[1]); 1112 } 1113 1114 // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was 1115 // never dragged over 1116 public void onDragStopped() { 1117 updateWhichPagesAcceptDrops(mShrunkenState); 1118 } 1119 1120 // We call this when we trigger an unshrink by clicking on the CellLayout cl 1121 public void unshrink(CellLayout clThatWasClicked) { 1122 int newCurrentPage = mCurrentPage; 1123 final int screenCount = getChildCount(); 1124 for (int i = 0; i < screenCount; i++) { 1125 if (getChildAt(i) == clThatWasClicked) { 1126 newCurrentPage = i; 1127 } 1128 } 1129 unshrink(newCurrentPage); 1130 } 1131 1132 @Override 1133 protected boolean handlePagingClicks() { 1134 return true; 1135 } 1136 1137 private void unshrink(int newCurrentPage) { 1138 if (mIsSmall) { 1139 int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 1140 int delta = newX - mScrollX; 1141 1142 final int screenCount = getChildCount(); 1143 for (int i = 0; i < screenCount; i++) { 1144 CellLayout cl = (CellLayout) getChildAt(i); 1145 cl.setX(cl.getX() + delta); 1146 } 1147 setCurrentPage(newCurrentPage); 1148 unshrink(); 1149 } 1150 } 1151 1152 void unshrink() { 1153 unshrink(true); 1154 } 1155 1156 void unshrink(boolean animated) { 1157 hideBackgroundGradient(); 1158 1159 if (mIsSmall) { 1160 mIsSmall = false; 1161 if (mAnimator != null) { 1162 mAnimator.cancel(); 1163 } 1164 1165 mAnimator = new AnimatorSet(); 1166 final int screenCount = getChildCount(); 1167 1168 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 1169 for (int i = 0; i < screenCount; i++) { 1170 final CellLayout cl = (CellLayout)getChildAt(i); 1171 float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; 1172 float rotation = 0.0f; 1173 1174 if (i < mCurrentPage) { 1175 rotation = WORKSPACE_ROTATION; 1176 } else if (i > mCurrentPage) { 1177 rotation = -WORKSPACE_ROTATION; 1178 } 1179 1180 float translation = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight()); 1181 1182 if (animated) { 1183 ObjectAnimator animWithInterpolator = ObjectAnimator.ofPropertyValuesHolder(cl, 1184 PropertyValuesHolder.ofFloat("translationX", translation), 1185 PropertyValuesHolder.ofFloat("translationY", 0.0f), 1186 PropertyValuesHolder.ofFloat("scaleX", 1.0f), 1187 PropertyValuesHolder.ofFloat("scaleY", 1.0f), 1188 PropertyValuesHolder.ofFloat("backgroundAlpha", 0.0f), 1189 PropertyValuesHolder.ofFloat("alpha", finalAlphaValue), 1190 PropertyValuesHolder.ofFloat("rotationY", rotation)); 1191 animWithInterpolator.setDuration(duration); 1192 animWithInterpolator.setInterpolator(mZoomInInterpolator); 1193 mAnimator.playTogether(animWithInterpolator); 1194 } else { 1195 cl.setTranslationX(translation); 1196 cl.setTranslationY(0.0f); 1197 cl.setScaleX(1.0f); 1198 cl.setScaleY(1.0f); 1199 cl.setBackgroundAlpha(0.0f); 1200 cl.setAlpha(finalAlphaValue); 1201 cl.setRotationY(rotation); 1202 } 1203 } 1204 1205 if (animated) { 1206 // If we call this when we're not animated, onAnimationEnd is never called on 1207 // the listener; make sure we only use the listener when we're actually animating 1208 mAnimator.addListener(mUnshrinkAnimationListener); 1209 mAnimator.start(); 1210 } 1211 } 1212 } 1213 1214 /** 1215 * Draw the View v into the given Canvas. 1216 * 1217 * @param v the view to draw 1218 * @param destCanvas the canvas to draw on 1219 * @param padding the horizontal and vertical padding to use when drawing 1220 */ 1221 private void drawDragView(View v, Canvas destCanvas, int padding) { 1222 final Rect clipRect = mTempRect; 1223 v.getDrawingRect(clipRect); 1224 1225 // For a TextView, adjust the clip rect so that we don't include the text label 1226 if (v instanceof TextView) { 1227 final TextView tv = (TextView) v; 1228 clipRect.bottom = clipRect.top + tv.getCompoundPaddingTop() - 1; 1229 } 1230 1231 // Draw the View into the bitmap. 1232 // The translate of scrollX and scrollY is necessary when drawing TextViews, because 1233 // they set scrollX and scrollY to large values to achieve centered text 1234 1235 destCanvas.save(); 1236 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2); 1237 destCanvas.clipRect(clipRect, Op.REPLACE); 1238 v.draw(destCanvas); 1239 destCanvas.restore(); 1240 } 1241 1242 /** 1243 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 1244 * Responsibility for the bitmap is transferred to the caller. 1245 */ 1246 private Bitmap createDragOutline(View v, Canvas canvas, int padding) { 1247 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 1248 final Bitmap b = Bitmap.createBitmap( 1249 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); 1250 1251 canvas.setBitmap(b); 1252 drawDragView(v, canvas, padding); 1253 mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); 1254 1255 return b; 1256 } 1257 1258 /** 1259 * Creates a drag outline to represent a drop (that we don't have the actual information for 1260 * yet). May be changed in the future to alter the drop outline slightly depending on the 1261 * clip description mime data. 1262 */ 1263 private Bitmap createExternalDragOutline(Canvas canvas, int padding) { 1264 Resources r = getResources(); 1265 final int outlineColor = r.getColor(R.color.drag_outline_color); 1266 final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width); 1267 final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height); 1268 final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius); 1269 final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f); 1270 final Bitmap b = Bitmap.createBitmap( 1271 iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888); 1272 1273 canvas.setBitmap(b); 1274 canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset), 1275 rectRadius, rectRadius, mExternalDragOutlinePaint); 1276 mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); 1277 1278 return b; 1279 } 1280 1281 /** 1282 * Returns a new bitmap to show when the given View is being dragged around. 1283 * Responsibility for the bitmap is transferred to the caller. 1284 */ 1285 private Bitmap createDragBitmap(View v, Canvas canvas, int padding) { 1286 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 1287 final Bitmap b = Bitmap.createBitmap( 1288 mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888); 1289 1290 canvas.setBitmap(b); 1291 canvas.drawBitmap(mDragOutline, 0, 0, null); 1292 drawDragView(v, canvas, padding); 1293 mOutlineHelper.applyOuterBlur(b, canvas, outlineColor); 1294 1295 return b; 1296 } 1297 1298 void startDrag(CellLayout.CellInfo cellInfo) { 1299 View child = cellInfo.cell; 1300 1301 // Make sure the drag was started by a long press as opposed to a long click. 1302 if (!child.isInTouchMode()) { 1303 return; 1304 } 1305 1306 mDragInfo = cellInfo; 1307 mDragInfo.screen = mCurrentPage; 1308 1309 CellLayout current = getCurrentDropLayout(); 1310 1311 current.onDragChild(child); 1312 child.setVisibility(View.GONE); 1313 1314 child.clearFocus(); 1315 child.setPressed(false); 1316 1317 final Canvas canvas = new Canvas(); 1318 1319 // We need to add extra padding to the bitmap to make room for the glow effect 1320 final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; 1321 1322 // The outline is used to visualize where the item will land if dropped 1323 mDragOutline = createDragOutline(child, canvas, bitmapPadding); 1324 1325 // The drag bitmap follows the touch point around on the screen 1326 final Bitmap b = createDragBitmap(child, canvas, bitmapPadding); 1327 1328 final int bmpWidth = b.getWidth(); 1329 final int bmpHeight = b.getHeight(); 1330 child.getLocationOnScreen(mTempXY); 1331 final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2; 1332 final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2; 1333 mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this, 1334 child.getTag(), DragController.DRAG_ACTION_MOVE, null); 1335 b.recycle(); 1336 } 1337 1338 void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, 1339 boolean insertAtFirst, int intersectX, int intersectY) { 1340 final CellLayout cellLayout = (CellLayout) getChildAt(screen); 1341 View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); 1342 1343 final int[] cellXY = new int[2]; 1344 cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 1345 addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 1346 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1347 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1348 cellXY[0], cellXY[1]); 1349 } 1350 1351 private void setPositionForDropAnimation( 1352 View dragView, int dragViewX, int dragViewY, View parent, View child) { 1353 final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1354 1355 // Based on the position of the drag view, find the top left of the original view 1356 int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2; 1357 int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2; 1358 viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX); 1359 viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY); 1360 1361 // Set its old pos (in the new parent's coordinates); it will be animated 1362 // in animateViewIntoPosition after the next layout pass 1363 lp.oldX = viewX - (parent.getLeft() - mScrollX); 1364 lp.oldY = viewY - (parent.getTop() - mScrollY); 1365 } 1366 1367 public void animateViewIntoPosition(final View view) { 1368 final CellLayout parent = (CellLayout) view.getParent(); 1369 final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1370 1371 // Convert the animation params to be relative to the Workspace, not the CellLayout 1372 final int fromX = lp.oldX + parent.getLeft(); 1373 final int fromY = lp.oldY + parent.getTop(); 1374 1375 final int dx = lp.x - lp.oldX; 1376 final int dy = lp.y - lp.oldY; 1377 1378 // Calculate the duration of the animation based on the object's distance 1379 final float dist = (float) Math.sqrt(dx*dx + dy*dy); 1380 final Resources res = getResources(); 1381 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); 1382 int duration = res.getInteger(R.integer.config_dropAnimMaxDuration); 1383 if (dist < maxDist) { 1384 duration *= mQuintEaseOutInterpolator.getInterpolation(dist / maxDist); 1385 } 1386 1387 if (mDropAnim != null) { 1388 // This should really be end(), but that will not be called synchronously, 1389 // so instead we use LauncherAnimatorListenerAdapter.onAnimationEndOrCancel() 1390 // and call cancel() here. 1391 mDropAnim.cancel(); 1392 } 1393 mDropAnim = new ValueAnimator(); 1394 mDropAnim.setInterpolator(mQuintEaseOutInterpolator); 1395 1396 // The view is invisible during the animation; we render it manually. 1397 mDropAnim.addListener(new LauncherAnimatorListenerAdapter() { 1398 public void onAnimationStart(Animator animation) { 1399 // Set this here so that we don't render it until the animation begins 1400 mDropView = view; 1401 } 1402 1403 public void onAnimationEndOrCancel(Animator animation) { 1404 if (mDropView != null) { 1405 mDropView.setVisibility(View.VISIBLE); 1406 mDropView = null; 1407 } 1408 } 1409 }); 1410 1411 mDropAnim.setDuration(duration); 1412 mDropAnim.setFloatValues(0.0f, 1.0f); 1413 mDropAnim.removeAllUpdateListeners(); 1414 mDropAnim.addUpdateListener(new AnimatorUpdateListener() { 1415 public void onAnimationUpdate(ValueAnimator animation) { 1416 final float percent = (Float) animation.getAnimatedValue(); 1417 // Invalidate the old position 1418 invalidate(mDropViewPos[0], mDropViewPos[1], 1419 mDropViewPos[0] + view.getWidth(), mDropViewPos[1] + view.getHeight()); 1420 1421 mDropViewPos[0] = fromX + (int) (percent * dx + 0.5f); 1422 mDropViewPos[1] = fromY + (int) (percent * dy + 0.5f); 1423 invalidate(mDropViewPos[0], mDropViewPos[1], 1424 mDropViewPos[0] + view.getWidth(), mDropViewPos[1] + view.getHeight()); 1425 } 1426 }); 1427 1428 1429 view.setVisibility(View.INVISIBLE); 1430 1431 if (!mScroller.isFinished()) { 1432 mAnimOnPageEndMoving = mDropAnim; 1433 } else { 1434 mDropAnim.start(); 1435 } 1436 } 1437 1438 /** 1439 * {@inheritDoc} 1440 */ 1441 public boolean acceptDrop(DragSource source, int x, int y, 1442 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1443 1444 // If it's an external drop (e.g. from All Apps), check if it should be accepted 1445 if (source != this) { 1446 // Don't accept the drop if we're not over a screen at time of drop 1447 if (mDragTargetLayout == null) { 1448 return false; 1449 } 1450 1451 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1452 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1453 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1454 1455 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1456 1457 // Don't accept the drop if there's no room for the item 1458 if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1459 mLauncher.showOutOfSpaceMessage(); 1460 return false; 1461 } 1462 } 1463 return true; 1464 } 1465 1466 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1467 DragView dragView, Object dragInfo) { 1468 1469 int originX = x - xOffset; 1470 int originY = y - yOffset; 1471 1472 if (mIsSmall || mIsInUnshrinkAnimation) { 1473 // get originX and originY in the local coordinate system of the screen 1474 mTempOriginXY[0] = originX; 1475 mTempOriginXY[1] = originY; 1476 mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY); 1477 originX = (int)mTempOriginXY[0]; 1478 originY = (int)mTempOriginXY[1]; 1479 } 1480 1481 if (source != this) { 1482 onDropExternal(originX, originY, dragInfo, mDragTargetLayout); 1483 } else if (mDragInfo != null) { 1484 final View cell = mDragInfo.cell; 1485 CellLayout dropTargetLayout = mDragTargetLayout; 1486 1487 // Handle the case where the user drops when in the scroll area. 1488 // This is treated as a drop on the adjacent page. 1489 if (dropTargetLayout == null && mInScrollArea) { 1490 if (mPendingScrollDirection == DragController.SCROLL_LEFT) { 1491 dropTargetLayout = (CellLayout) getChildAt(mCurrentPage - 1); 1492 } else if (mPendingScrollDirection == DragController.SCROLL_RIGHT) { 1493 dropTargetLayout = (CellLayout) getChildAt(mCurrentPage + 1); 1494 } 1495 } 1496 1497 if (dropTargetLayout != null) { 1498 // Move internally 1499 mTargetCell = findNearestVacantArea(originX, originY, 1500 mDragInfo.spanX, mDragInfo.spanY, cell, dropTargetLayout, 1501 mTargetCell); 1502 1503 final int screen = (mTargetCell == null) ? 1504 mDragInfo.screen : indexOfChild(dropTargetLayout); 1505 1506 if (screen != mCurrentPage) { 1507 snapToPage(screen); 1508 } 1509 1510 if (mTargetCell != null) { 1511 if (screen != mDragInfo.screen) { 1512 // Reparent the view 1513 ((CellLayout) getChildAt(mDragInfo.screen)).removeView(cell); 1514 addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], 1515 mDragInfo.spanX, mDragInfo.spanY); 1516 } 1517 1518 // update the item's position after drop 1519 final ItemInfo info = (ItemInfo) cell.getTag(); 1520 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 1521 dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 1522 lp.cellX = mTargetCell[0]; 1523 lp.cellY = mTargetCell[1]; 1524 cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, 1525 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 1526 1527 LauncherModel.moveItemInDatabase(mLauncher, info, 1528 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1529 lp.cellX, lp.cellY); 1530 } 1531 } 1532 1533 final CellLayout parent = (CellLayout) cell.getParent(); 1534 1535 // Prepare it to be animated into its new position 1536 // This must be called after the view has been re-parented 1537 setPositionForDropAnimation(dragView, originX, originY, parent, cell); 1538 parent.onDropChild(cell); 1539 } 1540 } 1541 1542 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1543 int yOffset, DragView dragView, Object dragInfo) { 1544 mDragTargetLayout = null; // Reset the drag state 1545 1546 if (!mIsSmall) { 1547 mDragTargetLayout = getCurrentDropLayout(); 1548 mDragTargetLayout.onDragEnter(); 1549 showOutlines(); 1550 } 1551 } 1552 1553 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, 1554 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1555 1556 if (mIsSmall || mIsInUnshrinkAnimation) { 1557 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 1558 return null; 1559 } 1560 // We may need to delegate the drag to a child view. If a 1x1 item 1561 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1562 // then drag events should be handled by that child. 1563 1564 ItemInfo item = (ItemInfo)dragInfo; 1565 CellLayout currentLayout = getCurrentDropLayout(); 1566 1567 int dragPointX, dragPointY; 1568 if (item.spanX == 1 && item.spanY == 1) { 1569 // For a 1x1, calculate the drop cell exactly as in onDragOver 1570 dragPointX = x - xOffset; 1571 dragPointY = y - yOffset; 1572 } else { 1573 // Otherwise, use the exact drag coordinates 1574 dragPointX = x; 1575 dragPointY = y; 1576 } 1577 dragPointX += mScrollX - currentLayout.getLeft(); 1578 dragPointY += mScrollY - currentLayout.getTop(); 1579 1580 // If we are dragging over a cell that contains a DropTarget that will 1581 // accept the drop, delegate to that DropTarget. 1582 final int[] cellXY = mTempCell; 1583 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1584 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1585 if (child instanceof DropTarget) { 1586 DropTarget target = (DropTarget)child; 1587 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1588 return target; 1589 } 1590 } 1591 return null; 1592 } 1593 1594 /** 1595 * Tests to see if the drop will be accepted by Launcher, and if so, includes additional data 1596 * in the returned structure related to the widgets that match the drop (or a null list if it is 1597 * a shortcut drop). If the drop is not accepted then a null structure is returned. 1598 */ 1599 private Pair<Integer, List<WidgetMimeTypeHandlerData>> validateDrag(DragEvent event) { 1600 final LauncherModel model = mLauncher.getModel(); 1601 final ClipDescription desc = event.getClipDescription(); 1602 final int mimeTypeCount = desc.getMimeTypeCount(); 1603 for (int i = 0; i < mimeTypeCount; ++i) { 1604 final String mimeType = desc.getMimeType(i); 1605 if (mimeType.equals(InstallShortcutReceiver.SHORTCUT_MIMETYPE)) { 1606 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, null); 1607 } else { 1608 final List<WidgetMimeTypeHandlerData> widgets = 1609 model.resolveWidgetsForMimeType(mContext, mimeType); 1610 if (widgets.size() > 0) { 1611 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, widgets); 1612 } 1613 } 1614 } 1615 return null; 1616 } 1617 1618 /** 1619 * Global drag and drop handler 1620 */ 1621 @Override 1622 public boolean onDragEvent(DragEvent event) { 1623 final ClipDescription desc = event.getClipDescription(); 1624 final CellLayout layout = (CellLayout) getChildAt(mCurrentPage); 1625 final int[] pos = new int[2]; 1626 layout.getLocationOnScreen(pos); 1627 // We need to offset the drag coordinates to layout coordinate space 1628 final int x = (int) event.getX() - pos[0]; 1629 final int y = (int) event.getY() - pos[1]; 1630 1631 switch (event.getAction()) { 1632 case DragEvent.ACTION_DRAG_STARTED: { 1633 // Validate this drag 1634 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event); 1635 if (test != null) { 1636 boolean isShortcut = (test.second == null); 1637 if (isShortcut) { 1638 // Check if we have enough space on this screen to add a new shortcut 1639 if (!layout.findCellForSpan(pos, 1, 1)) { 1640 Toast.makeText(mContext, mContext.getString(R.string.out_of_space), 1641 Toast.LENGTH_SHORT).show(); 1642 return false; 1643 } 1644 } 1645 } else { 1646 // Show error message if we couldn't accept any of the items 1647 Toast.makeText(mContext, mContext.getString(R.string.external_drop_widget_error), 1648 Toast.LENGTH_SHORT).show(); 1649 return false; 1650 } 1651 1652 // Create the drag outline 1653 // We need to add extra padding to the bitmap to make room for the glow effect 1654 final Canvas canvas = new Canvas(); 1655 final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; 1656 mDragOutline = createExternalDragOutline(canvas, bitmapPadding); 1657 1658 // Show the current page outlines to indicate that we can accept this drop 1659 showOutlines(); 1660 layout.setHover(true); 1661 layout.onDragEnter(); 1662 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); 1663 1664 return true; 1665 } 1666 case DragEvent.ACTION_DRAG_LOCATION: 1667 // Visualize the drop location 1668 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); 1669 return true; 1670 case DragEvent.ACTION_DROP: { 1671 // Try and add any shortcuts 1672 int newDropCount = 0; 1673 final LauncherModel model = mLauncher.getModel(); 1674 final ClipData data = event.getClipData(); 1675 1676 // We assume that the mime types are ordered in descending importance of 1677 // representation. So we enumerate the list of mime types and alert the 1678 // user if any widgets can handle the drop. Only the most preferred 1679 // representation will be handled. 1680 pos[0] = x; 1681 pos[1] = y; 1682 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event); 1683 if (test != null) { 1684 final int index = test.first; 1685 final List<WidgetMimeTypeHandlerData> widgets = test.second; 1686 final boolean isShortcut = (widgets == null); 1687 final String mimeType = desc.getMimeType(index); 1688 if (isShortcut) { 1689 final Intent intent = data.getItem(index).getIntent(); 1690 Object info = model.infoFromShortcutIntent(mContext, intent, data.getIcon()); 1691 onDropExternal(x, y, info, layout); 1692 } else { 1693 if (widgets.size() == 1) { 1694 // If there is only one item, then go ahead and add and configure 1695 // that widget 1696 final AppWidgetProviderInfo widgetInfo = widgets.get(0).widgetInfo; 1697 final PendingAddWidgetInfo createInfo = 1698 new PendingAddWidgetInfo(widgetInfo, mimeType, data); 1699 mLauncher.addAppWidgetFromDrop(createInfo, mCurrentPage, pos); 1700 } else { 1701 // Show the widget picker dialog if there is more than one widget 1702 // that can handle this data type 1703 final InstallWidgetReceiver.WidgetListAdapter adapter = 1704 new InstallWidgetReceiver.WidgetListAdapter(mLauncher, mimeType, 1705 data, widgets, layout, mCurrentPage, pos); 1706 final AlertDialog.Builder builder = 1707 new AlertDialog.Builder(mContext); 1708 builder.setAdapter(adapter, adapter); 1709 builder.setCancelable(true); 1710 builder.setTitle(mContext.getString( 1711 R.string.external_drop_widget_pick_title)); 1712 builder.setIcon(R.drawable.ic_no_applications); 1713 builder.show(); 1714 } 1715 } 1716 } 1717 return true; 1718 } 1719 case DragEvent.ACTION_DRAG_ENDED: 1720 // Hide the page outlines after the drop 1721 layout.setHover(false); 1722 layout.onDragExit(); 1723 hideOutlines(); 1724 return true; 1725 } 1726 return super.onDragEvent(event); 1727 } 1728 1729 /* 1730 * 1731 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1732 * coordinate space. The argument xy is modified with the return result. 1733 * 1734 */ 1735 void mapPointFromSelfToChild(View v, float[] xy) { 1736 mapPointFromSelfToChild(v, xy, null); 1737 } 1738 1739 /* 1740 * 1741 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1742 * coordinate space. The argument xy is modified with the return result. 1743 * 1744 * if cachedInverseMatrix is not null, this method will just use that matrix instead of 1745 * computing it itself; we use this to avoid redundant matrix inversions in 1746 * findMatchingPageForDragOver 1747 * 1748 */ 1749 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) { 1750 if (cachedInverseMatrix == null) { 1751 v.getMatrix().invert(mTempInverseMatrix); 1752 cachedInverseMatrix = mTempInverseMatrix; 1753 } 1754 xy[0] = xy[0] + mScrollX - v.getLeft(); 1755 xy[1] = xy[1] + mScrollY - v.getTop(); 1756 cachedInverseMatrix.mapPoints(xy); 1757 } 1758 1759 /* 1760 * 1761 * Convert the 2D coordinate xy from this CellLayout's coordinate space to 1762 * the parent View's coordinate space. The argument xy is modified with the return result. 1763 * 1764 */ 1765 void mapPointFromChildToSelf(View v, float[] xy) { 1766 v.getMatrix().mapPoints(xy); 1767 xy[0] -= (mScrollX - v.getLeft()); 1768 xy[1] -= (mScrollY - v.getTop()); 1769 } 1770 1771 static private float squaredDistance(float[] point1, float[] point2) { 1772 float distanceX = point1[0] - point2[0]; 1773 float distanceY = point2[1] - point2[1]; 1774 return distanceX * distanceX + distanceY * distanceY; 1775 } 1776 1777 /* 1778 * 1779 * Returns true if the passed CellLayout cl overlaps with dragView 1780 * 1781 */ 1782 boolean overlaps(CellLayout cl, DragView dragView, 1783 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) { 1784 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1785 final float[] draggedItemTopLeft = mTempDragCoordinates; 1786 draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset(); 1787 draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset(); 1788 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates; 1789 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth(); 1790 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight(); 1791 1792 // Transform the dragged item's top left coordinates 1793 // to the CellLayout's local coordinates 1794 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix); 1795 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]); 1796 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]); 1797 1798 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) { 1799 // Transform the dragged item's bottom right coordinates 1800 // to the CellLayout's local coordinates 1801 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix); 1802 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]); 1803 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]); 1804 1805 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) { 1806 float overlap = (overlapRegionRight - overlapRegionLeft) * 1807 (overlapRegionBottom - overlapRegionTop); 1808 if (overlap > 0) { 1809 return true; 1810 } 1811 } 1812 } 1813 return false; 1814 } 1815 1816 /* 1817 * 1818 * This method returns the CellLayout that is currently being dragged to. In order to drag 1819 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second 1820 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one 1821 * 1822 * Return null if no CellLayout is currently being dragged over 1823 * 1824 */ 1825 private CellLayout findMatchingPageForDragOver( 1826 DragView dragView, int originX, int originY, int offsetX, int offsetY) { 1827 // We loop through all the screens (ie CellLayouts) and see which ones overlap 1828 // with the item being dragged and then choose the one that's closest to the touch point 1829 final int screenCount = getChildCount(); 1830 CellLayout bestMatchingScreen = null; 1831 float smallestDistSoFar = Float.MAX_VALUE; 1832 1833 for (int i = 0; i < screenCount; i++) { 1834 CellLayout cl = (CellLayout)getChildAt(i); 1835 1836 final float[] touchXy = mTempTouchCoordinates; 1837 touchXy[0] = originX + offsetX; 1838 touchXy[1] = originY + offsetY; 1839 1840 // Transform the touch coordinates to the CellLayout's local coordinates 1841 // If the touch point is within the bounds of the cell layout, we can return immediately 1842 cl.getMatrix().invert(mTempInverseMatrix); 1843 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); 1844 1845 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && 1846 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { 1847 return cl; 1848 } 1849 1850 if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) { 1851 // Get the center of the cell layout in screen coordinates 1852 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates; 1853 cellLayoutCenter[0] = cl.getWidth()/2; 1854 cellLayoutCenter[1] = cl.getHeight()/2; 1855 mapPointFromChildToSelf(cl, cellLayoutCenter); 1856 1857 touchXy[0] = originX + offsetX; 1858 touchXy[1] = originY + offsetY; 1859 1860 // Calculate the distance between the center of the CellLayout 1861 // and the touch point 1862 float dist = squaredDistance(touchXy, cellLayoutCenter); 1863 1864 if (dist < smallestDistSoFar) { 1865 smallestDistSoFar = dist; 1866 bestMatchingScreen = cl; 1867 } 1868 } 1869 } 1870 return bestMatchingScreen; 1871 } 1872 1873 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1874 DragView dragView, Object dragInfo) { 1875 // When touch is inside the scroll area, skip dragOver actions for the current screen 1876 if (!mInScrollArea) { 1877 CellLayout layout; 1878 int originX = x - xOffset; 1879 int originY = y - yOffset; 1880 if (mIsSmall || mIsInUnshrinkAnimation) { 1881 layout = findMatchingPageForDragOver( 1882 dragView, originX, originY, xOffset, yOffset); 1883 1884 if (layout != mDragTargetLayout) { 1885 if (mDragTargetLayout != null) { 1886 mDragTargetLayout.setHover(false); 1887 } 1888 mDragTargetLayout = layout; 1889 if (mDragTargetLayout != null) { 1890 mDragTargetLayout.setHover(true); 1891 } 1892 } 1893 } else { 1894 layout = getCurrentDropLayout(); 1895 1896 final ItemInfo item = (ItemInfo)dragInfo; 1897 if (dragInfo instanceof LauncherAppWidgetInfo) { 1898 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1899 1900 if (widgetInfo.spanX == -1) { 1901 // Calculate the grid spans needed to fit this widget 1902 int[] spans = layout.rectToCell( 1903 widgetInfo.minWidth, widgetInfo.minHeight, null); 1904 item.spanX = spans[0]; 1905 item.spanY = spans[1]; 1906 } 1907 } 1908 1909 if (source instanceof AllAppsPagedView) { 1910 // This is a hack to fix the point used to determine which cell an icon from 1911 // the all apps screen is over 1912 if (item != null && item.spanX == 1 && layout != null) { 1913 int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2; 1914 1915 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1916 if (dragView.getDragRegionWidth() != layout.getCellWidth()) { 1917 dragView.setDragRegion(dragView.getDragRegionLeft(), 1918 dragView.getDragRegionTop(), 1919 layout.getCellWidth(), 1920 dragView.getDragRegionHeight()); 1921 } 1922 } 1923 } 1924 1925 if (layout != mDragTargetLayout) { 1926 if (mDragTargetLayout != null) { 1927 mDragTargetLayout.onDragExit(); 1928 } 1929 layout.onDragEnter(); 1930 mDragTargetLayout = layout; 1931 } 1932 1933 // only visualize the drop locations for moving icons within the home screen on 1934 // tablet on phone, we also visualize icons dragged in from All Apps 1935 if ((!LauncherApplication.isScreenXLarge() || source == this) 1936 && mDragTargetLayout != null) { 1937 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1938 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 1939 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 1940 mDragTargetLayout.visualizeDropLocation(child, mDragOutline, 1941 localOriginX, localOriginY, item.spanX, item.spanY); 1942 } 1943 } 1944 } 1945 } 1946 1947 public void onDragExit(DragSource source, int x, int y, int xOffset, 1948 int yOffset, DragView dragView, Object dragInfo) { 1949 if (mDragTargetLayout != null) { 1950 mDragTargetLayout.onDragExit(); 1951 } 1952 if (!mIsPageMoving) { 1953 hideOutlines(); 1954 } 1955 clearAllHovers(); 1956 } 1957 1958 private void onDropExternal(int x, int y, Object dragInfo, 1959 CellLayout cellLayout) { 1960 onDropExternal(x, y, dragInfo, cellLayout, false); 1961 } 1962 1963 @Override 1964 public void getHitRect(Rect outRect) { 1965 // We want the workspace to have the whole area of the display (it will find the correct 1966 // cell layout to drop to in the existing drag/drop logic. 1967 final Display d = mLauncher.getWindowManager().getDefaultDisplay(); 1968 outRect.set(0, 0, d.getWidth(), d.getHeight()); 1969 } 1970 1971 /** 1972 * Add the item specified by dragInfo to the given layout. 1973 * This is basically the equivalent of onDropExternal, except it's not initiated 1974 * by drag and drop. 1975 * @return true if successful 1976 */ 1977 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1978 CellLayout cl = (CellLayout) layout; 1979 ItemInfo info = (ItemInfo) dragInfo; 1980 1981 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1982 onDropExternal(-1, -1, dragInfo, cl, false); 1983 return true; 1984 } 1985 mLauncher.showOutOfSpaceMessage(); 1986 return false; 1987 } 1988 1989 // Drag from somewhere else 1990 // NOTE: This can also be called when we are outside of a drag event, when we want 1991 // to add an item to one of the workspace screens. 1992 private void onDropExternal(int x, int y, Object dragInfo, 1993 CellLayout cellLayout, boolean insertAtFirst) { 1994 int screen = indexOfChild(cellLayout); 1995 if (dragInfo instanceof PendingAddItemInfo) { 1996 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1997 // When dragging and dropping from customization tray, we deal with creating 1998 // widgets/shortcuts/folders in a slightly different way 1999 int[] touchXY = new int[2]; 2000 touchXY[0] = x; 2001 touchXY[1] = y; 2002 switch (info.itemType) { 2003 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 2004 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) info, screen, touchXY); 2005 break; 2006 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 2007 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 2008 break; 2009 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 2010 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 2011 break; 2012 default: 2013 throw new IllegalStateException("Unknown item type: " + info.itemType); 2014 } 2015 cellLayout.onDragExit(); 2016 cellLayout.animateDrop(); 2017 return; 2018 } 2019 2020 // This is for other drag/drop cases, like dragging from All Apps 2021 ItemInfo info = (ItemInfo) dragInfo; 2022 2023 View view = null; 2024 2025 switch (info.itemType) { 2026 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 2027 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 2028 if (info.container == NO_ID && info instanceof ApplicationInfo) { 2029 // Came from all apps -- make a copy 2030 info = new ShortcutInfo((ApplicationInfo) info); 2031 } 2032 view = mLauncher.createShortcut(R.layout.application, cellLayout, 2033 (ShortcutInfo) info); 2034 break; 2035 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 2036 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 2037 cellLayout, (UserFolderInfo) info, mIconCache); 2038 break; 2039 default: 2040 throw new IllegalStateException("Unknown item type: " + info.itemType); 2041 } 2042 2043 // If the view is null, it has already been added. 2044 if (view == null) { 2045 cellLayout.onDragExit(); 2046 } else { 2047 mTargetCell = new int[]{x, y}; 2048 cellLayout.findCellForSpan(mTargetCell, 1, 1); 2049 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 2050 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 2051 cellLayout.onDropChild(view); 2052 cellLayout.animateDrop(); 2053 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 2054 2055 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 2056 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 2057 lp.cellX, lp.cellY); 2058 } 2059 } 2060 2061 /** 2062 * Return the current {@link CellLayout}, correctly picking the destination 2063 * screen while a scroll is in progress. 2064 */ 2065 private CellLayout getCurrentDropLayout() { 2066 // if we're currently small, use findMatchingPageForDragOver instead 2067 if (mIsSmall) return null; 2068 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 2069 return (CellLayout) getChildAt(index); 2070 } 2071 2072 /** 2073 * Return the current CellInfo describing our current drag; this method exists 2074 * so that Launcher can sync this object with the correct info when the activity is created/ 2075 * destroyed 2076 * 2077 */ 2078 public CellLayout.CellInfo getDragInfo() { 2079 return mDragInfo; 2080 } 2081 2082 /** 2083 * Calculate the nearest cell where the given object would be dropped. 2084 */ 2085 private int[] findNearestVacantArea(int pixelX, int pixelY, 2086 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 2087 2088 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 2089 int localPixelY = pixelY - (layout.getTop() - mScrollY); 2090 2091 // Find the best target drop location 2092 return layout.findNearestVacantArea( 2093 localPixelX, localPixelY, spanX, spanY, ignoreView, recycle); 2094 } 2095 2096 /** 2097 * Estimate the size that a child with the given dimensions will take in the current screen. 2098 */ 2099 void estimateChildSize(int minWidth, int minHeight, int[] result) { 2100 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 2101 } 2102 2103 void setLauncher(Launcher launcher) { 2104 mLauncher = launcher; 2105 } 2106 2107 public void setDragController(DragController dragController) { 2108 mDragController = dragController; 2109 } 2110 2111 public void onDropCompleted(View target, boolean success) { 2112 if (success) { 2113 if (target != this && mDragInfo != null) { 2114 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 2115 cellLayout.removeView(mDragInfo.cell); 2116 if (mDragInfo.cell instanceof DropTarget) { 2117 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 2118 } 2119 // final Object tag = mDragInfo.cell.getTag(); 2120 } 2121 } else if (mDragInfo != null) { 2122 ((CellLayout) getChildAt(mDragInfo.screen)).onDropChild(mDragInfo.cell); 2123 } 2124 2125 mDragOutline = null; 2126 mDragInfo = null; 2127 } 2128 2129 public boolean isDropEnabled() { 2130 return true; 2131 } 2132 2133 @Override 2134 protected void onRestoreInstanceState(Parcelable state) { 2135 super.onRestoreInstanceState(state); 2136 Launcher.setScreen(mCurrentPage); 2137 } 2138 2139 @Override 2140 public void scrollLeft() { 2141 if (!mIsSmall && !mIsInUnshrinkAnimation) { 2142 super.scrollLeft(); 2143 } 2144 } 2145 2146 @Override 2147 public void scrollRight() { 2148 if (!mIsSmall && !mIsInUnshrinkAnimation) { 2149 super.scrollRight(); 2150 } 2151 } 2152 2153 @Override 2154 public void onEnterScrollArea(int direction) { 2155 if (!mIsSmall && !mIsInUnshrinkAnimation) { 2156 mInScrollArea = true; 2157 mPendingScrollDirection = direction; 2158 2159 final int page = mCurrentPage + (direction == DragController.SCROLL_LEFT ? -1 : 1); 2160 final CellLayout layout = (CellLayout) getChildAt(page); 2161 2162 if (layout != null) { 2163 layout.setHover(true); 2164 2165 if (mDragTargetLayout != null) { 2166 mDragTargetLayout.onDragExit(); 2167 mDragTargetLayout = null; 2168 } 2169 } 2170 } 2171 } 2172 2173 private void clearAllHovers() { 2174 final int childCount = getChildCount(); 2175 for (int i = 0; i < childCount; i++) { 2176 ((CellLayout) getChildAt(i)).setHover(false); 2177 } 2178 } 2179 2180 @Override 2181 public void onExitScrollArea() { 2182 if (mInScrollArea) { 2183 mInScrollArea = false; 2184 mPendingScrollDirection = DragController.SCROLL_NONE; 2185 clearAllHovers(); 2186 } 2187 } 2188 2189 public Folder getFolderForTag(Object tag) { 2190 final int screenCount = getChildCount(); 2191 for (int screen = 0; screen < screenCount; screen++) { 2192 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 2193 int count = currentScreen.getChildCount(); 2194 for (int i = 0; i < count; i++) { 2195 View child = currentScreen.getChildAt(i); 2196 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 2197 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 2198 Folder f = (Folder) child; 2199 if (f.getInfo() == tag && f.getInfo().opened) { 2200 return f; 2201 } 2202 } 2203 } 2204 } 2205 return null; 2206 } 2207 2208 public View getViewForTag(Object tag) { 2209 int screenCount = getChildCount(); 2210 for (int screen = 0; screen < screenCount; screen++) { 2211 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 2212 int count = currentScreen.getChildCount(); 2213 for (int i = 0; i < count; i++) { 2214 View child = currentScreen.getChildAt(i); 2215 if (child.getTag() == tag) { 2216 return child; 2217 } 2218 } 2219 } 2220 return null; 2221 } 2222 2223 2224 void removeItems(final ArrayList<ApplicationInfo> apps) { 2225 final int screenCount = getChildCount(); 2226 final PackageManager manager = getContext().getPackageManager(); 2227 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 2228 2229 final HashSet<String> packageNames = new HashSet<String>(); 2230 final int appCount = apps.size(); 2231 for (int i = 0; i < appCount; i++) { 2232 packageNames.add(apps.get(i).componentName.getPackageName()); 2233 } 2234 2235 for (int i = 0; i < screenCount; i++) { 2236 final CellLayout layout = (CellLayout) getChildAt(i); 2237 2238 // Avoid ANRs by treating each screen separately 2239 post(new Runnable() { 2240 public void run() { 2241 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 2242 childrenToRemove.clear(); 2243 2244 int childCount = layout.getChildCount(); 2245 for (int j = 0; j < childCount; j++) { 2246 final View view = layout.getChildAt(j); 2247 Object tag = view.getTag(); 2248 2249 if (tag instanceof ShortcutInfo) { 2250 final ShortcutInfo info = (ShortcutInfo) tag; 2251 final Intent intent = info.intent; 2252 final ComponentName name = intent.getComponent(); 2253 2254 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 2255 for (String packageName: packageNames) { 2256 if (packageName.equals(name.getPackageName())) { 2257 LauncherModel.deleteItemFromDatabase(mLauncher, info); 2258 childrenToRemove.add(view); 2259 } 2260 } 2261 } 2262 } else if (tag instanceof UserFolderInfo) { 2263 final UserFolderInfo info = (UserFolderInfo) tag; 2264 final ArrayList<ShortcutInfo> contents = info.contents; 2265 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 2266 final int contentsCount = contents.size(); 2267 boolean removedFromFolder = false; 2268 2269 for (int k = 0; k < contentsCount; k++) { 2270 final ShortcutInfo appInfo = contents.get(k); 2271 final Intent intent = appInfo.intent; 2272 final ComponentName name = intent.getComponent(); 2273 2274 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 2275 for (String packageName: packageNames) { 2276 if (packageName.equals(name.getPackageName())) { 2277 toRemove.add(appInfo); 2278 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 2279 removedFromFolder = true; 2280 } 2281 } 2282 } 2283 } 2284 2285 contents.removeAll(toRemove); 2286 if (removedFromFolder) { 2287 final Folder folder = getOpenFolder(); 2288 if (folder != null) 2289 folder.notifyDataSetChanged(); 2290 } 2291 } else if (tag instanceof LiveFolderInfo) { 2292 final LiveFolderInfo info = (LiveFolderInfo) tag; 2293 final Uri uri = info.uri; 2294 final ProviderInfo providerInfo = manager.resolveContentProvider( 2295 uri.getAuthority(), 0); 2296 2297 if (providerInfo != null) { 2298 for (String packageName: packageNames) { 2299 if (packageName.equals(providerInfo.packageName)) { 2300 LauncherModel.deleteItemFromDatabase(mLauncher, info); 2301 childrenToRemove.add(view); 2302 } 2303 } 2304 } 2305 } else if (tag instanceof LauncherAppWidgetInfo) { 2306 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 2307 final AppWidgetProviderInfo provider = 2308 widgets.getAppWidgetInfo(info.appWidgetId); 2309 if (provider != null) { 2310 for (String packageName: packageNames) { 2311 if (packageName.equals(provider.provider.getPackageName())) { 2312 LauncherModel.deleteItemFromDatabase(mLauncher, info); 2313 childrenToRemove.add(view); 2314 } 2315 } 2316 } 2317 } 2318 } 2319 2320 childCount = childrenToRemove.size(); 2321 for (int j = 0; j < childCount; j++) { 2322 View child = childrenToRemove.get(j); 2323 layout.removeViewInLayout(child); 2324 if (child instanceof DropTarget) { 2325 mDragController.removeDropTarget((DropTarget)child); 2326 } 2327 } 2328 2329 if (childCount > 0) { 2330 layout.requestLayout(); 2331 layout.invalidate(); 2332 } 2333 } 2334 }); 2335 } 2336 } 2337 2338 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 2339 final int screenCount = getChildCount(); 2340 for (int i = 0; i < screenCount; i++) { 2341 final CellLayout layout = (CellLayout) getChildAt(i); 2342 int childCount = layout.getChildCount(); 2343 for (int j = 0; j < childCount; j++) { 2344 final View view = layout.getChildAt(j); 2345 Object tag = view.getTag(); 2346 if (tag instanceof ShortcutInfo) { 2347 ShortcutInfo info = (ShortcutInfo)tag; 2348 // We need to check for ACTION_MAIN otherwise getComponent() might 2349 // return null for some shortcuts (for instance, for shortcuts to 2350 // web pages.) 2351 final Intent intent = info.intent; 2352 final ComponentName name = intent.getComponent(); 2353 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 2354 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 2355 final int appCount = apps.size(); 2356 for (int k = 0; k < appCount; k++) { 2357 ApplicationInfo app = apps.get(k); 2358 if (app.componentName.equals(name)) { 2359 info.setIcon(mIconCache.getIcon(info.intent)); 2360 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 2361 new FastBitmapDrawable(info.getIcon(mIconCache)), 2362 null, null); 2363 } 2364 } 2365 } 2366 } 2367 } 2368 } 2369 } 2370 2371 void moveToDefaultScreen(boolean animate) { 2372 if (mIsSmall || mIsInUnshrinkAnimation) { 2373 mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage)); 2374 } else if (animate) { 2375 snapToPage(mDefaultPage); 2376 } else { 2377 setCurrentPage(mDefaultPage); 2378 } 2379 getChildAt(mDefaultPage).requestFocus(); 2380 } 2381 2382 void setIndicators(Drawable previous, Drawable next) { 2383 mPreviousIndicator = previous; 2384 mNextIndicator = next; 2385 previous.setLevel(mCurrentPage); 2386 next.setLevel(mCurrentPage); 2387 } 2388 2389 @Override 2390 public void syncPages() { 2391 } 2392 2393 @Override 2394 public void syncPageItems(int page) { 2395 } 2396 2397} 2398