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