Workspace.java revision df4b83dd9d6380ab963c62d1f9d1312efc87cb0f
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 com.android.launcher.R; 20 21import android.animation.Animator; 22import android.animation.Animator.AnimatorListener; 23import android.animation.AnimatorListenerAdapter; 24import android.animation.AnimatorSet; 25import android.animation.ObjectAnimator; 26import android.animation.PropertyValuesHolder; 27import android.app.WallpaperManager; 28import android.appwidget.AppWidgetManager; 29import android.appwidget.AppWidgetProviderInfo; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.pm.PackageManager; 34import android.content.pm.ProviderInfo; 35import android.content.res.Resources; 36import android.content.res.TypedArray; 37import android.graphics.Bitmap; 38import android.graphics.Canvas; 39import android.graphics.Matrix; 40import android.graphics.Rect; 41import android.graphics.Region.Op; 42import android.graphics.drawable.Drawable; 43import android.net.Uri; 44import android.os.IBinder; 45import android.os.Parcelable; 46import android.util.AttributeSet; 47import android.util.Log; 48import android.view.MotionEvent; 49import android.view.View; 50import android.widget.TextView; 51 52import java.util.ArrayList; 53import java.util.HashSet; 54 55/** 56 * The workspace is a wide area with a wallpaper and a finite number of pages. 57 * Each page contains a number of icons, folders or widgets the user can 58 * interact with. A workspace is meant to be used with a fixed width only. 59 */ 60public class Workspace extends SmoothPagedView 61 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 62 @SuppressWarnings({"UnusedDeclaration"}) 63 private static final String TAG = "Launcher.Workspace"; 64 65 // This is how much the workspace shrinks when we enter all apps or 66 // customization mode 67 private static final float SHRINK_FACTOR = 0.16f; 68 69 // Y rotation to apply to the workspace screens 70 private static final float WORKSPACE_ROTATION = 12.5f; 71 72 // These are extra scale factors to apply to the mini home screens 73 // so as to achieve the desired transform 74 private static final float EXTRA_SCALE_FACTOR_0 = 0.97f; 75 private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; 76 private static final float EXTRA_SCALE_FACTOR_2 = 1.08f; 77 78 private static final int BACKGROUND_FADE_OUT_DELAY = 300; 79 private static final int BACKGROUND_FADE_OUT_DURATION = 300; 80 private static final int BACKGROUND_FADE_IN_DURATION = 100; 81 82 // These animators are used to fade the background 83 private ObjectAnimator mBackgroundFadeInAnimation; 84 private ObjectAnimator mBackgroundFadeOutAnimation; 85 private float mBackgroundAlpha = 0; 86 87 private final WallpaperManager mWallpaperManager; 88 89 private int mDefaultPage; 90 91 private boolean mWaitingToShrinkToBottom = false; 92 93 private boolean mPageMoving = false; 94 95 /** 96 * CellInfo for the cell that is currently being dragged 97 */ 98 private CellLayout.CellInfo mDragInfo; 99 100 /** 101 * Target drop area calculated during last acceptDrop call. 102 */ 103 private int[] mTargetCell = null; 104 105 /** 106 * The CellLayout that is currently being dragged over 107 */ 108 private CellLayout mDragTargetLayout = null; 109 110 private Launcher mLauncher; 111 private IconCache mIconCache; 112 private DragController mDragController; 113 114 // These are temporary variables to prevent having to allocate a new object just to 115 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 116 private int[] mTempCell = new int[2]; 117 private int[] mTempEstimate = new int[2]; 118 private float[] mTempOriginXY = new float[2]; 119 private float[] mTempDragCoordinates = new float[2]; 120 private float[] mTempTouchCoordinates = new float[2]; 121 private float[] mTempCellLayoutCenterCoordinates = new float[2]; 122 private float[] mTempDragBottomRightCoordinates = new float[2]; 123 private Matrix mTempInverseMatrix = new Matrix(); 124 125 private static final int DEFAULT_CELL_COUNT_X = 4; 126 private static final int DEFAULT_CELL_COUNT_Y = 4; 127 128 private Drawable mPreviousIndicator; 129 private Drawable mNextIndicator; 130 131 // State variable that indicates whether the pages are small (ie when you're 132 // in all apps or customize mode) 133 private boolean mIsSmall = false; 134 private boolean mIsInUnshrinkAnimation = false; 135 private AnimatorListener mUnshrinkAnimationListener; 136 private enum ShrinkPosition { 137 SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE }; 138 private ShrinkPosition mShrunkenState; 139 140 private boolean mInScrollArea = false; 141 142 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper(); 143 private Bitmap mDragOutline = null; 144 private final Rect mTempRect = new Rect(); 145 private final int[] mTempXY = new int[2]; 146 147 /** 148 * Used to inflate the Workspace from XML. 149 * 150 * @param context The application's context. 151 * @param attrs The attributes set containing the Workspace's customization values. 152 */ 153 public Workspace(Context context, AttributeSet attrs) { 154 this(context, attrs, 0); 155 } 156 157 /** 158 * Used to inflate the Workspace from XML. 159 * 160 * @param context The application's context. 161 * @param attrs The attributes set containing the Workspace's customization values. 162 * @param defStyle Unused. 163 */ 164 public Workspace(Context context, AttributeSet attrs, int defStyle) { 165 super(context, attrs, defStyle); 166 mContentIsRefreshable = false; 167 168 if (!LauncherApplication.isScreenXLarge()) { 169 mFadeInAdjacentScreens = false; 170 } 171 172 mWallpaperManager = WallpaperManager.getInstance(context); 173 174 TypedArray a = context.obtainStyledAttributes(attrs, 175 R.styleable.Workspace, defStyle, 0); 176 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 177 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 178 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); 179 a.recycle(); 180 181 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 182 setHapticFeedbackEnabled(false); 183 184 initWorkspace(); 185 } 186 187 /** 188 * Initializes various states for this workspace. 189 */ 190 protected void initWorkspace() { 191 Context context = getContext(); 192 mCurrentPage = mDefaultPage; 193 Launcher.setScreen(mCurrentPage); 194 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 195 mIconCache = app.getIconCache(); 196 197 mUnshrinkAnimationListener = new AnimatorListenerAdapter() { 198 public void onAnimationStart(Animator animation) { 199 mIsInUnshrinkAnimation = true; 200 } 201 public void onAnimationEnd(Animator animation) { 202 mIsInUnshrinkAnimation = false; 203 } 204 }; 205 206 mSnapVelocity = 600; 207 } 208 209 @Override 210 protected int getScrollMode() { 211 if (LauncherApplication.isScreenXLarge()) { 212 return SmoothPagedView.QUINTIC_MODE; 213 } else { 214 return SmoothPagedView.OVERSHOOT_MODE; 215 } 216 } 217 218 @Override 219 public void addView(View child, int index, LayoutParams params) { 220 if (!(child instanceof CellLayout)) { 221 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 222 } 223 ((CellLayout) child).setOnInterceptTouchListener(this); 224 super.addView(child, index, params); 225 } 226 227 @Override 228 public void addView(View child) { 229 if (!(child instanceof CellLayout)) { 230 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 231 } 232 ((CellLayout) child).setOnInterceptTouchListener(this); 233 super.addView(child); 234 } 235 236 @Override 237 public void addView(View child, int index) { 238 if (!(child instanceof CellLayout)) { 239 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 240 } 241 ((CellLayout) child).setOnInterceptTouchListener(this); 242 super.addView(child, index); 243 } 244 245 @Override 246 public void addView(View child, int width, int height) { 247 if (!(child instanceof CellLayout)) { 248 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 249 } 250 ((CellLayout) child).setOnInterceptTouchListener(this); 251 super.addView(child, width, height); 252 } 253 254 @Override 255 public void addView(View child, LayoutParams params) { 256 if (!(child instanceof CellLayout)) { 257 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 258 } 259 ((CellLayout) child).setOnInterceptTouchListener(this); 260 super.addView(child, params); 261 } 262 263 /** 264 * @return The open folder on the current screen, or null if there is none 265 */ 266 Folder getOpenFolder() { 267 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 268 int count = currentPage.getChildCount(); 269 for (int i = 0; i < count; i++) { 270 View child = currentPage.getChildAt(i); 271 if (child instanceof Folder) { 272 Folder folder = (Folder) child; 273 if (folder.getInfo().opened) 274 return folder; 275 } 276 } 277 return null; 278 } 279 280 ArrayList<Folder> getOpenFolders() { 281 final int screenCount = getChildCount(); 282 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 283 284 for (int screen = 0; screen < screenCount; screen++) { 285 CellLayout currentPage = (CellLayout) getChildAt(screen); 286 int count = currentPage.getChildCount(); 287 for (int i = 0; i < count; i++) { 288 View child = currentPage.getChildAt(i); 289 if (child instanceof Folder) { 290 Folder folder = (Folder) child; 291 if (folder.getInfo().opened) 292 folders.add(folder); 293 break; 294 } 295 } 296 } 297 298 return folders; 299 } 300 301 boolean isDefaultPageShowing() { 302 return mCurrentPage == mDefaultPage; 303 } 304 305 /** 306 * Sets the current screen. 307 * 308 * @param currentPage 309 */ 310 @Override 311 void setCurrentPage(int currentPage) { 312 super.setCurrentPage(currentPage); 313 updateWallpaperOffset(mScrollX); 314 } 315 316 /** 317 * Adds the specified child in the specified screen. The position and dimension of 318 * the child are defined by x, y, spanX and spanY. 319 * 320 * @param child The child to add in one of the workspace's screens. 321 * @param screen The screen in which to add the child. 322 * @param x The X position of the child in the screen's grid. 323 * @param y The Y position of the child in the screen's grid. 324 * @param spanX The number of cells spanned horizontally by the child. 325 * @param spanY The number of cells spanned vertically by the child. 326 */ 327 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 328 addInScreen(child, screen, x, y, spanX, spanY, false); 329 } 330 331 void addInFullScreen(View child, int screen) { 332 addInScreen(child, screen, 0, 0, -1, -1); 333 } 334 335 /** 336 * Adds the specified child in the specified screen. The position and dimension of 337 * the child are defined by x, y, spanX and spanY. 338 * 339 * @param child The child to add in one of the workspace's screens. 340 * @param screen The screen in which to add the child. 341 * @param x The X position of the child in the screen's grid. 342 * @param y The Y position of the child in the screen's grid. 343 * @param spanX The number of cells spanned horizontally by the child. 344 * @param spanY The number of cells spanned vertically by the child. 345 * @param insert When true, the child is inserted at the beginning of the children list. 346 */ 347 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 348 if (screen < 0 || screen >= getChildCount()) { 349 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 350 + " (was " + screen + "); skipping child"); 351 return; 352 } 353 354 final CellLayout group = (CellLayout) getChildAt(screen); 355 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 356 if (lp == null) { 357 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 358 } else { 359 lp.cellX = x; 360 lp.cellY = y; 361 lp.cellHSpan = spanX; 362 lp.cellVSpan = spanY; 363 } 364 365 // Get the canonical child id to uniquely represent this view in this screen 366 int childId = LauncherModel.getCellLayoutChildId(-1, screen, x, y, spanX, spanY); 367 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { 368 // TODO: This branch occurs when the workspace is adding views 369 // outside of the defined grid 370 // maybe we should be deleting these items from the LauncherModel? 371 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 372 } 373 374 if (!(child instanceof Folder)) { 375 child.setHapticFeedbackEnabled(false); 376 child.setOnLongClickListener(mLongClickListener); 377 } 378 if (child instanceof DropTarget) { 379 mDragController.addDropTarget((DropTarget) child); 380 } 381 } 382 383 public boolean onTouch(View v, MotionEvent event) { 384 // this is an intercepted event being forwarded from a cell layout 385 if (mIsSmall || mIsInUnshrinkAnimation) { 386 mLauncher.onWorkspaceClick((CellLayout) v); 387 return true; 388 } else if (!mPageMoving) { 389 if (v == getChildAt(mCurrentPage - 1)) { 390 snapToPage(mCurrentPage - 1); 391 return true; 392 } else if (v == getChildAt(mCurrentPage + 1)) { 393 snapToPage(mCurrentPage + 1); 394 return true; 395 } 396 } 397 return false; 398 } 399 400 @Override 401 public boolean dispatchUnhandledMove(View focused, int direction) { 402 if (mIsSmall || mIsInUnshrinkAnimation) { 403 // when the home screens are shrunken, shouldn't allow side-scrolling 404 return false; 405 } 406 return super.dispatchUnhandledMove(focused, direction); 407 } 408 409 @Override 410 public boolean onInterceptTouchEvent(MotionEvent ev) { 411 if (mIsSmall || mIsInUnshrinkAnimation) { 412 // when the home screens are shrunken, shouldn't allow side-scrolling 413 return false; 414 } 415 return super.onInterceptTouchEvent(ev); 416 } 417 418 @Override 419 protected void determineScrollingStart(MotionEvent ev) { 420 if (!mIsSmall && !mIsInUnshrinkAnimation) super.determineScrollingStart(ev); 421 } 422 423 protected void onPageBeginMoving() { 424 if (mNextPage != INVALID_PAGE) { 425 // we're snapping to a particular screen 426 enableChildrenCache(mCurrentPage, mNextPage); 427 } else { 428 // this is when user is actively dragging a particular screen, they might 429 // swipe it either left or right (but we won't advance by more than one screen) 430 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); 431 } 432 showOutlines(); 433 mPageMoving = true; 434 } 435 436 protected void onPageEndMoving() { 437 clearChildrenCache(); 438 // Hide the outlines, as long as we're not dragging 439 if (!mDragController.dragging()) { 440 hideOutlines(); 441 } 442 mPageMoving = false; 443 } 444 445 @Override 446 protected void notifyPageSwitchListener() { 447 super.notifyPageSwitchListener(); 448 449 if (mPreviousIndicator != null) { 450 // if we know the next page, we show the indication for it right away; it looks 451 // weird if the indicators are lagging 452 int page = mNextPage; 453 if (page == INVALID_PAGE) { 454 page = mCurrentPage; 455 } 456 mPreviousIndicator.setLevel(page); 457 mNextIndicator.setLevel(page); 458 } 459 Launcher.setScreen(mCurrentPage); 460 }; 461 462 private void updateWallpaperOffset() { 463 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 464 } 465 466 private void updateWallpaperOffset(int scrollRange) { 467 final boolean isStaticWallpaper = (mWallpaperManager != null) && 468 (mWallpaperManager.getWallpaperInfo() == null); 469 if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { 470 IBinder token = getWindowToken(); 471 if (token != null) { 472 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 473 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 474 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 475 } 476 } 477 } 478 479 public void showOutlines() { 480 if (!mIsSmall && !mIsInUnshrinkAnimation) { 481 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 482 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 483 mBackgroundFadeInAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 1.0f); 484 mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION); 485 mBackgroundFadeInAnimation.start(); 486 } 487 } 488 489 public void hideOutlines() { 490 if (!mIsSmall && !mIsInUnshrinkAnimation) { 491 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 492 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 493 mBackgroundFadeOutAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 0.0f); 494 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); 495 mBackgroundFadeOutAnimation.setStartDelay(BACKGROUND_FADE_OUT_DELAY); 496 mBackgroundFadeOutAnimation.start(); 497 } 498 } 499 500 public void setBackgroundAlpha(float alpha) { 501 mBackgroundAlpha = alpha; 502 for (int i = 0; i < getChildCount(); i++) { 503 CellLayout cl = (CellLayout) getChildAt(i); 504 cl.setBackgroundAlpha(alpha); 505 } 506 } 507 508 public float getBackgroundAlpha() { 509 return mBackgroundAlpha; 510 } 511 512 @Override 513 protected void screenScrolled(int screenCenter) { 514 final int halfScreenSize = getMeasuredWidth() / 2; 515 for (int i = 0; i < getChildCount(); i++) { 516 View v = getChildAt(i); 517 if (v != null) { 518 int totalDistance = v.getMeasuredWidth() + mPageSpacing; 519 int delta = screenCenter - (getChildOffset(i) - 520 getRelativeChildOffset(i) + halfScreenSize); 521 522 float scrollProgress = delta/(totalDistance*1.0f); 523 scrollProgress = Math.min(scrollProgress, 1.0f); 524 scrollProgress = Math.max(scrollProgress, -1.0f); 525 526 float rotation = WORKSPACE_ROTATION * scrollProgress; 527 v.setRotationY(rotation); 528 } 529 } 530 } 531 532 protected void onAttachedToWindow() { 533 super.onAttachedToWindow(); 534 computeScroll(); 535 mDragController.setWindowToken(getWindowToken()); 536 } 537 538 @Override 539 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 540 super.onLayout(changed, left, top, right, bottom); 541 542 // if shrinkToBottom() is called on initialization, it has to be deferred 543 // until after the first call to onLayout so that it has the correct width 544 if (mWaitingToShrinkToBottom) { 545 shrinkToBottom(false); 546 mWaitingToShrinkToBottom = false; 547 } 548 549 if (LauncherApplication.isInPlaceRotationEnabled()) { 550 // When the device is rotated, the scroll position of the current screen 551 // needs to be refreshed 552 setCurrentPage(getCurrentPage()); 553 } 554 } 555 556 @Override 557 protected void dispatchDraw(Canvas canvas) { 558 if (mIsSmall || mIsInUnshrinkAnimation) { 559 // Draw all the workspaces if we're small 560 final int pageCount = getChildCount(); 561 final long drawingTime = getDrawingTime(); 562 for (int i = 0; i < pageCount; i++) { 563 final View page = (View) getChildAt(i); 564 565 drawChild(canvas, page, drawingTime); 566 } 567 } else { 568 super.dispatchDraw(canvas); 569 } 570 } 571 572 @Override 573 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 574 if (!mLauncher.isAllAppsVisible()) { 575 final Folder openFolder = getOpenFolder(); 576 if (openFolder != null) { 577 return openFolder.requestFocus(direction, previouslyFocusedRect); 578 } else { 579 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 580 } 581 } 582 return false; 583 } 584 585 @Override 586 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 587 if (!mLauncher.isAllAppsVisible()) { 588 final Folder openFolder = getOpenFolder(); 589 if (openFolder != null) { 590 openFolder.addFocusables(views, direction); 591 } else { 592 super.addFocusables(views, direction, focusableMode); 593 } 594 } 595 } 596 597 @Override 598 public boolean dispatchTouchEvent(MotionEvent ev) { 599 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 600 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 601 // ie when you click on a mini-screen, it zooms back to that screen) 602 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 603 return false; 604 } 605 } 606 return super.dispatchTouchEvent(ev); 607 } 608 609 void enableChildrenCache(int fromPage, int toPage) { 610 if (fromPage > toPage) { 611 final int temp = fromPage; 612 fromPage = toPage; 613 toPage = temp; 614 } 615 616 final int screenCount = getChildCount(); 617 618 fromPage = Math.max(fromPage, 0); 619 toPage = Math.min(toPage, screenCount - 1); 620 621 for (int i = fromPage; i <= toPage; i++) { 622 final CellLayout layout = (CellLayout) getChildAt(i); 623 layout.setChildrenDrawnWithCacheEnabled(true); 624 layout.setChildrenDrawingCacheEnabled(true); 625 } 626 } 627 628 void clearChildrenCache() { 629 final int screenCount = getChildCount(); 630 for (int i = 0; i < screenCount; i++) { 631 final CellLayout layout = (CellLayout) getChildAt(i); 632 layout.setChildrenDrawnWithCacheEnabled(false); 633 } 634 } 635 636 @Override 637 public boolean onTouchEvent(MotionEvent ev) { 638 if (mLauncher.isAllAppsVisible()) { 639 // Cancel any scrolling that is in progress. 640 if (!mScroller.isFinished()) { 641 mScroller.abortAnimation(); 642 } 643 setCurrentPage(mCurrentPage); 644 return false; // We don't want the events. Let them fall through to the all apps view. 645 } 646 647 return super.onTouchEvent(ev); 648 } 649 650 public boolean isSmall() { 651 return mIsSmall; 652 } 653 654 void shrinkToTop(boolean animated) { 655 shrink(ShrinkPosition.SHRINK_TO_TOP, animated); 656 } 657 658 void shrinkToMiddle() { 659 shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); 660 } 661 662 void shrinkToBottom() { 663 shrinkToBottom(true); 664 } 665 666 void shrinkToBottom(boolean animated) { 667 if (mFirstLayout) { 668 // (mFirstLayout == "first layout has not happened yet") 669 // if we get a call to shrink() as part of our initialization (for example, if 670 // Launcher is started in All Apps mode) then we need to wait for a layout call 671 // to get our width so we can layout the mini-screen views correctly 672 mWaitingToShrinkToBottom = true; 673 } else { 674 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated); 675 } 676 } 677 678 private float getYScaleForScreen(int screen) { 679 int x = Math.abs(screen - 2); 680 681 // TODO: This should be generalized for use with arbitrary rotation angles. 682 switch(x) { 683 case 0: return EXTRA_SCALE_FACTOR_0; 684 case 1: return EXTRA_SCALE_FACTOR_1; 685 case 2: return EXTRA_SCALE_FACTOR_2; 686 } 687 return 1.0f; 688 } 689 690 // we use this to shrink the workspace for the all apps view and the customize view 691 private void shrink(ShrinkPosition shrinkPosition, boolean animated) { 692 mIsSmall = true; 693 mShrunkenState = shrinkPosition; 694 695 // Stop any scrolling, move to the current page right away 696 setCurrentPage(mCurrentPage); 697 updateWhichPagesAcceptDrops(mShrunkenState); 698 699 // we intercept and reject all touch events when we're small, so be sure to reset the state 700 mTouchState = TOUCH_STATE_REST; 701 mActivePointerId = INVALID_POINTER; 702 703 final Resources res = getResources(); 704 final int screenWidth = getWidth(); 705 final int screenHeight = getHeight(); 706 707 // Making the assumption that all pages have the same width as the 0th 708 final int pageWidth = getChildAt(0).getMeasuredWidth(); 709 final int pageHeight = getChildAt(0).getMeasuredHeight(); 710 711 final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); 712 final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); 713 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); 714 715 final int screenCount = getChildCount(); 716 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; 717 718 boolean isPortrait = getMeasuredHeight() > getMeasuredWidth(); 719 float newY = (isPortrait ? 720 getResources().getDimension(R.dimen.smallScreenVerticalMarginPortrait) : 721 getResources().getDimension(R.dimen.smallScreenVerticalMarginLandscape)); 722 float finalAlpha = 1.0f; 723 float extraShrinkFactor = 1.0f; 724 if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) { 725 newY = screenHeight - newY - scaledPageHeight; 726 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 727 728 // We shrink and disappear to nothing in the case of all apps 729 // (which is when we shrink to the bottom) 730 newY = screenHeight - newY - scaledPageHeight; 731 finalAlpha = 0.0f; 732 extraShrinkFactor = 0.1f; 733 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { 734 newY = screenHeight / 2 - scaledPageHeight / 2; 735 finalAlpha = 1.0f; 736 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) { 737 newY = screenHeight / 10; 738 } 739 740 // We animate all the screens to the centered position in workspace 741 // At the same time, the screens become greyed/dimmed 742 743 // newX is initialized to the left-most position of the centered screens 744 float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; 745 746 // We are going to scale about the center of the view, so we need to adjust the positions 747 // of the views accordingly 748 newX -= (pageWidth - scaledPageWidth) / 2.0f; 749 newY -= (pageHeight - scaledPageHeight) / 2.0f; 750 for (int i = 0; i < screenCount; i++) { 751 CellLayout cl = (CellLayout) getChildAt(i); 752 753 float rotation = (-i + 2) * WORKSPACE_ROTATION; 754 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); 755 float rotationScaleY = getYScaleForScreen(i); 756 757 if (animated) { 758 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 759 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(cl, 760 PropertyValuesHolder.ofFloat("x", newX), 761 PropertyValuesHolder.ofFloat("y", newY), 762 PropertyValuesHolder.ofFloat("scaleX", 763 SHRINK_FACTOR * rotationScaleX * extraShrinkFactor), 764 PropertyValuesHolder.ofFloat("scaleY", 765 SHRINK_FACTOR * rotationScaleY * extraShrinkFactor), 766 PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha), 767 PropertyValuesHolder.ofFloat("alpha", finalAlpha), 768 PropertyValuesHolder.ofFloat("rotationY", rotation)); 769 anim.setDuration(duration); 770 anim.start(); 771 } else { 772 cl.setX((int)newX); 773 cl.setY((int)newY); 774 cl.setScaleX(SHRINK_FACTOR * rotationScaleX); 775 cl.setScaleY(SHRINK_FACTOR * rotationScaleY); 776 cl.setBackgroundAlpha(1.0f); 777 cl.setAlpha(finalAlpha); 778 cl.setRotationY(rotation); 779 } 780 // increment newX for the next screen 781 newX += scaledPageWidth + extraScaledSpacing; 782 } 783 setChildrenDrawnWithCacheEnabled(true); 784 } 785 786 787 private void updateWhichPagesAcceptDrops(ShrinkPosition state) { 788 updateWhichPagesAcceptDropsHelper(state, false, 1, 1); 789 } 790 791 792 private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) { 793 updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY); 794 } 795 796 private void updateWhichPagesAcceptDropsHelper( 797 ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) { 798 final int screenCount = getChildCount(); 799 for (int i = 0; i < screenCount; i++) { 800 CellLayout cl = (CellLayout) getChildAt(i); 801 802 switch (state) { 803 case SHRINK_TO_TOP: 804 if (!isDragHappening) { 805 boolean showDropHighlight = i == mCurrentPage; 806 cl.setAcceptsDrops(showDropHighlight); 807 break; 808 } 809 // otherwise, fall through below and mark non-full screens as accepting drops 810 case SHRINK_TO_BOTTOM_HIDDEN: 811 case SHRINK_TO_BOTTOM_VISIBLE: 812 if (!isDragHappening) { 813 // even if a drag isn't happening, we don't want to show a screen as 814 // accepting drops if it doesn't have at least one free cell 815 spanX = 1; 816 spanY = 1; 817 } 818 // the page accepts drops if we can find at least one empty spot 819 cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY)); 820 break; 821 default: 822 throw new RuntimeException( 823 "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition"); 824 } 825 } 826 } 827 828 /* 829 * 830 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we 831 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace 832 * 833 * These methods mark the appropriate pages as accepting drops (which alters their visual 834 * appearance) and, if the pages are hidden, makes them visible. 835 * 836 */ 837 public void onDragStartedWithItemSpans(int spanX, int spanY) { 838 updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY); 839 if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 840 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true); 841 } 842 } 843 844 public void onDragStartedWithItemMinSize(int minWidth, int minHeight) { 845 int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null); 846 onDragStartedWithItemSpans(spanXY[0], spanXY[1]); 847 } 848 849 // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was 850 // never dragged over 851 public void onDragStopped() { 852 updateWhichPagesAcceptDrops(mShrunkenState); 853 if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) { 854 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, true); 855 } 856 } 857 858 // We call this when we trigger an unshrink by clicking on the CellLayout cl 859 public void unshrink(CellLayout clThatWasClicked) { 860 int newCurrentPage = mCurrentPage; 861 final int screenCount = getChildCount(); 862 for (int i = 0; i < screenCount; i++) { 863 if (getChildAt(i) == clThatWasClicked) { 864 newCurrentPage = i; 865 } 866 } 867 unshrink(newCurrentPage); 868 } 869 870 @Override 871 protected boolean handlePagingClicks() { 872 return true; 873 } 874 875 private void unshrink(int newCurrentPage) { 876 if (mIsSmall) { 877 int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 878 int delta = newX - mScrollX; 879 880 final int screenCount = getChildCount(); 881 for (int i = 0; i < screenCount; i++) { 882 CellLayout cl = (CellLayout) getChildAt(i); 883 cl.setX(cl.getX() + delta); 884 } 885 setCurrentPage(newCurrentPage); 886 unshrink(); 887 } 888 } 889 890 void unshrink() { 891 unshrink(true); 892 } 893 894 void unshrink(boolean animated) { 895 if (mIsSmall) { 896 mIsSmall = false; 897 AnimatorSet s = new AnimatorSet(); 898 final int screenCount = getChildCount(); 899 900 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 901 for (int i = 0; i < screenCount; i++) { 902 final CellLayout cl = (CellLayout)getChildAt(i); 903 float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; 904 float rotation = 0.0f; 905 906 if (i < mCurrentPage) { 907 rotation = WORKSPACE_ROTATION; 908 } else if (i > mCurrentPage) { 909 rotation = -WORKSPACE_ROTATION; 910 } 911 912 if (animated) { 913 914 s.playTogether( 915 ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration), 916 ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration), 917 ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration), 918 ObjectAnimator.ofFloat(cl, "scaleY", 1.0f).setDuration(duration), 919 ObjectAnimator.ofFloat(cl, "backgroundAlpha", 0.0f).setDuration(duration), 920 ObjectAnimator.ofFloat(cl, "alpha", finalAlphaValue).setDuration(duration), 921 ObjectAnimator.ofFloat(cl, "rotationY", rotation).setDuration(duration)); 922 } else { 923 cl.setTranslationX(0.0f); 924 cl.setTranslationY(0.0f); 925 cl.setScaleX(1.0f); 926 cl.setScaleY(1.0f); 927 cl.setBackgroundAlpha(0.0f); 928 cl.setAlpha(finalAlphaValue); 929 cl.setRotationY(rotation); 930 } 931 } 932 if (animated) { 933 // If we call this when we're not animated, onAnimationEnd is never called on 934 // the listener; make sure we only use the listener when we're actually animating 935 s.addListener(mUnshrinkAnimationListener); 936 s.start(); 937 } 938 } 939 } 940 941 /** 942 * Draw the View v into the given Canvas. 943 * 944 * @param v the view to draw 945 * @param destCanvas the canvas to draw on 946 * @param padding the horizontal and vertical padding to use when drawing 947 */ 948 private void drawDragView(View v, Canvas destCanvas, int padding) { 949 final Rect clipRect = mTempRect; 950 v.getDrawingRect(clipRect); 951 952 // For a TextView, adjust the clip rect so that we don't include the text label 953 if (v instanceof TextView) { 954 final int iconHeight = ((TextView)v).getCompoundPaddingTop() - v.getPaddingTop(); 955 clipRect.bottom = clipRect.top + iconHeight; 956 } 957 958 // Draw the View into the bitmap. 959 // The translate of scrollX and scrollY is necessary when drawing TextViews, because 960 // they set scrollX and scrollY to large values to achieve centered text 961 962 destCanvas.save(); 963 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2); 964 destCanvas.clipRect(clipRect, Op.REPLACE); 965 v.draw(destCanvas); 966 destCanvas.restore(); 967 } 968 969 /** 970 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 971 * Responsibility for the bitmap is transferred to the caller. 972 */ 973 private Bitmap createDragOutline(View v, Canvas canvas, int padding) { 974 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 975 final Bitmap b = Bitmap.createBitmap( 976 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); 977 978 canvas.setBitmap(b); 979 drawDragView(v, canvas, padding); 980 mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); 981 982 return b; 983 } 984 985 /** 986 * Returns a new bitmap to show when the given View is being dragged around. 987 * Responsibility for the bitmap is transferred to the caller. 988 */ 989 private Bitmap createDragBitmap(View v, Canvas canvas, int padding) { 990 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 991 final Bitmap b = Bitmap.createBitmap( 992 mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888); 993 994 canvas.setBitmap(b); 995 canvas.drawBitmap(mDragOutline, 0, 0, null); 996 drawDragView(v, canvas, padding); 997 mOutlineHelper.applyOuterBlur(b, canvas, outlineColor); 998 999 return b; 1000 } 1001 1002 void startDrag(CellLayout.CellInfo cellInfo) { 1003 View child = cellInfo.cell; 1004 1005 // Make sure the drag was started by a long press as opposed to a long click. 1006 if (!child.isInTouchMode()) { 1007 return; 1008 } 1009 1010 mDragInfo = cellInfo; 1011 mDragInfo.screen = mCurrentPage; 1012 1013 CellLayout current = getCurrentDropLayout(); 1014 1015 current.onDragChild(child); 1016 1017 child.clearFocus(); 1018 child.setPressed(false); 1019 1020 final Canvas canvas = new Canvas(); 1021 1022 // We need to add extra padding to the bitmap to make room for the glow effect 1023 final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; 1024 1025 // The outline is used to visualize where the item will land if dropped 1026 mDragOutline = createDragOutline(child, canvas, bitmapPadding); 1027 1028 // The drag bitmap follows the touch point around on the screen 1029 final Bitmap b = createDragBitmap(child, canvas, bitmapPadding); 1030 1031 final int bmpWidth = b.getWidth(); 1032 final int bmpHeight = b.getHeight(); 1033 child.getLocationOnScreen(mTempXY); 1034 final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2; 1035 final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2; 1036 mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this, 1037 child.getTag(), DragController.DRAG_ACTION_MOVE, null); 1038 b.recycle(); 1039 } 1040 1041 void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, 1042 boolean insertAtFirst, int intersectX, int intersectY) { 1043 final CellLayout cellLayout = (CellLayout) getChildAt(screen); 1044 View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); 1045 1046 final int[] cellXY = new int[2]; 1047 cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 1048 addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 1049 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1050 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1051 cellXY[0], cellXY[1]); 1052 } 1053 1054 private void setPositionForDropAnimation( 1055 View dragView, int dragViewX, int dragViewY, View parent, View child) { 1056 final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1057 1058 // Based on the position of the drag view, find the top left of the original view 1059 int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2; 1060 int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2; 1061 viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX); 1062 viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY); 1063 1064 // Set its old pos (in the new parent's coordinates); the CellLayout will 1065 // animate it from this position during the next layout pass 1066 lp.oldX = viewX - (parent.getLeft() - mScrollX); 1067 lp.oldY = viewY - (parent.getTop() - mScrollY); 1068 } 1069 1070 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1071 DragView dragView, Object dragInfo) { 1072 1073 int originX = x - xOffset; 1074 int originY = y - yOffset; 1075 1076 if (mDragTargetLayout == null) { 1077 // Cancel the drag if we're not over a screen at time of drop 1078 if (mDragInfo != null) { 1079 // Set its position so the parent can animate it back 1080 final View parent = getChildAt(mDragInfo.screen); 1081 setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell); 1082 } 1083 return; 1084 } 1085 1086 if (mIsSmall || mIsInUnshrinkAnimation) { 1087 // get originX and originY in the local coordinate system of the screen 1088 mTempOriginXY[0] = originX; 1089 mTempOriginXY[1] = originY; 1090 mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY); 1091 originX = (int)mTempOriginXY[0]; 1092 originY = (int)mTempOriginXY[1]; 1093 } 1094 1095 if (source != this) { 1096 onDropExternal(originX, originY, dragInfo, mDragTargetLayout); 1097 } else if (mDragInfo != null) { 1098 // Move internally 1099 final View cell = mDragInfo.cell; 1100 mTargetCell = findNearestVacantArea(originX, originY, 1101 mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout, 1102 mTargetCell); 1103 1104 int screen = indexOfChild(mDragTargetLayout); 1105 if (screen != mDragInfo.screen) { 1106 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1107 originalCellLayout.removeView(cell); 1108 addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], 1109 mDragInfo.spanX, mDragInfo.spanY); 1110 } 1111 mDragTargetLayout.onDropChild(cell); 1112 1113 // update the item's position after drop 1114 final ItemInfo info = (ItemInfo) cell.getTag(); 1115 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 1116 mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 1117 lp.cellX = mTargetCell[0]; 1118 lp.cellY = mTargetCell[1]; 1119 cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, 1120 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 1121 1122 LauncherModel.moveItemInDatabase(mLauncher, info, 1123 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1124 lp.cellX, lp.cellY); 1125 1126 // Prepare it to be animated into its new position 1127 // This must be called after the view has been re-parented 1128 setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell); 1129 } 1130 } 1131 1132 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1133 int yOffset, DragView dragView, Object dragInfo) { 1134 mDragTargetLayout = null; // Reset the drag state 1135 1136 if (!mIsSmall) { 1137 mDragTargetLayout = getCurrentDropLayout(); 1138 mDragTargetLayout.onDragEnter(dragView); 1139 showOutlines(); 1140 } 1141 } 1142 1143 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, 1144 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1145 1146 if (mIsSmall || mIsInUnshrinkAnimation) { 1147 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 1148 return null; 1149 } 1150 // We may need to delegate the drag to a child view. If a 1x1 item 1151 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1152 // then drag events should be handled by that child. 1153 1154 ItemInfo item = (ItemInfo)dragInfo; 1155 CellLayout currentLayout = getCurrentDropLayout(); 1156 1157 int dragPointX, dragPointY; 1158 if (item.spanX == 1 && item.spanY == 1) { 1159 // For a 1x1, calculate the drop cell exactly as in onDragOver 1160 dragPointX = x - xOffset; 1161 dragPointY = y - yOffset; 1162 } else { 1163 // Otherwise, use the exact drag coordinates 1164 dragPointX = x; 1165 dragPointY = y; 1166 } 1167 dragPointX += mScrollX - currentLayout.getLeft(); 1168 dragPointY += mScrollY - currentLayout.getTop(); 1169 1170 // If we are dragging over a cell that contains a DropTarget that will 1171 // accept the drop, delegate to that DropTarget. 1172 final int[] cellXY = mTempCell; 1173 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1174 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1175 if (child instanceof DropTarget) { 1176 DropTarget target = (DropTarget)child; 1177 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1178 return target; 1179 } 1180 } 1181 return null; 1182 } 1183 1184 /* 1185 * 1186 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1187 * coordinate space. The argument xy is modified with the return result. 1188 * 1189 */ 1190 void mapPointFromSelfToChild(View v, float[] xy) { 1191 mapPointFromSelfToChild(v, xy, null); 1192 } 1193 1194 /* 1195 * 1196 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1197 * coordinate space. The argument xy is modified with the return result. 1198 * 1199 * if cachedInverseMatrix is not null, this method will just use that matrix instead of 1200 * computing it itself; we use this to avoid redundant matrix inversions in 1201 * findMatchingPageForDragOver 1202 * 1203 */ 1204 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) { 1205 if (cachedInverseMatrix == null) { 1206 v.getMatrix().invert(mTempInverseMatrix); 1207 cachedInverseMatrix = mTempInverseMatrix; 1208 } 1209 xy[0] = xy[0] + mScrollX - v.getLeft(); 1210 xy[1] = xy[1] + mScrollY - v.getTop(); 1211 cachedInverseMatrix.mapPoints(xy); 1212 } 1213 1214 /* 1215 * 1216 * Convert the 2D coordinate xy from this CellLayout's coordinate space to 1217 * the parent View's coordinate space. The argument xy is modified with the return result. 1218 * 1219 */ 1220 void mapPointFromChildToSelf(View v, float[] xy) { 1221 v.getMatrix().mapPoints(xy); 1222 xy[0] -= (mScrollX - v.getLeft()); 1223 xy[1] -= (mScrollY - v.getTop()); 1224 } 1225 1226 static private float squaredDistance(float[] point1, float[] point2) { 1227 float distanceX = point1[0] - point2[0]; 1228 float distanceY = point2[1] - point2[1]; 1229 return distanceX * distanceX + distanceY * distanceY; 1230 } 1231 1232 /* 1233 * 1234 * Returns true if the passed CellLayout cl overlaps with dragView 1235 * 1236 */ 1237 boolean overlaps(CellLayout cl, DragView dragView, 1238 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) { 1239 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1240 final float[] draggedItemTopLeft = mTempDragCoordinates; 1241 draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset(); 1242 draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset(); 1243 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates; 1244 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth(); 1245 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight(); 1246 1247 // Transform the dragged item's top left coordinates 1248 // to the CellLayout's local coordinates 1249 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix); 1250 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]); 1251 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]); 1252 1253 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) { 1254 // Transform the dragged item's bottom right coordinates 1255 // to the CellLayout's local coordinates 1256 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix); 1257 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]); 1258 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]); 1259 1260 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) { 1261 float overlap = (overlapRegionRight - overlapRegionLeft) * 1262 (overlapRegionBottom - overlapRegionTop); 1263 if (overlap > 0) { 1264 return true; 1265 } 1266 } 1267 } 1268 return false; 1269 } 1270 1271 /* 1272 * 1273 * This method returns the CellLayout that is currently being dragged to. In order to drag 1274 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second 1275 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one 1276 * 1277 * Return null if no CellLayout is currently being dragged over 1278 * 1279 */ 1280 private CellLayout findMatchingPageForDragOver( 1281 DragView dragView, int originX, int originY, int offsetX, int offsetY) { 1282 // We loop through all the screens (ie CellLayouts) and see which ones overlap 1283 // with the item being dragged and then choose the one that's closest to the touch point 1284 final int screenCount = getChildCount(); 1285 CellLayout bestMatchingScreen = null; 1286 float smallestDistSoFar = Float.MAX_VALUE; 1287 1288 for (int i = 0; i < screenCount; i++) { 1289 CellLayout cl = (CellLayout)getChildAt(i); 1290 1291 final float[] touchXy = mTempTouchCoordinates; 1292 touchXy[0] = originX + offsetX; 1293 touchXy[1] = originY + offsetY; 1294 1295 // Transform the touch coordinates to the CellLayout's local coordinates 1296 // If the touch point is within the bounds of the cell layout, we can return immediately 1297 cl.getMatrix().invert(mTempInverseMatrix); 1298 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); 1299 1300 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && 1301 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { 1302 return cl; 1303 } 1304 1305 if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) { 1306 // Get the center of the cell layout in screen coordinates 1307 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates; 1308 cellLayoutCenter[0] = cl.getWidth()/2; 1309 cellLayoutCenter[1] = cl.getHeight()/2; 1310 mapPointFromChildToSelf(cl, cellLayoutCenter); 1311 1312 touchXy[0] = originX + offsetX; 1313 touchXy[1] = originY + offsetY; 1314 1315 // Calculate the distance between the center of the CellLayout 1316 // and the touch point 1317 float dist = squaredDistance(touchXy, cellLayoutCenter); 1318 1319 if (dist < smallestDistSoFar) { 1320 smallestDistSoFar = dist; 1321 bestMatchingScreen = cl; 1322 } 1323 } 1324 } 1325 return bestMatchingScreen; 1326 } 1327 1328 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1329 DragView dragView, Object dragInfo) { 1330 // When touch is inside the scroll area, skip dragOver actions for the current screen 1331 if (!mInScrollArea) { 1332 CellLayout layout; 1333 int originX = x - xOffset; 1334 int originY = y - yOffset; 1335 if (mIsSmall || mIsInUnshrinkAnimation) { 1336 layout = findMatchingPageForDragOver( 1337 dragView, originX, originY, xOffset, yOffset); 1338 1339 if (layout != mDragTargetLayout) { 1340 if (mDragTargetLayout != null) { 1341 mDragTargetLayout.setHover(false); 1342 } 1343 mDragTargetLayout = layout; 1344 if (mDragTargetLayout != null) { 1345 mDragTargetLayout.setHover(true); 1346 } 1347 } 1348 } else { 1349 layout = getCurrentDropLayout(); 1350 1351 final ItemInfo item = (ItemInfo)dragInfo; 1352 if (dragInfo instanceof LauncherAppWidgetInfo) { 1353 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1354 1355 if (widgetInfo.spanX == -1) { 1356 // Calculate the grid spans needed to fit this widget 1357 int[] spans = layout.rectToCell( 1358 widgetInfo.minWidth, widgetInfo.minHeight, null); 1359 item.spanX = spans[0]; 1360 item.spanY = spans[1]; 1361 } 1362 } 1363 1364 if (source instanceof AllAppsPagedView) { 1365 // This is a hack to fix the point used to determine which cell an icon from 1366 // the all apps screen is over 1367 if (item != null && item.spanX == 1 && layout != null) { 1368 int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2; 1369 1370 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1371 if (dragView.getDragRegionWidth() != layout.getCellWidth()) { 1372 dragView.setDragRegion(dragView.getDragRegionLeft(), 1373 dragView.getDragRegionTop(), 1374 layout.getCellWidth(), 1375 dragView.getDragRegionHeight()); 1376 } 1377 } 1378 } 1379 1380 if (layout != mDragTargetLayout) { 1381 if (mDragTargetLayout != null) { 1382 mDragTargetLayout.onDragExit(); 1383 } 1384 layout.onDragEnter(dragView); 1385 mDragTargetLayout = layout; 1386 } 1387 1388 // only visualize the drop locations for moving icons within the home screen on 1389 // tablet on phone, we also visualize icons dragged in from All Apps 1390 if ((!LauncherApplication.isScreenXLarge() || source == this) 1391 && mDragTargetLayout != null) { 1392 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1393 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 1394 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 1395 mDragTargetLayout.visualizeDropLocation(child, mDragOutline, 1396 localOriginX, localOriginY, item.spanX, item.spanY); 1397 } 1398 } 1399 } 1400 } 1401 1402 public void onDragExit(DragSource source, int x, int y, int xOffset, 1403 int yOffset, DragView dragView, Object dragInfo) { 1404 if (mDragTargetLayout != null) { 1405 mDragTargetLayout.onDragExit(); 1406 } 1407 if (!mIsPageMoving) { 1408 hideOutlines(); 1409 } 1410 clearAllHovers(); 1411 } 1412 1413 private void onDropExternal(int x, int y, Object dragInfo, 1414 CellLayout cellLayout) { 1415 onDropExternal(x, y, dragInfo, cellLayout, false); 1416 } 1417 1418 /** 1419 * Add the item specified by dragInfo to the given layout. 1420 * This is basically the equivalent of onDropExternal, except it's not initiated 1421 * by drag and drop. 1422 * @return true if successful 1423 */ 1424 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1425 CellLayout cl = (CellLayout) layout; 1426 ItemInfo info = (ItemInfo) dragInfo; 1427 1428 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1429 onDropExternal(0, 0, dragInfo, cl, false); 1430 return true; 1431 } 1432 mLauncher.showOutOfSpaceMessage(); 1433 return false; 1434 } 1435 1436 // Drag from somewhere else 1437 // NOTE: This can also be called when we are outside of a drag event, when we want 1438 // to add an item to one of the workspace screens. 1439 private void onDropExternal(int x, int y, Object dragInfo, 1440 CellLayout cellLayout, boolean insertAtFirst) { 1441 int screen = indexOfChild(cellLayout); 1442 if (dragInfo instanceof PendingAddItemInfo) { 1443 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1444 // When dragging and dropping from customization tray, we deal with creating 1445 // widgets/shortcuts/folders in a slightly different way 1446 int[] touchXY = new int[2]; 1447 touchXY[0] = x; 1448 touchXY[1] = y; 1449 switch (info.itemType) { 1450 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1451 mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); 1452 break; 1453 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1454 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 1455 break; 1456 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1457 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 1458 break; 1459 default: 1460 throw new IllegalStateException("Unknown item type: " + info.itemType); 1461 } 1462 cellLayout.onDragExit(); 1463 cellLayout.animateDrop(); 1464 return; 1465 } 1466 1467 // This is for other drag/drop cases, like dragging from All Apps 1468 ItemInfo info = (ItemInfo) dragInfo; 1469 1470 View view = null; 1471 1472 switch (info.itemType) { 1473 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1474 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1475 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1476 // Came from all apps -- make a copy 1477 info = new ShortcutInfo((ApplicationInfo) info); 1478 } 1479 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1480 (ShortcutInfo) info); 1481 break; 1482 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1483 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1484 cellLayout, ((UserFolderInfo) info)); 1485 break; 1486 default: 1487 throw new IllegalStateException("Unknown item type: " + info.itemType); 1488 } 1489 1490 // If the view is null, it has already been added. 1491 if (view == null) { 1492 cellLayout.onDragExit(); 1493 } else { 1494 mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell); 1495 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1496 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1497 cellLayout.onDropChild(view); 1498 cellLayout.animateDrop(); 1499 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1500 1501 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1502 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1503 lp.cellX, lp.cellY); 1504 } 1505 } 1506 1507 /** 1508 * Return the current {@link CellLayout}, correctly picking the destination 1509 * screen while a scroll is in progress. 1510 */ 1511 private CellLayout getCurrentDropLayout() { 1512 // if we're currently small, use findMatchingPageForDragOver instead 1513 if (mIsSmall) return null; 1514 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 1515 return (CellLayout) getChildAt(index); 1516 } 1517 1518 /** 1519 * Return the current CellInfo describing our current drag; this method exists 1520 * so that Launcher can sync this object with the correct info when the activity is created/ 1521 * destroyed 1522 * 1523 */ 1524 public CellLayout.CellInfo getDragInfo() { 1525 return mDragInfo; 1526 } 1527 1528 /** 1529 * {@inheritDoc} 1530 */ 1531 public boolean acceptDrop(DragSource source, int x, int y, 1532 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1533 if (mDragTargetLayout == null) { 1534 // cancel the drag if we're not over a screen at time of drop 1535 return false; 1536 } 1537 1538 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1539 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1540 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1541 1542 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1543 1544 if (mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1545 return true; 1546 } else { 1547 mLauncher.showOutOfSpaceMessage(); 1548 return false; 1549 } 1550 } 1551 1552 /** 1553 * Calculate the nearest cell where the given object would be dropped. 1554 */ 1555 private int[] findNearestVacantArea(int pixelX, int pixelY, 1556 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1557 1558 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 1559 int localPixelY = pixelY - (layout.getTop() - mScrollY); 1560 1561 // Find the best target drop location 1562 return layout.findNearestVacantArea( 1563 localPixelX, localPixelY, spanX, spanY, ignoreView, recycle); 1564 } 1565 1566 /** 1567 * Estimate the size that a child with the given dimensions will take in the current screen. 1568 */ 1569 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1570 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 1571 } 1572 1573 void setLauncher(Launcher launcher) { 1574 mLauncher = launcher; 1575 } 1576 1577 public void setDragController(DragController dragController) { 1578 mDragController = dragController; 1579 } 1580 1581 public void onDropCompleted(View target, boolean success) { 1582 if (success) { 1583 if (target != this && mDragInfo != null) { 1584 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1585 cellLayout.removeView(mDragInfo.cell); 1586 if (mDragInfo.cell instanceof DropTarget) { 1587 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1588 } 1589 // final Object tag = mDragInfo.cell.getTag(); 1590 } 1591 } else if (mDragInfo != null) { 1592 ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell); 1593 } 1594 1595 mDragOutline = null; 1596 mDragInfo = null; 1597 } 1598 1599 public boolean isDropEnabled() { 1600 return true; 1601 } 1602 1603 @Override 1604 protected void onRestoreInstanceState(Parcelable state) { 1605 super.onRestoreInstanceState(state); 1606 Launcher.setScreen(mCurrentPage); 1607 } 1608 1609 @Override 1610 public void scrollLeft() { 1611 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1612 super.scrollLeft(); 1613 } 1614 } 1615 1616 @Override 1617 public void scrollRight() { 1618 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1619 super.scrollRight(); 1620 } 1621 } 1622 1623 @Override 1624 public void onEnterScrollArea(int direction) { 1625 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1626 mInScrollArea = true; 1627 final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1); 1628 if (0 <= screen && screen < getChildCount()) { 1629 ((CellLayout) getChildAt(screen)).setHover(true); 1630 } 1631 1632 if (mDragTargetLayout != null) { 1633 mDragTargetLayout.onDragExit(); 1634 mDragTargetLayout = null; 1635 } 1636 } 1637 } 1638 1639 private void clearAllHovers() { 1640 final int childCount = getChildCount(); 1641 for (int i = 0; i < childCount; i++) { 1642 ((CellLayout) getChildAt(i)).setHover(false); 1643 } 1644 } 1645 1646 @Override 1647 public void onExitScrollArea() { 1648 if (mInScrollArea) { 1649 mInScrollArea = false; 1650 clearAllHovers(); 1651 } 1652 } 1653 1654 public Folder getFolderForTag(Object tag) { 1655 final int screenCount = getChildCount(); 1656 for (int screen = 0; screen < screenCount; screen++) { 1657 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1658 int count = currentScreen.getChildCount(); 1659 for (int i = 0; i < count; i++) { 1660 View child = currentScreen.getChildAt(i); 1661 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1662 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1663 Folder f = (Folder) child; 1664 if (f.getInfo() == tag && f.getInfo().opened) { 1665 return f; 1666 } 1667 } 1668 } 1669 } 1670 return null; 1671 } 1672 1673 public View getViewForTag(Object tag) { 1674 int screenCount = getChildCount(); 1675 for (int screen = 0; screen < screenCount; screen++) { 1676 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1677 int count = currentScreen.getChildCount(); 1678 for (int i = 0; i < count; i++) { 1679 View child = currentScreen.getChildAt(i); 1680 if (child.getTag() == tag) { 1681 return child; 1682 } 1683 } 1684 } 1685 return null; 1686 } 1687 1688 1689 void removeItems(final ArrayList<ApplicationInfo> apps) { 1690 final int screenCount = getChildCount(); 1691 final PackageManager manager = getContext().getPackageManager(); 1692 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1693 1694 final HashSet<String> packageNames = new HashSet<String>(); 1695 final int appCount = apps.size(); 1696 for (int i = 0; i < appCount; i++) { 1697 packageNames.add(apps.get(i).componentName.getPackageName()); 1698 } 1699 1700 for (int i = 0; i < screenCount; i++) { 1701 final CellLayout layout = (CellLayout) getChildAt(i); 1702 1703 // Avoid ANRs by treating each screen separately 1704 post(new Runnable() { 1705 public void run() { 1706 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1707 childrenToRemove.clear(); 1708 1709 int childCount = layout.getChildCount(); 1710 for (int j = 0; j < childCount; j++) { 1711 final View view = layout.getChildAt(j); 1712 Object tag = view.getTag(); 1713 1714 if (tag instanceof ShortcutInfo) { 1715 final ShortcutInfo info = (ShortcutInfo) tag; 1716 final Intent intent = info.intent; 1717 final ComponentName name = intent.getComponent(); 1718 1719 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1720 for (String packageName: packageNames) { 1721 if (packageName.equals(name.getPackageName())) { 1722 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1723 childrenToRemove.add(view); 1724 } 1725 } 1726 } 1727 } else if (tag instanceof UserFolderInfo) { 1728 final UserFolderInfo info = (UserFolderInfo) tag; 1729 final ArrayList<ShortcutInfo> contents = info.contents; 1730 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1731 final int contentsCount = contents.size(); 1732 boolean removedFromFolder = false; 1733 1734 for (int k = 0; k < contentsCount; k++) { 1735 final ShortcutInfo appInfo = contents.get(k); 1736 final Intent intent = appInfo.intent; 1737 final ComponentName name = intent.getComponent(); 1738 1739 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1740 for (String packageName: packageNames) { 1741 if (packageName.equals(name.getPackageName())) { 1742 toRemove.add(appInfo); 1743 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1744 removedFromFolder = true; 1745 } 1746 } 1747 } 1748 } 1749 1750 contents.removeAll(toRemove); 1751 if (removedFromFolder) { 1752 final Folder folder = getOpenFolder(); 1753 if (folder != null) 1754 folder.notifyDataSetChanged(); 1755 } 1756 } else if (tag instanceof LiveFolderInfo) { 1757 final LiveFolderInfo info = (LiveFolderInfo) tag; 1758 final Uri uri = info.uri; 1759 final ProviderInfo providerInfo = manager.resolveContentProvider( 1760 uri.getAuthority(), 0); 1761 1762 if (providerInfo != null) { 1763 for (String packageName: packageNames) { 1764 if (packageName.equals(providerInfo.packageName)) { 1765 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1766 childrenToRemove.add(view); 1767 } 1768 } 1769 } 1770 } else if (tag instanceof LauncherAppWidgetInfo) { 1771 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1772 final AppWidgetProviderInfo provider = 1773 widgets.getAppWidgetInfo(info.appWidgetId); 1774 if (provider != null) { 1775 for (String packageName: packageNames) { 1776 if (packageName.equals(provider.provider.getPackageName())) { 1777 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1778 childrenToRemove.add(view); 1779 } 1780 } 1781 } 1782 } 1783 } 1784 1785 childCount = childrenToRemove.size(); 1786 for (int j = 0; j < childCount; j++) { 1787 View child = childrenToRemove.get(j); 1788 layout.removeViewInLayout(child); 1789 if (child instanceof DropTarget) { 1790 mDragController.removeDropTarget((DropTarget)child); 1791 } 1792 } 1793 1794 if (childCount > 0) { 1795 layout.requestLayout(); 1796 layout.invalidate(); 1797 } 1798 } 1799 }); 1800 } 1801 } 1802 1803 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1804 final int screenCount = getChildCount(); 1805 for (int i = 0; i < screenCount; i++) { 1806 final CellLayout layout = (CellLayout) getChildAt(i); 1807 int childCount = layout.getChildCount(); 1808 for (int j = 0; j < childCount; j++) { 1809 final View view = layout.getChildAt(j); 1810 Object tag = view.getTag(); 1811 if (tag instanceof ShortcutInfo) { 1812 ShortcutInfo info = (ShortcutInfo)tag; 1813 // We need to check for ACTION_MAIN otherwise getComponent() might 1814 // return null for some shortcuts (for instance, for shortcuts to 1815 // web pages.) 1816 final Intent intent = info.intent; 1817 final ComponentName name = intent.getComponent(); 1818 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1819 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1820 final int appCount = apps.size(); 1821 for (int k = 0; k < appCount; k++) { 1822 ApplicationInfo app = apps.get(k); 1823 if (app.componentName.equals(name)) { 1824 info.setIcon(mIconCache.getIcon(info.intent)); 1825 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1826 new FastBitmapDrawable(info.getIcon(mIconCache)), 1827 null, null); 1828 } 1829 } 1830 } 1831 } 1832 } 1833 } 1834 } 1835 1836 void moveToDefaultScreen(boolean animate) { 1837 if (mIsSmall || mIsInUnshrinkAnimation) { 1838 mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage)); 1839 } else if (animate) { 1840 snapToPage(mDefaultPage); 1841 } else { 1842 setCurrentPage(mDefaultPage); 1843 } 1844 getChildAt(mDefaultPage).requestFocus(); 1845 } 1846 1847 void setIndicators(Drawable previous, Drawable next) { 1848 mPreviousIndicator = previous; 1849 mNextIndicator = next; 1850 previous.setLevel(mCurrentPage); 1851 next.setLevel(mCurrentPage); 1852 } 1853 1854 @Override 1855 public void syncPages() { 1856 } 1857 1858 @Override 1859 public void syncPageItems(int page) { 1860 } 1861 1862} 1863