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