Workspace.java revision 6a1435d78d5133b1f37274c4d358bf6d22e10229
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.AnimatorSet; 23import android.animation.ObjectAnimator; 24import android.animation.PropertyValuesHolder; 25import android.animation.Animator.AnimatorListener; 26import android.app.WallpaperManager; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProviderInfo; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.pm.PackageManager; 33import android.content.pm.ProviderInfo; 34import android.content.res.Resources; 35import android.content.res.TypedArray; 36import android.graphics.Canvas; 37import android.graphics.Matrix; 38import android.graphics.Rect; 39import android.graphics.drawable.Drawable; 40import android.net.Uri; 41import android.os.IBinder; 42import android.os.Parcelable; 43import android.util.AttributeSet; 44import android.util.Log; 45import android.view.MotionEvent; 46import android.view.View; 47import android.view.ViewGroup; 48import android.widget.TextView; 49 50import java.util.ArrayList; 51import java.util.HashSet; 52 53/** 54 * The workspace is a wide area with a wallpaper and a finite number of pages. 55 * Each page contains a number of icons, folders or widgets the user can 56 * interact with. A workspace is meant to be used with a fixed width only. 57 */ 58public class Workspace extends SmoothPagedView 59 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 60 @SuppressWarnings({"UnusedDeclaration"}) 61 private static final String TAG = "Launcher.Workspace"; 62 63 // This is how much the workspace shrinks when we enter all apps or 64 // customization mode 65 private static final float SHRINK_FACTOR = 0.16f; 66 67 // The maximum Y rotation to apply to the mini home screens 68 private static final float MINI_PAGE_MAX_ROTATION = 25.0f; 69 70 // These are extra scale factors to apply to the mini home screens 71 // so as to achieve the desired transform 72 private static final float EXTRA_SCALE_FACTOR_0 = 0.97f; 73 private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; 74 private static final float EXTRA_SCALE_FACTOR_2 = 1.08f; 75 76 private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM }; 77 78 private final WallpaperManager mWallpaperManager; 79 80 private int mDefaultPage; 81 82 private boolean mWaitingToShrinkToBottom = false; 83 84 /** 85 * CellInfo for the cell that is currently being dragged 86 */ 87 private CellLayout.CellInfo mDragInfo; 88 89 /** 90 * Target drop area calculated during last acceptDrop call. 91 */ 92 private int[] mTargetCell = null; 93 94 /** 95 * The CellLayout that is currently being dragged over 96 */ 97 private CellLayout mDragTargetLayout = null; 98 99 private Launcher mLauncher; 100 private IconCache mIconCache; 101 private DragController mDragController; 102 103 private int[] mTempCell = new int[2]; 104 private int[] mTempEstimate = new int[2]; 105 private float[] mTempOriginXY = new float[2]; 106 private float[] mTempDragCoordinates = new float[2]; 107 private float[] mTempDragBottomRightCoordinates = new float[2]; 108 private Matrix mTempInverseMatrix = new Matrix(); 109 110 private static final int DEFAULT_CELL_COUNT_X = 4; 111 private static final int DEFAULT_CELL_COUNT_Y = 4; 112 113 private Drawable mPreviousIndicator; 114 private Drawable mNextIndicator; 115 116 // State variable that indicated whether the pages are small (ie when you're 117 // in all apps or customize mode) 118 private boolean mIsSmall; 119 private AnimatorListener mUnshrinkAnimationListener; 120 121 /** 122 * Used to inflate the Workspace from XML. 123 * 124 * @param context The application's context. 125 * @param attrs The attributes set containing the Workspace's customization values. 126 */ 127 public Workspace(Context context, AttributeSet attrs) { 128 this(context, attrs, 0); 129 } 130 131 /** 132 * Used to inflate the Workspace from XML. 133 * 134 * @param context The application's context. 135 * @param attrs The attributes set containing the Workspace's customization values. 136 * @param defStyle Unused. 137 */ 138 public Workspace(Context context, AttributeSet attrs, int defStyle) { 139 super(context, attrs, defStyle); 140 mContentIsRefreshable = false; 141 142 if (!LauncherApplication.isScreenXLarge()) { 143 mFadeInAdjacentScreens = false; 144 } 145 146 mWallpaperManager = WallpaperManager.getInstance(context); 147 148 TypedArray a = context.obtainStyledAttributes(attrs, 149 R.styleable.Workspace, defStyle, 0); 150 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 151 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 152 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); 153 a.recycle(); 154 155 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 156 setHapticFeedbackEnabled(false); 157 158 initWorkspace(); 159 } 160 161 /** 162 * Initializes various states for this workspace. 163 */ 164 protected void initWorkspace() { 165 Context context = getContext(); 166 mCurrentPage = mDefaultPage; 167 Launcher.setScreen(mCurrentPage); 168 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 169 mIconCache = app.getIconCache(); 170 171 mUnshrinkAnimationListener = new AnimatorListener() { 172 public void onAnimationStart(Animator animation) {} 173 public void onAnimationEnd(Animator animation) { 174 mIsSmall = false; 175 } 176 public void onAnimationCancel(Animator animation) {} 177 public void onAnimationRepeat(Animator animation) {} 178 }; 179 180 mSnapVelocity = 600; 181 } 182 183 @Override 184 public void addView(View child, int index, LayoutParams params) { 185 if (!(child instanceof CellLayout)) { 186 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 187 } 188 super.addView(child, index, params); 189 } 190 191 @Override 192 public void addView(View child) { 193 if (!(child instanceof CellLayout)) { 194 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 195 } 196 super.addView(child); 197 } 198 199 @Override 200 public void addView(View child, int index) { 201 if (!(child instanceof CellLayout)) { 202 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 203 } 204 super.addView(child, index); 205 } 206 207 @Override 208 public void addView(View child, int width, int height) { 209 if (!(child instanceof CellLayout)) { 210 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 211 } 212 super.addView(child, width, height); 213 } 214 215 @Override 216 public void addView(View child, LayoutParams params) { 217 if (!(child instanceof CellLayout)) { 218 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 219 } 220 super.addView(child, params); 221 } 222 223 /** 224 * @return The open folder on the current screen, or null if there is none 225 */ 226 Folder getOpenFolder() { 227 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 228 int count = currentPage.getChildCount(); 229 for (int i = 0; i < count; i++) { 230 View child = currentPage.getChildAt(i); 231 if (child instanceof Folder) { 232 Folder folder = (Folder) child; 233 if (folder.getInfo().opened) 234 return folder; 235 } 236 } 237 return null; 238 } 239 240 ArrayList<Folder> getOpenFolders() { 241 final int screenCount = getChildCount(); 242 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 243 244 for (int screen = 0; screen < screenCount; screen++) { 245 CellLayout currentPage = (CellLayout) getChildAt(screen); 246 int count = currentPage.getChildCount(); 247 for (int i = 0; i < count; i++) { 248 View child = currentPage.getChildAt(i); 249 if (child instanceof Folder) { 250 Folder folder = (Folder) child; 251 if (folder.getInfo().opened) 252 folders.add(folder); 253 break; 254 } 255 } 256 } 257 258 return folders; 259 } 260 261 boolean isDefaultPageShowing() { 262 return mCurrentPage == mDefaultPage; 263 } 264 265 /** 266 * Sets the current screen. 267 * 268 * @param currentPage 269 */ 270 @Override 271 void setCurrentPage(int currentPage) { 272 super.setCurrentPage(currentPage); 273 updateWallpaperOffset(mScrollX); 274 } 275 276 /** 277 * Adds the specified child in the specified screen. The position and dimension of 278 * the child are defined by x, y, spanX and spanY. 279 * 280 * @param child The child to add in one of the workspace's screens. 281 * @param screen The screen in which to add the child. 282 * @param x The X position of the child in the screen's grid. 283 * @param y The Y position of the child in the screen's grid. 284 * @param spanX The number of cells spanned horizontally by the child. 285 * @param spanY The number of cells spanned vertically by the child. 286 */ 287 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 288 addInScreen(child, screen, x, y, spanX, spanY, false); 289 } 290 291 void addInFullScreen(View child, int screen) { 292 addInScreen(child, screen, 0, 0, -1, -1); 293 } 294 295 /** 296 * Adds the specified child in the specified screen. The position and dimension of 297 * the child are defined by x, y, spanX and spanY. 298 * 299 * @param child The child to add in one of the workspace's screens. 300 * @param screen The screen in which to add the child. 301 * @param x The X position of the child in the screen's grid. 302 * @param y The Y position of the child in the screen's grid. 303 * @param spanX The number of cells spanned horizontally by the child. 304 * @param spanY The number of cells spanned vertically by the child. 305 * @param insert When true, the child is inserted at the beginning of the children list. 306 */ 307 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 308 if (screen < 0 || screen >= getChildCount()) { 309 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 310 + " (was " + screen + "); skipping child"); 311 return; 312 } 313 314 final CellLayout group = (CellLayout) getChildAt(screen); 315 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 316 if (lp == null) { 317 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 318 } else { 319 lp.cellX = x; 320 lp.cellY = y; 321 lp.cellHSpan = spanX; 322 lp.cellVSpan = spanY; 323 } 324 325 // Get the canonical child id to uniquely represent this view in this screen 326 int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); 327 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { 328 // TODO: This branch occurs when the workspace is adding views 329 // outside of the defined grid 330 // maybe we should be deleting these items from the LauncherModel? 331 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 332 } 333 334 if (!(child instanceof Folder)) { 335 child.setHapticFeedbackEnabled(false); 336 child.setOnLongClickListener(mLongClickListener); 337 } 338 if (child instanceof DropTarget) { 339 mDragController.addDropTarget((DropTarget) child); 340 } 341 } 342 343 public boolean onTouch(View v, MotionEvent event) { 344 // this is an intercepted event being forwarded from a cell layout 345 if (mIsSmall) { 346 mLauncher.onWorkspaceClick((CellLayout) v); 347 return true; 348 } 349 return false; 350 } 351 352 @Override 353 public boolean dispatchUnhandledMove(View focused, int direction) { 354 if (mIsSmall) { 355 // when the home screens are shrunken, shouldn't allow side-scrolling 356 return false; 357 } 358 return super.dispatchUnhandledMove(focused, direction); 359 } 360 361 @Override 362 public boolean onInterceptTouchEvent(MotionEvent ev) { 363 if (mIsSmall) { 364 // when the home screens are shrunken, shouldn't allow side-scrolling 365 return false; 366 } 367 return super.onInterceptTouchEvent(ev); 368 } 369 370 protected void pageBeginMoving() { 371 if (mNextPage != INVALID_PAGE) { 372 // we're snapping to a particular screen 373 // there's an issue where the alpha of neighboring pages doesn't get updated 374 // if drawing cache is enabled on children-- we only use that on xlarge devices, 375 // so disable drawing cache in those cases 376 if (!LauncherApplication.isScreenXLarge()) { 377 enableChildrenCache(mCurrentPage, mNextPage); 378 } 379 } else { 380 // this is when user is actively dragging a particular screen, they might 381 // swipe it either left or right (but we won't advance by more than one screen) 382 if (!LauncherApplication.isScreenXLarge()) { 383 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); 384 } 385 } 386 } 387 388 protected void pageEndMoving() { 389 if (!LauncherApplication.isScreenXLarge()) { 390 clearChildrenCache(); 391 } 392 } 393 394 @Override 395 protected void notifyPageSwitchListener() { 396 super.notifyPageSwitchListener(); 397 398 if (mPreviousIndicator != null) { 399 // if we know the next page, we show the indication for it right away; it looks 400 // weird if the indicators are lagging 401 int page = mNextPage; 402 if (page == INVALID_PAGE) { 403 page = mCurrentPage; 404 } 405 mPreviousIndicator.setLevel(page); 406 mNextIndicator.setLevel(page); 407 } 408 Launcher.setScreen(mCurrentPage); 409 }; 410 411 private void updateWallpaperOffset() { 412 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 413 } 414 415 private void updateWallpaperOffset(int scrollRange) { 416 final boolean isStaticWallpaper = (mWallpaperManager != null) && 417 (mWallpaperManager.getWallpaperInfo() == null); 418 if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { 419 IBinder token = getWindowToken(); 420 if (token != null) { 421 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 422 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 423 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 424 } 425 } 426 } 427 428 protected void onAttachedToWindow() { 429 super.onAttachedToWindow(); 430 computeScroll(); 431 mDragController.setWindowToken(getWindowToken()); 432 } 433 434 @Override 435 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 436 super.onLayout(changed, left, top, right, bottom); 437 438 // if shrinkToBottom() is called on initialization, it has to be deferred 439 // until after the first call to onLayout so that it has the correct width 440 if (mWaitingToShrinkToBottom) { 441 shrinkToBottom(false); 442 mWaitingToShrinkToBottom = false; 443 } 444 445 if (LauncherApplication.isInPlaceRotationEnabled()) { 446 // When the device is rotated, the scroll position of the current screen 447 // needs to be refreshed 448 setCurrentPage(getCurrentPage()); 449 } 450 } 451 452 @Override 453 protected void dispatchDraw(Canvas canvas) { 454 if (mIsSmall) { 455 // Draw all the workspaces if we're small 456 final int pageCount = getChildCount(); 457 final long drawingTime = getDrawingTime(); 458 for (int i = 0; i < pageCount; i++) { 459 final View page = (View) getChildAt(i); 460 461 drawChild(canvas, page, drawingTime); 462 } 463 } else { 464 super.dispatchDraw(canvas); 465 } 466 } 467 468 @Override 469 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 470 if (!mLauncher.isAllAppsVisible()) { 471 final Folder openFolder = getOpenFolder(); 472 if (openFolder != null) { 473 return openFolder.requestFocus(direction, previouslyFocusedRect); 474 } else { 475 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 476 } 477 } 478 return false; 479 } 480 481 @Override 482 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 483 if (!mLauncher.isAllAppsVisible()) { 484 final Folder openFolder = getOpenFolder(); 485 if (openFolder != null) { 486 openFolder.addFocusables(views, direction); 487 } else { 488 super.addFocusables(views, direction, focusableMode); 489 } 490 } 491 } 492 493 @Override 494 public boolean dispatchTouchEvent(MotionEvent ev) { 495 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 496 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 497 // ie when you click on a mini-screen, it zooms back to that screen) 498 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 499 return false; 500 } 501 } 502 return super.dispatchTouchEvent(ev); 503 } 504 505 void enableChildrenCache(int fromPage, int toPage) { 506 if (fromPage > toPage) { 507 final int temp = fromPage; 508 fromPage = toPage; 509 toPage = temp; 510 } 511 512 final int screenCount = getChildCount(); 513 514 fromPage = Math.max(fromPage, 0); 515 toPage = Math.min(toPage, screenCount - 1); 516 517 for (int i = fromPage; i <= toPage; i++) { 518 final CellLayout layout = (CellLayout) getChildAt(i); 519 layout.setChildrenDrawnWithCacheEnabled(true); 520 layout.setChildrenDrawingCacheEnabled(true); 521 } 522 } 523 524 void clearChildrenCache() { 525 final int screenCount = getChildCount(); 526 for (int i = 0; i < screenCount; i++) { 527 final CellLayout layout = (CellLayout) getChildAt(i); 528 layout.setChildrenDrawnWithCacheEnabled(false); 529 } 530 } 531 532 @Override 533 public boolean onTouchEvent(MotionEvent ev) { 534 if (mLauncher.isAllAppsVisible()) { 535 // Cancel any scrolling that is in progress. 536 if (!mScroller.isFinished()) { 537 mScroller.abortAnimation(); 538 } 539 snapToPage(mCurrentPage); 540 return false; // We don't want the events. Let them fall through to the all apps view. 541 } 542 543 return super.onTouchEvent(ev); 544 } 545 546 public boolean isSmall() { 547 return mIsSmall; 548 } 549 550 void shrinkToTop(boolean animated) { 551 shrink(ShrinkPosition.SHRINK_TO_TOP, animated); 552 } 553 554 void shrinkToMiddle() { 555 shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); 556 } 557 558 void shrinkToBottom() { 559 shrinkToBottom(true); 560 } 561 562 void shrinkToBottom(boolean animated) { 563 if (mFirstLayout) { 564 // (mFirstLayout == "first layout has not happened yet") 565 // if we get a call to shrink() as part of our initialization (for example, if 566 // Launcher is started in All Apps mode) then we need to wait for a layout call 567 // to get our width so we can layout the mini-screen views correctly 568 mWaitingToShrinkToBottom = true; 569 } else { 570 shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated); 571 } 572 } 573 574 private float getYScaleForScreen(int screen) { 575 int x = Math.abs(screen - 2); 576 577 // TODO: This should be generalized for use with arbitrary rotation angles. 578 switch(x) { 579 case 0: return EXTRA_SCALE_FACTOR_0; 580 case 1: return EXTRA_SCALE_FACTOR_1; 581 case 2: return EXTRA_SCALE_FACTOR_2; 582 } 583 return 1.0f; 584 } 585 586 // we use this to shrink the workspace for the all apps view and the customize view 587 private void shrink(ShrinkPosition shrinkPosition, boolean animated) { 588 mIsSmall = true; 589 // we intercept and reject all touch events when we're small, so be sure to reset the state 590 mTouchState = TOUCH_STATE_REST; 591 mActivePointerId = INVALID_POINTER; 592 593 final Resources res = getResources(); 594 final int screenWidth = getWidth(); 595 final int screenHeight = getHeight(); 596 597 // Making the assumption that all pages have the same width as the 0th 598 final int pageWidth = getChildAt(0).getMeasuredWidth(); 599 final int pageHeight = getChildAt(0).getMeasuredHeight(); 600 601 final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); 602 final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); 603 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); 604 605 final int screenCount = getChildCount(); 606 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; 607 608 float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); 609 if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) { 610 newY = screenHeight - newY - scaledPageHeight; 611 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { 612 newY = screenHeight / 2 - scaledPageHeight / 2; 613 } 614 615 // We animate all the screens to the centered position in workspace 616 // At the same time, the screens become greyed/dimmed 617 618 // newX is initialized to the left-most position of the centered screens 619 float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; 620 621 // We are going to scale about the center of the view, so we need to adjust the positions 622 // of the views accordingly 623 newX -= (pageWidth - scaledPageWidth) / 2.0f; 624 newY -= (pageHeight - scaledPageHeight) / 2.0f; 625 for (int i = 0; i < screenCount; i++) { 626 CellLayout cl = (CellLayout) getChildAt(i); 627 628 float rotation = (-i + 2) * MINI_PAGE_MAX_ROTATION / 2.0f; 629 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); 630 float rotationScaleY = getYScaleForScreen(i); 631 632 if (animated) { 633 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 634 new ObjectAnimator<Float>(duration, cl, 635 new PropertyValuesHolder<Float>("x", newX), 636 new PropertyValuesHolder<Float>("y", newY), 637 new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX), 638 new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY), 639 new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f), 640 new PropertyValuesHolder<Float>("alpha", 0.0f), 641 new PropertyValuesHolder<Float>("rotationY", rotation)).start(); 642 } else { 643 cl.setX((int)newX); 644 cl.setY((int)newY); 645 cl.setScaleX(SHRINK_FACTOR); 646 cl.setScaleY(SHRINK_FACTOR); 647 cl.setBackgroundAlpha(1.0f); 648 cl.setAlpha(0.0f); 649 cl.setRotationY(rotation); 650 } 651 // increment newX for the next screen 652 newX += scaledPageWidth + extraScaledSpacing; 653 cl.setOnInterceptTouchListener(this); 654 } 655 setChildrenDrawnWithCacheEnabled(true); 656 } 657 658 // We call this when we trigger an unshrink by clicking on the CellLayout cl 659 public void unshrink(CellLayout clThatWasClicked) { 660 int newCurrentPage = mCurrentPage; 661 final int screenCount = getChildCount(); 662 for (int i = 0; i < screenCount; i++) { 663 if (getChildAt(i) == clThatWasClicked) { 664 newCurrentPage = i; 665 } 666 } 667 unshrink(newCurrentPage); 668 } 669 670 private void unshrink(int newCurrentPage) { 671 if (mIsSmall) { 672 int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 673 int delta = newX - mScrollX; 674 675 final int screenCount = getChildCount(); 676 for (int i = 0; i < screenCount; i++) { 677 CellLayout cl = (CellLayout) getChildAt(i); 678 cl.setX(cl.getX() + delta); 679 } 680 snapToPage(newCurrentPage); 681 unshrink(); 682 683 setCurrentPage(newCurrentPage); 684 } 685 } 686 687 void unshrink() { 688 unshrink(true); 689 } 690 691 void unshrink(boolean animated) { 692 if (mIsSmall) { 693 AnimatorSet s = new AnimatorSet(); 694 final int screenCount = getChildCount(); 695 696 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 697 for (int i = 0; i < screenCount; i++) { 698 final CellLayout cl = (CellLayout)getChildAt(i); 699 float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; 700 if (animated) { 701 s.playTogether( 702 new ObjectAnimator<Float>(duration, cl, "translationX", 0.0f), 703 new ObjectAnimator<Float>(duration, cl, "translationY", 0.0f), 704 new ObjectAnimator<Float>(duration, cl, "scaleX", 1.0f), 705 new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f), 706 new ObjectAnimator<Float>(duration, cl, "backgroundAlpha", 0.0f), 707 new ObjectAnimator<Float>(duration, cl, "alpha", finalAlphaValue), 708 new ObjectAnimator<Float>(duration, cl, "rotationY", 0.0f)); 709 } else { 710 cl.setTranslationX(0.0f); 711 cl.setTranslationY(0.0f); 712 cl.setScaleX(1.0f); 713 cl.setScaleY(1.0f); 714 cl.setBackgroundAlpha(0.0f); 715 cl.setAlpha(1.0f); 716 cl.setRotationY(0.0f); 717 } 718 } 719 s.addListener(mUnshrinkAnimationListener); 720 s.start(); 721 } 722 } 723 724 void startDrag(CellLayout.CellInfo cellInfo) { 725 View child = cellInfo.cell; 726 727 // Make sure the drag was started by a long press as opposed to a long click. 728 if (!child.isInTouchMode()) { 729 return; 730 } 731 732 mDragInfo = cellInfo; 733 mDragInfo.screen = mCurrentPage; 734 735 CellLayout current = ((CellLayout) getChildAt(mCurrentPage)); 736 737 current.onDragChild(child); 738 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 739 invalidate(); 740 } 741 742 void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, 743 boolean insertAtFirst, int intersectX, int intersectY) { 744 final CellLayout cellLayout = (CellLayout) getChildAt(screen); 745 View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); 746 747 final int[] cellXY = new int[2]; 748 cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 749 addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 750 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 751 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 752 cellXY[0], cellXY[1]); 753 } 754 755 756 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 757 DragView dragView, Object dragInfo) { 758 CellLayout cellLayout; 759 int originX = x - xOffset; 760 int originY = y - yOffset; 761 if (mIsSmall) { 762 cellLayout = findMatchingPageForDragOver(dragView, originX, originY); 763 if (cellLayout == null) { 764 // cancel the drag if we're not over a mini-screen at time of drop 765 // TODO: maybe add a nice fade here? 766 return; 767 } 768 // get originX and originY in the local coordinate system of the screen 769 mTempOriginXY[0] = originX; 770 mTempOriginXY[1] = originY; 771 mapPointGlobalToLocal(cellLayout, mTempOriginXY); 772 originX = (int)mTempOriginXY[0]; 773 originY = (int)mTempOriginXY[1]; 774 } else { 775 cellLayout = getCurrentDropLayout(); 776 } 777 if (source != this) { 778 onDropExternal(originX, originY, dragInfo, cellLayout); 779 } else { 780 // Move internally 781 if (mDragInfo != null) { 782 final View cell = mDragInfo.cell; 783 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 784 if (index != mDragInfo.screen) { 785 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 786 originalCellLayout.removeView(cell); 787 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 788 mDragInfo.spanX, mDragInfo.spanY); 789 } 790 791 mTargetCell = findNearestVacantArea(originX, originY, 792 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 793 mTargetCell); 794 cellLayout.onDropChild(cell); 795 796 // update the item's position after drop 797 final ItemInfo info = (ItemInfo) cell.getTag(); 798 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 799 cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 800 lp.cellX = mTargetCell[0]; 801 lp.cellY = mTargetCell[1]; 802 803 LauncherModel.moveItemInDatabase(mLauncher, info, 804 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 805 lp.cellX, lp.cellY); 806 } 807 } 808 } 809 810 public void onDragEnter(DragSource source, int x, int y, int xOffset, 811 int yOffset, DragView dragView, Object dragInfo) { 812 } 813 814 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 815 DragView dragView, Object dragInfo) { 816 817 if (mIsSmall) { 818 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 819 return null; 820 } 821 // We may need to delegate the drag to a child view. If a 1x1 item 822 // would land in a cell occupied by a DragTarget (e.g. a Folder), 823 // then drag events should be handled by that child. 824 825 ItemInfo item = (ItemInfo)dragInfo; 826 CellLayout currentLayout = getCurrentDropLayout(); 827 828 int dragPointX, dragPointY; 829 if (item.spanX == 1 && item.spanY == 1) { 830 // For a 1x1, calculate the drop cell exactly as in onDragOver 831 dragPointX = x - xOffset; 832 dragPointY = y - yOffset; 833 } else { 834 // Otherwise, use the exact drag coordinates 835 dragPointX = x; 836 dragPointY = y; 837 } 838 dragPointX += mScrollX - currentLayout.getLeft(); 839 dragPointY += mScrollY - currentLayout.getTop(); 840 841 // If we are dragging over a cell that contains a DropTarget that will 842 // accept the drop, delegate to that DropTarget. 843 final int[] cellXY = mTempCell; 844 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 845 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 846 if (child instanceof DropTarget) { 847 DropTarget target = (DropTarget)child; 848 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 849 return target; 850 } 851 } 852 return null; 853 } 854 855 856 private void mapPointGlobalToLocal(View v, float[] xy) { 857 xy[0] = xy[0] + mScrollX - v.getLeft(); 858 xy[1] = xy[1] + mScrollY - v.getTop(); 859 v.getMatrix().invert(mTempInverseMatrix); 860 mTempInverseMatrix.mapPoints(xy); 861 } 862 863 // xy = upper left corner of item being dragged 864 // bottomRightXy = lower right corner of item being dragged 865 // This method will see which mini-screen is most overlapped by the item being dragged, and 866 // return it. It will also transform the parameters xy and bottomRightXy into the local 867 // coordinate space of the returned screen 868 private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) { 869 float x = originX + dragView.getScaledDragRegionXOffset(); 870 float y = originY + dragView.getScaledDragRegionYOffset(); 871 float right = x + dragView.getScaledDragRegionWidth(); 872 float bottom = y + dragView.getScaledDragRegionHeight(); 873 874 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 875 // with the item being dragged. 876 final int screenCount = getChildCount(); 877 CellLayout bestMatchingScreen = null; 878 float smallestDistSoFar = Float.MAX_VALUE; 879 final float[] xy = mTempDragCoordinates; 880 final float[] bottomRightXy = mTempDragBottomRightCoordinates; 881 for (int i = 0; i < screenCount; i++) { 882 CellLayout cl = (CellLayout)getChildAt(i); 883 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 884 float left = cl.getLeft(); 885 float top = cl.getTop(); 886 xy[0] = x + mScrollX - left; 887 xy[1] = y + mScrollY - top; 888 889 bottomRightXy[0] = right + mScrollX - left; 890 bottomRightXy[1] = bottom + mScrollY - top; 891 892 cl.getMatrix().invert(mTempInverseMatrix); 893 mTempInverseMatrix.mapPoints(xy); 894 mTempInverseMatrix.mapPoints(bottomRightXy); 895 896 float dragRegionX = xy[0]; 897 float dragRegionY = xy[1]; 898 float dragRegionRight = bottomRightXy[0]; 899 float dragRegionBottom = bottomRightXy[1]; 900 float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f; 901 float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f; 902 903 // Find the overlapping region 904 float overlapLeft = Math.max(0f, dragRegionX); 905 float overlapTop = Math.max(0f, dragRegionY); 906 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 907 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 908 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 909 (overlapTop >= 0 && overlapBottom <= cl.getHeight())) { 910 // Calculate the distance between the two centers 911 float distX = dragRegionCenterX - cl.getWidth()/2; 912 float distY = dragRegionCenterY - cl.getHeight()/2; 913 float dist = distX * distX + distY * distY; 914 915 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 916 917 // Calculate the closest overlapping region 918 if (overlap > 0 && dist < smallestDistSoFar) { 919 smallestDistSoFar = dist; 920 bestMatchingScreen = cl; 921 } 922 } 923 } 924 925 if (bestMatchingScreen != mDragTargetLayout) { 926 if (mDragTargetLayout != null) { 927 mDragTargetLayout.onDragExit(); 928 } 929 mDragTargetLayout = bestMatchingScreen; 930 } 931 return bestMatchingScreen; 932 } 933 934 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 935 DragView dragView, Object dragInfo) { 936 CellLayout currentLayout; 937 int originX = x - xOffset; 938 int originY = y - yOffset; 939 if (mIsSmall) { 940 currentLayout = findMatchingPageForDragOver(dragView, originX, originY); 941 942 if (currentLayout == null) { 943 return; 944 } 945 946 currentLayout.setHover(true); 947 // get originX and originY in the local coordinate system of the screen 948 mTempOriginXY[0] = originX; 949 mTempOriginXY[1] = originY; 950 mapPointGlobalToLocal(currentLayout, mTempOriginXY); 951 originX = (int)mTempOriginXY[0]; 952 originY = (int)mTempOriginXY[1]; 953 } else { 954 currentLayout = getCurrentDropLayout(); 955 } 956 957 final ItemInfo item = (ItemInfo)dragInfo; 958 959 if (dragInfo instanceof LauncherAppWidgetInfo) { 960 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 961 962 if (widgetInfo.spanX == -1) { 963 // Calculate the grid spans needed to fit this widget 964 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 965 item.spanX = spans[0]; 966 item.spanY = spans[1]; 967 } 968 } 969 970 if (source != this) { 971 // This is a hack to fix the point used to determine which cell an icon from the all 972 // apps screen is over 973 if (item != null && item.spanX == 1 && currentLayout != null) { 974 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 975 976 originX += dragRegionLeft - dragView.getDragRegionLeft(); 977 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 978 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 979 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 980 } 981 } 982 } 983 if (currentLayout != mDragTargetLayout) { 984 if (mDragTargetLayout != null) { 985 mDragTargetLayout.onDragExit(); 986 } 987 mDragTargetLayout = currentLayout; 988 } 989 990 // only visualize the drop locations for moving icons within the home screen on tablet 991 // on phone, we also visualize icons dragged in from All Apps 992 if ((!LauncherApplication.isScreenXLarge() || source == this) 993 && mDragTargetLayout != null) { 994 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 995 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 996 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 997 mDragTargetLayout.visualizeDropLocation( 998 child, localOriginX, localOriginY, item.spanX, item.spanY, child); 999 } 1000 } 1001 1002 public void onDragExit(DragSource source, int x, int y, int xOffset, 1003 int yOffset, DragView dragView, Object dragInfo) { 1004 if (mDragTargetLayout != null) { 1005 mDragTargetLayout.onDragExit(); 1006 mDragTargetLayout = null; 1007 } 1008 } 1009 1010 private void onDropExternal(int x, int y, Object dragInfo, 1011 CellLayout cellLayout) { 1012 onDropExternal(x, y, dragInfo, cellLayout, false); 1013 } 1014 1015 /** 1016 * Add the item specified by dragInfo to the given layout. 1017 * This is basically the equivalent of onDropExternal, except it's not initiated 1018 * by drag and drop. 1019 * @return true if successful 1020 */ 1021 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1022 CellLayout cl = (CellLayout) layout; 1023 ItemInfo info = (ItemInfo) dragInfo; 1024 1025 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1026 onDropExternal(0, 0, dragInfo, cl, false); 1027 return true; 1028 } 1029 mLauncher.showOutOfSpaceMessage(); 1030 return false; 1031 } 1032 1033 // Drag from somewhere else 1034 private void onDropExternal(int x, int y, Object dragInfo, 1035 CellLayout cellLayout, boolean insertAtFirst) { 1036 int screen = indexOfChild(cellLayout); 1037 if (dragInfo instanceof PendingAddItemInfo) { 1038 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1039 // When dragging and dropping from customization tray, we deal with creating 1040 // widgets/shortcuts/folders in a slightly different way 1041 int[] touchXY = new int[2]; 1042 touchXY[0] = x; 1043 touchXY[1] = y; 1044 switch (info.itemType) { 1045 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1046 mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); 1047 break; 1048 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1049 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 1050 break; 1051 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1052 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 1053 break; 1054 default: 1055 throw new IllegalStateException("Unknown item type: " + info.itemType); 1056 } 1057 cellLayout.onDragExit(); 1058 return; 1059 } 1060 1061 // This is for other drag/drop cases, like dragging from All Apps 1062 ItemInfo info = (ItemInfo) dragInfo; 1063 1064 View view = null; 1065 1066 switch (info.itemType) { 1067 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1068 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1069 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1070 // Came from all apps -- make a copy 1071 info = new ShortcutInfo((ApplicationInfo) info); 1072 } 1073 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1074 (ShortcutInfo) info); 1075 break; 1076 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1077 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1078 cellLayout, ((UserFolderInfo) info)); 1079 break; 1080 default: 1081 throw new IllegalStateException("Unknown item type: " + info.itemType); 1082 } 1083 1084 // If the view is null, it has already been added. 1085 if (view == null) { 1086 cellLayout.onDragExit(); 1087 } else { 1088 mTargetCell = findNearestVacantArea(x, y, 1, 1, view, cellLayout, mTargetCell); 1089 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1090 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1091 cellLayout.onDropChild(view); 1092 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1093 1094 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1095 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1096 lp.cellX, lp.cellY); 1097 } 1098 } 1099 1100 /** 1101 * Return the current {@link CellLayout}, correctly picking the destination 1102 * screen while a scroll is in progress. 1103 */ 1104 private CellLayout getCurrentDropLayout() { 1105 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 1106 return (CellLayout) getChildAt(index); 1107 } 1108 1109 /** 1110 * Return the current CellInfo describing our current drag; this method exists 1111 * so that Launcher can sync this object with the correct info when the activity is created/ 1112 * destroyed 1113 * 1114 */ 1115 public CellLayout.CellInfo getDragInfo() { 1116 return mDragInfo; 1117 } 1118 1119 /** 1120 * {@inheritDoc} 1121 */ 1122 public boolean acceptDrop(DragSource source, int x, int y, 1123 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1124 CellLayout layout; 1125 if (mIsSmall) { 1126 layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset); 1127 if (layout == null) { 1128 // cancel the drag if we're not over a mini-screen at time of drop 1129 return false; 1130 } 1131 } else { 1132 layout = getCurrentDropLayout(); 1133 } 1134 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1135 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1136 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1137 1138 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1139 1140 if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1141 return true; 1142 } else { 1143 mLauncher.showOutOfSpaceMessage(); 1144 return false; 1145 } 1146 } 1147 1148 /** 1149 * Calculate the nearest cell where the given object would be dropped. 1150 */ 1151 private int[] findNearestVacantArea(int pixelX, int pixelY, 1152 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1153 1154 final int[] cellXY = mTempCell; 1155 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 1156 int localPixelY = pixelY - (layout.getTop() - mScrollY); 1157 layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY); 1158 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1159 1160 // Find the best target drop location 1161 return layout.findNearestVacantArea( 1162 mTempEstimate[0], mTempEstimate[1], spanX, spanY, ignoreView, recycle); 1163 } 1164 1165 /** 1166 * Estimate the size that a child with the given dimensions will take in the current screen. 1167 */ 1168 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1169 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 1170 } 1171 1172 void setLauncher(Launcher launcher) { 1173 mLauncher = launcher; 1174 } 1175 1176 public void setDragController(DragController dragController) { 1177 mDragController = dragController; 1178 } 1179 1180 public void onDropCompleted(View target, boolean success) { 1181 if (success) { 1182 if (target != this && mDragInfo != null) { 1183 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1184 cellLayout.removeView(mDragInfo.cell); 1185 if (mDragInfo.cell instanceof DropTarget) { 1186 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1187 } 1188 // final Object tag = mDragInfo.cell.getTag(); 1189 } 1190 } else { 1191 if (mDragInfo != null) { 1192 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1193 cellLayout.onDropAborted(mDragInfo.cell); 1194 } 1195 } 1196 1197 mDragInfo = null; 1198 } 1199 1200 public boolean isDropEnabled() { 1201 return true; 1202 } 1203 1204 @Override 1205 protected void onRestoreInstanceState(Parcelable state) { 1206 super.onRestoreInstanceState(state); 1207 Launcher.setScreen(mCurrentPage); 1208 } 1209 1210 @Override 1211 public void scrollLeft() { 1212 if (!mIsSmall) { 1213 super.scrollLeft(); 1214 } 1215 } 1216 1217 @Override 1218 public void scrollRight() { 1219 if (!mIsSmall) { 1220 super.scrollRight(); 1221 } 1222 } 1223 1224 public Folder getFolderForTag(Object tag) { 1225 final int screenCount = getChildCount(); 1226 for (int screen = 0; screen < screenCount; screen++) { 1227 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1228 int count = currentScreen.getChildCount(); 1229 for (int i = 0; i < count; i++) { 1230 View child = currentScreen.getChildAt(i); 1231 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1232 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1233 Folder f = (Folder) child; 1234 if (f.getInfo() == tag && f.getInfo().opened) { 1235 return f; 1236 } 1237 } 1238 } 1239 } 1240 return null; 1241 } 1242 1243 public View getViewForTag(Object tag) { 1244 int screenCount = getChildCount(); 1245 for (int screen = 0; screen < screenCount; screen++) { 1246 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1247 int count = currentScreen.getChildCount(); 1248 for (int i = 0; i < count; i++) { 1249 View child = currentScreen.getChildAt(i); 1250 if (child.getTag() == tag) { 1251 return child; 1252 } 1253 } 1254 } 1255 return null; 1256 } 1257 1258 1259 void removeItems(final ArrayList<ApplicationInfo> apps) { 1260 final int screenCount = getChildCount(); 1261 final PackageManager manager = getContext().getPackageManager(); 1262 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1263 1264 final HashSet<String> packageNames = new HashSet<String>(); 1265 final int appCount = apps.size(); 1266 for (int i = 0; i < appCount; i++) { 1267 packageNames.add(apps.get(i).componentName.getPackageName()); 1268 } 1269 1270 for (int i = 0; i < screenCount; i++) { 1271 final CellLayout layout = (CellLayout) getChildAt(i); 1272 1273 // Avoid ANRs by treating each screen separately 1274 post(new Runnable() { 1275 public void run() { 1276 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1277 childrenToRemove.clear(); 1278 1279 int childCount = layout.getChildCount(); 1280 for (int j = 0; j < childCount; j++) { 1281 final View view = layout.getChildAt(j); 1282 Object tag = view.getTag(); 1283 1284 if (tag instanceof ShortcutInfo) { 1285 final ShortcutInfo info = (ShortcutInfo) tag; 1286 final Intent intent = info.intent; 1287 final ComponentName name = intent.getComponent(); 1288 1289 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1290 for (String packageName: packageNames) { 1291 if (packageName.equals(name.getPackageName())) { 1292 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1293 childrenToRemove.add(view); 1294 } 1295 } 1296 } 1297 } else if (tag instanceof UserFolderInfo) { 1298 final UserFolderInfo info = (UserFolderInfo) tag; 1299 final ArrayList<ShortcutInfo> contents = info.contents; 1300 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1301 final int contentsCount = contents.size(); 1302 boolean removedFromFolder = false; 1303 1304 for (int k = 0; k < contentsCount; k++) { 1305 final ShortcutInfo appInfo = contents.get(k); 1306 final Intent intent = appInfo.intent; 1307 final ComponentName name = intent.getComponent(); 1308 1309 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1310 for (String packageName: packageNames) { 1311 if (packageName.equals(name.getPackageName())) { 1312 toRemove.add(appInfo); 1313 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1314 removedFromFolder = true; 1315 } 1316 } 1317 } 1318 } 1319 1320 contents.removeAll(toRemove); 1321 if (removedFromFolder) { 1322 final Folder folder = getOpenFolder(); 1323 if (folder != null) 1324 folder.notifyDataSetChanged(); 1325 } 1326 } else if (tag instanceof LiveFolderInfo) { 1327 final LiveFolderInfo info = (LiveFolderInfo) tag; 1328 final Uri uri = info.uri; 1329 final ProviderInfo providerInfo = manager.resolveContentProvider( 1330 uri.getAuthority(), 0); 1331 1332 if (providerInfo != null) { 1333 for (String packageName: packageNames) { 1334 if (packageName.equals(providerInfo.packageName)) { 1335 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1336 childrenToRemove.add(view); 1337 } 1338 } 1339 } 1340 } else if (tag instanceof LauncherAppWidgetInfo) { 1341 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1342 final AppWidgetProviderInfo provider = 1343 widgets.getAppWidgetInfo(info.appWidgetId); 1344 if (provider != null) { 1345 for (String packageName: packageNames) { 1346 if (packageName.equals(provider.provider.getPackageName())) { 1347 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1348 childrenToRemove.add(view); 1349 } 1350 } 1351 } 1352 } 1353 } 1354 1355 childCount = childrenToRemove.size(); 1356 for (int j = 0; j < childCount; j++) { 1357 View child = childrenToRemove.get(j); 1358 layout.removeViewInLayout(child); 1359 if (child instanceof DropTarget) { 1360 mDragController.removeDropTarget((DropTarget)child); 1361 } 1362 } 1363 1364 if (childCount > 0) { 1365 layout.requestLayout(); 1366 layout.invalidate(); 1367 } 1368 } 1369 }); 1370 } 1371 } 1372 1373 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1374 final int screenCount = getChildCount(); 1375 for (int i = 0; i < screenCount; i++) { 1376 final CellLayout layout = (CellLayout) getChildAt(i); 1377 int childCount = layout.getChildCount(); 1378 for (int j = 0; j < childCount; j++) { 1379 final View view = layout.getChildAt(j); 1380 Object tag = view.getTag(); 1381 if (tag instanceof ShortcutInfo) { 1382 ShortcutInfo info = (ShortcutInfo)tag; 1383 // We need to check for ACTION_MAIN otherwise getComponent() might 1384 // return null for some shortcuts (for instance, for shortcuts to 1385 // web pages.) 1386 final Intent intent = info.intent; 1387 final ComponentName name = intent.getComponent(); 1388 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1389 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1390 final int appCount = apps.size(); 1391 for (int k = 0; k < appCount; k++) { 1392 ApplicationInfo app = apps.get(k); 1393 if (app.componentName.equals(name)) { 1394 info.setIcon(mIconCache.getIcon(info.intent)); 1395 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1396 new FastBitmapDrawable(info.getIcon(mIconCache)), 1397 null, null); 1398 } 1399 } 1400 } 1401 } 1402 } 1403 } 1404 } 1405 1406 void moveToDefaultScreen(boolean animate) { 1407 if (animate) { 1408 if (mIsSmall) { 1409 unshrink(mDefaultPage); 1410 } else { 1411 snapToPage(mDefaultPage); 1412 } 1413 } else { 1414 setCurrentPage(mDefaultPage); 1415 } 1416 getChildAt(mDefaultPage).requestFocus(); 1417 } 1418 1419 void setIndicators(Drawable previous, Drawable next) { 1420 mPreviousIndicator = previous; 1421 mNextIndicator = next; 1422 previous.setLevel(mCurrentPage); 1423 next.setLevel(mCurrentPage); 1424 } 1425 1426 @Override 1427 public void syncPages() { 1428 } 1429 1430 @Override 1431 public void syncPageItems(int page) { 1432 } 1433 1434} 1435