Workspace.java revision 60587efe1a60ed698db9e2cafcd48f2c3fc8aa40
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 if (source != this) { 778 onDropExternal(originX, originY, dragInfo, cellLayout); 779 } else { 780 // Move internally 781 if (mDragInfo != null) { 782 final View cell = mDragInfo.cell; 783 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 784 if (index != mDragInfo.screen) { 785 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 786 originalCellLayout.removeView(cell); 787 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 788 mDragInfo.spanX, mDragInfo.spanY); 789 } 790 791 mTargetCell = findNearestVacantArea(originX, originY, 792 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 793 mTargetCell); 794 cellLayout.onDropChild(cell); 795 796 // update the item's position after drop 797 final ItemInfo info = (ItemInfo) cell.getTag(); 798 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 799 cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 800 lp.cellX = mTargetCell[0]; 801 lp.cellY = mTargetCell[1]; 802 cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, 803 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 804 805 LauncherModel.moveItemInDatabase(mLauncher, info, 806 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 807 lp.cellX, lp.cellY); 808 } 809 } 810 } 811 812 public void onDragEnter(DragSource source, int x, int y, int xOffset, 813 int yOffset, DragView dragView, Object dragInfo) { 814 getCurrentDropLayout().onDragEnter(dragView); 815 } 816 817 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 818 DragView dragView, Object dragInfo) { 819 820 if (mIsSmall) { 821 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 822 return null; 823 } 824 // We may need to delegate the drag to a child view. If a 1x1 item 825 // would land in a cell occupied by a DragTarget (e.g. a Folder), 826 // then drag events should be handled by that child. 827 828 ItemInfo item = (ItemInfo)dragInfo; 829 CellLayout currentLayout = getCurrentDropLayout(); 830 831 int dragPointX, dragPointY; 832 if (item.spanX == 1 && item.spanY == 1) { 833 // For a 1x1, calculate the drop cell exactly as in onDragOver 834 dragPointX = x - xOffset; 835 dragPointY = y - yOffset; 836 } else { 837 // Otherwise, use the exact drag coordinates 838 dragPointX = x; 839 dragPointY = y; 840 } 841 dragPointX += mScrollX - currentLayout.getLeft(); 842 dragPointY += mScrollY - currentLayout.getTop(); 843 844 // If we are dragging over a cell that contains a DropTarget that will 845 // accept the drop, delegate to that DropTarget. 846 final int[] cellXY = mTempCell; 847 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 848 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 849 if (child instanceof DropTarget) { 850 DropTarget target = (DropTarget)child; 851 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 852 return target; 853 } 854 } 855 return null; 856 } 857 858 private void mapPointGlobalToLocal(View v, float[] xy) { 859 xy[0] = xy[0] + mScrollX - v.getLeft(); 860 xy[1] = xy[1] + mScrollY - v.getTop(); 861 v.getMatrix().invert(mTempInverseMatrix); 862 mTempInverseMatrix.mapPoints(xy); 863 } 864 865 // xy = upper left corner of item being dragged 866 // bottomRightXy = lower right corner of item being dragged 867 // This method will see which mini-screen is most overlapped by the item being dragged, and 868 // return it. It will also transform the parameters xy and bottomRightXy into the local 869 // coordinate space of the returned screen 870 private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) { 871 float x = originX + dragView.getScaledDragRegionXOffset(); 872 float y = originY + dragView.getScaledDragRegionYOffset(); 873 float right = x + dragView.getScaledDragRegionWidth(); 874 float bottom = y + dragView.getScaledDragRegionHeight(); 875 876 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 877 // with the item being dragged. 878 final int screenCount = getChildCount(); 879 CellLayout bestMatchingScreen = null; 880 float smallestDistSoFar = Float.MAX_VALUE; 881 final float[] xy = mTempDragCoordinates; 882 final float[] bottomRightXy = mTempDragBottomRightCoordinates; 883 for (int i = 0; i < screenCount; i++) { 884 CellLayout cl = (CellLayout)getChildAt(i); 885 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 886 float left = cl.getLeft(); 887 float top = cl.getTop(); 888 xy[0] = x + mScrollX - left; 889 xy[1] = y + mScrollY - top; 890 891 bottomRightXy[0] = right + mScrollX - left; 892 bottomRightXy[1] = bottom + mScrollY - top; 893 894 cl.getMatrix().invert(mTempInverseMatrix); 895 mTempInverseMatrix.mapPoints(xy); 896 mTempInverseMatrix.mapPoints(bottomRightXy); 897 898 float dragRegionX = xy[0]; 899 float dragRegionY = xy[1]; 900 float dragRegionRight = bottomRightXy[0]; 901 float dragRegionBottom = bottomRightXy[1]; 902 float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f; 903 float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f; 904 905 // Find the overlapping region 906 float overlapLeft = Math.max(0f, dragRegionX); 907 float overlapTop = Math.max(0f, dragRegionY); 908 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 909 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 910 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 911 (overlapTop >= 0 && overlapBottom <= cl.getHeight())) { 912 // Calculate the distance between the two centers 913 float distX = dragRegionCenterX - cl.getWidth()/2; 914 float distY = dragRegionCenterY - cl.getHeight()/2; 915 float dist = distX * distX + distY * distY; 916 917 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 918 919 // Calculate the closest overlapping region 920 if (overlap > 0 && dist < smallestDistSoFar) { 921 smallestDistSoFar = dist; 922 bestMatchingScreen = cl; 923 } 924 } 925 } 926 927 if (bestMatchingScreen != mDragTargetLayout) { 928 if (mDragTargetLayout != null) { 929 mDragTargetLayout.onDragExit(); 930 } 931 mDragTargetLayout = bestMatchingScreen; 932 } 933 return bestMatchingScreen; 934 } 935 936 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 937 DragView dragView, Object dragInfo) { 938 CellLayout currentLayout; 939 int originX = x - xOffset; 940 int originY = y - yOffset; 941 if (mIsSmall) { 942 currentLayout = findMatchingPageForDragOver(dragView, originX, originY); 943 944 if (currentLayout == null) { 945 return; 946 } 947 948 currentLayout.setHover(true); 949 // get originX and originY in the local coordinate system of the screen 950 mTempOriginXY[0] = originX; 951 mTempOriginXY[1] = originY; 952 mapPointGlobalToLocal(currentLayout, mTempOriginXY); 953 originX = (int)mTempOriginXY[0]; 954 originY = (int)mTempOriginXY[1]; 955 } else { 956 currentLayout = getCurrentDropLayout(); 957 } 958 959 final ItemInfo item = (ItemInfo)dragInfo; 960 961 if (dragInfo instanceof LauncherAppWidgetInfo) { 962 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 963 964 if (widgetInfo.spanX == -1) { 965 // Calculate the grid spans needed to fit this widget 966 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 967 item.spanX = spans[0]; 968 item.spanY = spans[1]; 969 } 970 } 971 972 if (source != this) { 973 // This is a hack to fix the point used to determine which cell an icon from the all 974 // apps screen is over 975 if (item != null && item.spanX == 1 && currentLayout != null) { 976 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 977 978 originX += dragRegionLeft - dragView.getDragRegionLeft(); 979 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 980 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 981 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 982 } 983 } 984 } 985 if (currentLayout != mDragTargetLayout) { 986 if (mDragTargetLayout != null) { 987 mDragTargetLayout.onDragExit(); 988 currentLayout.onDragEnter(dragView); 989 } 990 mDragTargetLayout = currentLayout; 991 } 992 993 // only visualize the drop locations for moving icons within the home screen on tablet 994 // on phone, we also visualize icons dragged in from All Apps 995 if ((!LauncherApplication.isScreenXLarge() || source == this) 996 && mDragTargetLayout != null) { 997 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 998 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 999 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 1000 mDragTargetLayout.visualizeDropLocation( 1001 child, localOriginX, localOriginY, item.spanX, item.spanY); 1002 } 1003 } 1004 1005 public void onDragExit(DragSource source, int x, int y, int xOffset, 1006 int yOffset, DragView dragView, Object dragInfo) { 1007 if (mDragTargetLayout != null) { 1008 mDragTargetLayout.onDragExit(); 1009 mDragTargetLayout = null; 1010 } 1011 } 1012 1013 private void onDropExternal(int x, int y, Object dragInfo, 1014 CellLayout cellLayout) { 1015 onDropExternal(x, y, dragInfo, cellLayout, false); 1016 } 1017 1018 /** 1019 * Add the item specified by dragInfo to the given layout. 1020 * This is basically the equivalent of onDropExternal, except it's not initiated 1021 * by drag and drop. 1022 * @return true if successful 1023 */ 1024 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1025 CellLayout cl = (CellLayout) layout; 1026 ItemInfo info = (ItemInfo) dragInfo; 1027 1028 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1029 onDropExternal(0, 0, dragInfo, cl, false); 1030 return true; 1031 } 1032 mLauncher.showOutOfSpaceMessage(); 1033 return false; 1034 } 1035 1036 // Drag from somewhere else 1037 private void onDropExternal(int x, int y, Object dragInfo, 1038 CellLayout cellLayout, boolean insertAtFirst) { 1039 int screen = indexOfChild(cellLayout); 1040 if (dragInfo instanceof PendingAddItemInfo) { 1041 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1042 // When dragging and dropping from customization tray, we deal with creating 1043 // widgets/shortcuts/folders in a slightly different way 1044 int[] touchXY = new int[2]; 1045 touchXY[0] = x; 1046 touchXY[1] = y; 1047 switch (info.itemType) { 1048 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1049 mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); 1050 break; 1051 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1052 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 1053 break; 1054 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1055 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 1056 break; 1057 default: 1058 throw new IllegalStateException("Unknown item type: " + info.itemType); 1059 } 1060 cellLayout.onDragExit(); 1061 return; 1062 } 1063 1064 // This is for other drag/drop cases, like dragging from All Apps 1065 ItemInfo info = (ItemInfo) dragInfo; 1066 1067 View view = null; 1068 1069 switch (info.itemType) { 1070 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1071 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1072 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1073 // Came from all apps -- make a copy 1074 info = new ShortcutInfo((ApplicationInfo) info); 1075 } 1076 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1077 (ShortcutInfo) info); 1078 break; 1079 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1080 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1081 cellLayout, ((UserFolderInfo) info)); 1082 break; 1083 default: 1084 throw new IllegalStateException("Unknown item type: " + info.itemType); 1085 } 1086 1087 // If the view is null, it has already been added. 1088 if (view == null) { 1089 cellLayout.onDragExit(); 1090 } else { 1091 mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell); 1092 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1093 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1094 cellLayout.onDropChild(view); 1095 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1096 1097 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1098 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1099 lp.cellX, lp.cellY); 1100 } 1101 } 1102 1103 /** 1104 * Return the current {@link CellLayout}, correctly picking the destination 1105 * screen while a scroll is in progress. 1106 */ 1107 private CellLayout getCurrentDropLayout() { 1108 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 1109 return (CellLayout) getChildAt(index); 1110 } 1111 1112 /** 1113 * Return the current CellInfo describing our current drag; this method exists 1114 * so that Launcher can sync this object with the correct info when the activity is created/ 1115 * destroyed 1116 * 1117 */ 1118 public CellLayout.CellInfo getDragInfo() { 1119 return mDragInfo; 1120 } 1121 1122 /** 1123 * {@inheritDoc} 1124 */ 1125 public boolean acceptDrop(DragSource source, int x, int y, 1126 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1127 CellLayout layout; 1128 if (mIsSmall) { 1129 layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset); 1130 if (layout == null) { 1131 // cancel the drag if we're not over a mini-screen at time of drop 1132 return false; 1133 } 1134 } else { 1135 layout = getCurrentDropLayout(); 1136 } 1137 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1138 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1139 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1140 1141 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1142 1143 if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1144 return true; 1145 } else { 1146 mLauncher.showOutOfSpaceMessage(); 1147 return false; 1148 } 1149 } 1150 1151 /** 1152 * Calculate the nearest cell where the given object would be dropped. 1153 */ 1154 private int[] findNearestVacantArea(int pixelX, int pixelY, 1155 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1156 1157 final int[] cellXY = mTempCell; 1158 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 1159 int localPixelY = pixelY - (layout.getTop() - mScrollY); 1160 layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY); 1161 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1162 1163 // Find the best target drop location 1164 return layout.findNearestVacantArea( 1165 mTempEstimate[0], mTempEstimate[1], spanX, spanY, ignoreView, recycle); 1166 } 1167 1168 /** 1169 * Estimate the size that a child with the given dimensions will take in the current screen. 1170 */ 1171 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1172 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 1173 } 1174 1175 void setLauncher(Launcher launcher) { 1176 mLauncher = launcher; 1177 } 1178 1179 public void setDragController(DragController dragController) { 1180 mDragController = dragController; 1181 } 1182 1183 public void onDropCompleted(View target, boolean success) { 1184 if (success) { 1185 if (target != this && mDragInfo != null) { 1186 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1187 cellLayout.removeView(mDragInfo.cell); 1188 if (mDragInfo.cell instanceof DropTarget) { 1189 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1190 } 1191 // final Object tag = mDragInfo.cell.getTag(); 1192 } 1193 } else { 1194 if (mDragInfo != null) { 1195 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1196 cellLayout.onDropAborted(mDragInfo.cell); 1197 } 1198 } 1199 1200 mDragInfo = null; 1201 } 1202 1203 public boolean isDropEnabled() { 1204 return true; 1205 } 1206 1207 @Override 1208 protected void onRestoreInstanceState(Parcelable state) { 1209 super.onRestoreInstanceState(state); 1210 Launcher.setScreen(mCurrentPage); 1211 } 1212 1213 @Override 1214 public void scrollLeft() { 1215 if (!mIsSmall) { 1216 super.scrollLeft(); 1217 } 1218 } 1219 1220 @Override 1221 public void scrollRight() { 1222 if (!mIsSmall) { 1223 super.scrollRight(); 1224 } 1225 } 1226 1227 public Folder getFolderForTag(Object tag) { 1228 final int screenCount = getChildCount(); 1229 for (int screen = 0; screen < screenCount; screen++) { 1230 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1231 int count = currentScreen.getChildCount(); 1232 for (int i = 0; i < count; i++) { 1233 View child = currentScreen.getChildAt(i); 1234 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1235 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1236 Folder f = (Folder) child; 1237 if (f.getInfo() == tag && f.getInfo().opened) { 1238 return f; 1239 } 1240 } 1241 } 1242 } 1243 return null; 1244 } 1245 1246 public View getViewForTag(Object tag) { 1247 int screenCount = getChildCount(); 1248 for (int screen = 0; screen < screenCount; screen++) { 1249 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1250 int count = currentScreen.getChildCount(); 1251 for (int i = 0; i < count; i++) { 1252 View child = currentScreen.getChildAt(i); 1253 if (child.getTag() == tag) { 1254 return child; 1255 } 1256 } 1257 } 1258 return null; 1259 } 1260 1261 1262 void removeItems(final ArrayList<ApplicationInfo> apps) { 1263 final int screenCount = getChildCount(); 1264 final PackageManager manager = getContext().getPackageManager(); 1265 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1266 1267 final HashSet<String> packageNames = new HashSet<String>(); 1268 final int appCount = apps.size(); 1269 for (int i = 0; i < appCount; i++) { 1270 packageNames.add(apps.get(i).componentName.getPackageName()); 1271 } 1272 1273 for (int i = 0; i < screenCount; i++) { 1274 final CellLayout layout = (CellLayout) getChildAt(i); 1275 1276 // Avoid ANRs by treating each screen separately 1277 post(new Runnable() { 1278 public void run() { 1279 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1280 childrenToRemove.clear(); 1281 1282 int childCount = layout.getChildCount(); 1283 for (int j = 0; j < childCount; j++) { 1284 final View view = layout.getChildAt(j); 1285 Object tag = view.getTag(); 1286 1287 if (tag instanceof ShortcutInfo) { 1288 final ShortcutInfo info = (ShortcutInfo) tag; 1289 final Intent intent = info.intent; 1290 final ComponentName name = intent.getComponent(); 1291 1292 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1293 for (String packageName: packageNames) { 1294 if (packageName.equals(name.getPackageName())) { 1295 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1296 childrenToRemove.add(view); 1297 } 1298 } 1299 } 1300 } else if (tag instanceof UserFolderInfo) { 1301 final UserFolderInfo info = (UserFolderInfo) tag; 1302 final ArrayList<ShortcutInfo> contents = info.contents; 1303 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1304 final int contentsCount = contents.size(); 1305 boolean removedFromFolder = false; 1306 1307 for (int k = 0; k < contentsCount; k++) { 1308 final ShortcutInfo appInfo = contents.get(k); 1309 final Intent intent = appInfo.intent; 1310 final ComponentName name = intent.getComponent(); 1311 1312 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1313 for (String packageName: packageNames) { 1314 if (packageName.equals(name.getPackageName())) { 1315 toRemove.add(appInfo); 1316 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1317 removedFromFolder = true; 1318 } 1319 } 1320 } 1321 } 1322 1323 contents.removeAll(toRemove); 1324 if (removedFromFolder) { 1325 final Folder folder = getOpenFolder(); 1326 if (folder != null) 1327 folder.notifyDataSetChanged(); 1328 } 1329 } else if (tag instanceof LiveFolderInfo) { 1330 final LiveFolderInfo info = (LiveFolderInfo) tag; 1331 final Uri uri = info.uri; 1332 final ProviderInfo providerInfo = manager.resolveContentProvider( 1333 uri.getAuthority(), 0); 1334 1335 if (providerInfo != null) { 1336 for (String packageName: packageNames) { 1337 if (packageName.equals(providerInfo.packageName)) { 1338 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1339 childrenToRemove.add(view); 1340 } 1341 } 1342 } 1343 } else if (tag instanceof LauncherAppWidgetInfo) { 1344 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1345 final AppWidgetProviderInfo provider = 1346 widgets.getAppWidgetInfo(info.appWidgetId); 1347 if (provider != null) { 1348 for (String packageName: packageNames) { 1349 if (packageName.equals(provider.provider.getPackageName())) { 1350 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1351 childrenToRemove.add(view); 1352 } 1353 } 1354 } 1355 } 1356 } 1357 1358 childCount = childrenToRemove.size(); 1359 for (int j = 0; j < childCount; j++) { 1360 View child = childrenToRemove.get(j); 1361 layout.removeViewInLayout(child); 1362 if (child instanceof DropTarget) { 1363 mDragController.removeDropTarget((DropTarget)child); 1364 } 1365 } 1366 1367 if (childCount > 0) { 1368 layout.requestLayout(); 1369 layout.invalidate(); 1370 } 1371 } 1372 }); 1373 } 1374 } 1375 1376 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1377 final int screenCount = getChildCount(); 1378 for (int i = 0; i < screenCount; i++) { 1379 final CellLayout layout = (CellLayout) getChildAt(i); 1380 int childCount = layout.getChildCount(); 1381 for (int j = 0; j < childCount; j++) { 1382 final View view = layout.getChildAt(j); 1383 Object tag = view.getTag(); 1384 if (tag instanceof ShortcutInfo) { 1385 ShortcutInfo info = (ShortcutInfo)tag; 1386 // We need to check for ACTION_MAIN otherwise getComponent() might 1387 // return null for some shortcuts (for instance, for shortcuts to 1388 // web pages.) 1389 final Intent intent = info.intent; 1390 final ComponentName name = intent.getComponent(); 1391 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1392 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1393 final int appCount = apps.size(); 1394 for (int k = 0; k < appCount; k++) { 1395 ApplicationInfo app = apps.get(k); 1396 if (app.componentName.equals(name)) { 1397 info.setIcon(mIconCache.getIcon(info.intent)); 1398 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1399 new FastBitmapDrawable(info.getIcon(mIconCache)), 1400 null, null); 1401 } 1402 } 1403 } 1404 } 1405 } 1406 } 1407 } 1408 1409 void moveToDefaultScreen(boolean animate) { 1410 if (animate) { 1411 if (mIsSmall) { 1412 unshrink(mDefaultPage); 1413 } else { 1414 snapToPage(mDefaultPage); 1415 } 1416 } else { 1417 setCurrentPage(mDefaultPage); 1418 } 1419 getChildAt(mDefaultPage).requestFocus(); 1420 } 1421 1422 void setIndicators(Drawable previous, Drawable next) { 1423 mPreviousIndicator = previous; 1424 mNextIndicator = next; 1425 previous.setLevel(mCurrentPage); 1426 next.setLevel(mCurrentPage); 1427 } 1428 1429 @Override 1430 public void syncPages() { 1431 } 1432 1433 @Override 1434 public void syncPageItems(int page) { 1435 } 1436 1437} 1438