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