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