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