Workspace.java revision 16fed41e5e680c547b23e108788eb85f1b04d36d
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 View cur = getChildAt(mCurrentPage); 479 View toRight = getChildAt(mCurrentPage + 1); 480 View toLeft = 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 pageWidth = cur.getMeasuredWidth(); 498 int delta = screenCenter - (mCurrentPage * pageWidth + pageWidth / 2 + 499 getRelativeChildOffset(0)); 500 501 float scrollProgress = Math.abs(delta/(pageWidth*1.0f)); 502 int scrollDirection = delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT; 503 504 float rotation; 505 506 if (scrollDirection == SCROLL_RIGHT) { 507 rotation = -scrollProgress * WORKSPACE_ROTATION; 508 cur.setRotationY(rotation); 509 cur.setScaleX(getScaleXForRotation(rotation)); 510 if (toLeft != null) { 511 rotation = WORKSPACE_ROTATION * (1 - scrollProgress); 512 toLeft.setRotationY(rotation); 513 toLeft.setScaleX(getScaleXForRotation(rotation)); 514 } 515 if (toRight != null) { 516 toRight.setRotationY(-WORKSPACE_ROTATION); 517 toRight.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION)); 518 } 519 } else { 520 rotation = scrollProgress * WORKSPACE_ROTATION; 521 cur.setRotationY(rotation); 522 cur.setScaleX(getScaleXForRotation(rotation)); 523 524 if (toRight != null) { 525 rotation = -WORKSPACE_ROTATION * (1 - scrollProgress); 526 toRight.setRotationY(rotation); 527 toRight.setScaleX(getScaleXForRotation(rotation)); 528 } 529 if (toLeft != null) { 530 toLeft.setRotationY(WORKSPACE_ROTATION); 531 toLeft.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION)); 532 } 533 } 534 } 535 536 protected void onAttachedToWindow() { 537 super.onAttachedToWindow(); 538 computeScroll(); 539 mDragController.setWindowToken(getWindowToken()); 540 } 541 542 @Override 543 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 544 super.onLayout(changed, left, top, right, bottom); 545 546 // if shrinkToBottom() is called on initialization, it has to be deferred 547 // until after the first call to onLayout so that it has the correct width 548 if (mWaitingToShrinkToBottom) { 549 shrinkToBottom(false); 550 mWaitingToShrinkToBottom = false; 551 } 552 553 if (LauncherApplication.isInPlaceRotationEnabled()) { 554 // When the device is rotated, the scroll position of the current screen 555 // needs to be refreshed 556 setCurrentPage(getCurrentPage()); 557 } 558 } 559 560 @Override 561 protected void dispatchDraw(Canvas canvas) { 562 if (mIsSmall) { 563 // Draw all the workspaces if we're small 564 final int pageCount = getChildCount(); 565 final long drawingTime = getDrawingTime(); 566 for (int i = 0; i < pageCount; i++) { 567 final View page = (View) getChildAt(i); 568 569 drawChild(canvas, page, drawingTime); 570 } 571 } else { 572 super.dispatchDraw(canvas); 573 } 574 } 575 576 @Override 577 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 578 if (!mLauncher.isAllAppsVisible()) { 579 final Folder openFolder = getOpenFolder(); 580 if (openFolder != null) { 581 return openFolder.requestFocus(direction, previouslyFocusedRect); 582 } else { 583 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 584 } 585 } 586 return false; 587 } 588 589 @Override 590 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 591 if (!mLauncher.isAllAppsVisible()) { 592 final Folder openFolder = getOpenFolder(); 593 if (openFolder != null) { 594 openFolder.addFocusables(views, direction); 595 } else { 596 super.addFocusables(views, direction, focusableMode); 597 } 598 } 599 } 600 601 @Override 602 public boolean dispatchTouchEvent(MotionEvent ev) { 603 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 604 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 605 // ie when you click on a mini-screen, it zooms back to that screen) 606 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 607 return false; 608 } 609 } 610 return super.dispatchTouchEvent(ev); 611 } 612 613 void enableChildrenCache(int fromPage, int toPage) { 614 if (fromPage > toPage) { 615 final int temp = fromPage; 616 fromPage = toPage; 617 toPage = temp; 618 } 619 620 final int screenCount = getChildCount(); 621 622 fromPage = Math.max(fromPage, 0); 623 toPage = Math.min(toPage, screenCount - 1); 624 625 for (int i = fromPage; i <= toPage; i++) { 626 final CellLayout layout = (CellLayout) getChildAt(i); 627 layout.setChildrenDrawnWithCacheEnabled(true); 628 layout.setChildrenDrawingCacheEnabled(true); 629 } 630 } 631 632 void clearChildrenCache() { 633 final int screenCount = getChildCount(); 634 for (int i = 0; i < screenCount; i++) { 635 final CellLayout layout = (CellLayout) getChildAt(i); 636 layout.setChildrenDrawnWithCacheEnabled(false); 637 } 638 } 639 640 @Override 641 public boolean onTouchEvent(MotionEvent ev) { 642 if (mLauncher.isAllAppsVisible()) { 643 // Cancel any scrolling that is in progress. 644 if (!mScroller.isFinished()) { 645 mScroller.abortAnimation(); 646 } 647 snapToPage(mCurrentPage); 648 return false; // We don't want the events. Let them fall through to the all apps view. 649 } 650 651 return super.onTouchEvent(ev); 652 } 653 654 public boolean isSmall() { 655 return mIsSmall; 656 } 657 658 void shrinkToTop(boolean animated) { 659 shrink(ShrinkPosition.SHRINK_TO_TOP, animated); 660 } 661 662 void shrinkToMiddle() { 663 shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); 664 } 665 666 void shrinkToBottom() { 667 shrinkToBottom(true); 668 } 669 670 void shrinkToBottom(boolean animated) { 671 if (mFirstLayout) { 672 // (mFirstLayout == "first layout has not happened yet") 673 // if we get a call to shrink() as part of our initialization (for example, if 674 // Launcher is started in All Apps mode) then we need to wait for a layout call 675 // to get our width so we can layout the mini-screen views correctly 676 mWaitingToShrinkToBottom = true; 677 } else { 678 shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated); 679 } 680 } 681 682 private float getYScaleForScreen(int screen) { 683 int x = Math.abs(screen - 2); 684 685 // TODO: This should be generalized for use with arbitrary rotation angles. 686 switch(x) { 687 case 0: return EXTRA_SCALE_FACTOR_0; 688 case 1: return EXTRA_SCALE_FACTOR_1; 689 case 2: return EXTRA_SCALE_FACTOR_2; 690 } 691 return 1.0f; 692 } 693 694 // we use this to shrink the workspace for the all apps view and the customize view 695 private void shrink(ShrinkPosition shrinkPosition, boolean animated) { 696 mIsSmall = true; 697 // we intercept and reject all touch events when we're small, so be sure to reset the state 698 mTouchState = TOUCH_STATE_REST; 699 mActivePointerId = INVALID_POINTER; 700 701 final Resources res = getResources(); 702 final int screenWidth = getWidth(); 703 final int screenHeight = getHeight(); 704 705 // Making the assumption that all pages have the same width as the 0th 706 final int pageWidth = getChildAt(0).getMeasuredWidth(); 707 final int pageHeight = getChildAt(0).getMeasuredHeight(); 708 709 final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); 710 final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); 711 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); 712 713 final int screenCount = getChildCount(); 714 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; 715 716 float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); 717 if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) { 718 newY = screenHeight - newY - scaledPageHeight; 719 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { 720 newY = screenHeight / 2 - scaledPageHeight / 2; 721 } 722 723 // We animate all the screens to the centered position in workspace 724 // At the same time, the screens become greyed/dimmed 725 726 // newX is initialized to the left-most position of the centered screens 727 float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; 728 729 // We are going to scale about the center of the view, so we need to adjust the positions 730 // of the views accordingly 731 newX -= (pageWidth - scaledPageWidth) / 2.0f; 732 newY -= (pageHeight - scaledPageHeight) / 2.0f; 733 for (int i = 0; i < screenCount; i++) { 734 CellLayout cl = (CellLayout) getChildAt(i); 735 736 float rotation = (-i + 2) * WORKSPACE_ROTATION; 737 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); 738 float rotationScaleY = getYScaleForScreen(i); 739 740 if (animated) { 741 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 742 new ObjectAnimator<Float>(duration, cl, 743 new PropertyValuesHolder<Float>("x", newX), 744 new PropertyValuesHolder<Float>("y", newY), 745 new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX), 746 new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY), 747 new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f), 748 new PropertyValuesHolder<Float>("alpha", 1.0f), 749 new PropertyValuesHolder<Float>("rotationY", rotation)).start(); 750 } else { 751 cl.setX((int)newX); 752 cl.setY((int)newY); 753 cl.setScaleX(SHRINK_FACTOR * rotationScaleX); 754 cl.setScaleY(SHRINK_FACTOR * rotationScaleY); 755 cl.setBackgroundAlpha(1.0f); 756 cl.setAlpha(0.0f); 757 cl.setRotationY(rotation); 758 } 759 // increment newX for the next screen 760 newX += scaledPageWidth + extraScaledSpacing; 761 cl.setOnInterceptTouchListener(this); 762 } 763 setChildrenDrawnWithCacheEnabled(true); 764 } 765 766 // We call this when we trigger an unshrink by clicking on the CellLayout cl 767 public void unshrink(CellLayout clThatWasClicked) { 768 int newCurrentPage = mCurrentPage; 769 final int screenCount = getChildCount(); 770 for (int i = 0; i < screenCount; i++) { 771 if (getChildAt(i) == clThatWasClicked) { 772 newCurrentPage = i; 773 } 774 } 775 unshrink(newCurrentPage); 776 } 777 778 private void unshrink(int newCurrentPage) { 779 if (mIsSmall) { 780 int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 781 int delta = newX - mScrollX; 782 783 final int screenCount = getChildCount(); 784 for (int i = 0; i < screenCount; i++) { 785 CellLayout cl = (CellLayout) getChildAt(i); 786 cl.setX(cl.getX() + delta); 787 } 788 snapToPage(newCurrentPage); 789 unshrink(); 790 791 setCurrentPage(newCurrentPage); 792 } 793 } 794 795 void unshrink() { 796 unshrink(true); 797 } 798 799 void unshrink(boolean animated) { 800 if (mIsSmall) { 801 AnimatorSet s = new AnimatorSet(); 802 final int screenCount = getChildCount(); 803 804 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 805 for (int i = 0; i < screenCount; i++) { 806 final CellLayout cl = (CellLayout)getChildAt(i); 807 float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; 808 float rotation = 0.0f; 809 810 if (i < mCurrentPage) { 811 rotation = WORKSPACE_ROTATION; 812 } else if (i > mCurrentPage) { 813 rotation = -WORKSPACE_ROTATION; 814 } 815 816 if (animated) { 817 s.playTogether( 818 new ObjectAnimator<Float>(duration, cl, "translationX", 0.0f), 819 new ObjectAnimator<Float>(duration, cl, "translationY", 0.0f), 820 new ObjectAnimator<Float>(duration, cl, "scaleX", 1.0f), 821 new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f), 822 new ObjectAnimator<Float>(duration, cl, "backgroundAlpha", 0.0f), 823 new ObjectAnimator<Float>(duration, cl, "alpha", finalAlphaValue), 824 new ObjectAnimator<Float>(duration, cl, "rotationY", rotation)); 825 } else { 826 cl.setTranslationX(0.0f); 827 cl.setTranslationY(0.0f); 828 cl.setScaleX(1.0f); 829 cl.setScaleY(1.0f); 830 cl.setBackgroundAlpha(0.0f); 831 cl.setAlpha(finalAlphaValue); 832 cl.setRotationY(rotation); 833 } 834 } 835 s.addListener(mUnshrinkAnimationListener); 836 s.start(); 837 } 838 } 839 840 void startDrag(CellLayout.CellInfo cellInfo) { 841 View child = cellInfo.cell; 842 843 // Make sure the drag was started by a long press as opposed to a long click. 844 if (!child.isInTouchMode()) { 845 return; 846 } 847 848 mDragInfo = cellInfo; 849 mDragInfo.screen = mCurrentPage; 850 851 CellLayout current = ((CellLayout) getChildAt(mCurrentPage)); 852 853 current.onDragChild(child); 854 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 855 current.onDragEnter(child); 856 invalidate(); 857 } 858 859 void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, 860 boolean insertAtFirst, int intersectX, int intersectY) { 861 final CellLayout cellLayout = (CellLayout) getChildAt(screen); 862 View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); 863 864 final int[] cellXY = new int[2]; 865 cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 866 addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 867 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 868 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 869 cellXY[0], cellXY[1]); 870 } 871 872 873 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 874 DragView dragView, Object dragInfo) { 875 CellLayout cellLayout; 876 int originX = x - xOffset; 877 int originY = y - yOffset; 878 if (mIsSmall) { 879 cellLayout = findMatchingPageForDragOver(dragView, originX, originY); 880 if (cellLayout == null) { 881 // cancel the drag if we're not over a mini-screen at time of drop 882 // TODO: maybe add a nice fade here? 883 return; 884 } 885 // get originX and originY in the local coordinate system of the screen 886 mTempOriginXY[0] = originX; 887 mTempOriginXY[1] = originY; 888 mapPointGlobalToLocal(cellLayout, mTempOriginXY); 889 originX = (int)mTempOriginXY[0]; 890 originY = (int)mTempOriginXY[1]; 891 } else { 892 cellLayout = getCurrentDropLayout(); 893 } 894 895 if (source != this) { 896 onDropExternal(originX, originY, dragInfo, cellLayout); 897 } else { 898 // Move internally 899 if (mDragInfo != null) { 900 final View cell = mDragInfo.cell; 901 902 mTargetCell = findNearestVacantArea(originX, originY, 903 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 904 mTargetCell); 905 906 int screen = indexOfChild(cellLayout); 907 if (screen != mDragInfo.screen) { 908 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 909 originalCellLayout.removeView(cell); 910 addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], 911 mDragInfo.spanX, mDragInfo.spanY); 912 } 913 cellLayout.onDropChild(cell); 914 915 // update the item's position after drop 916 final ItemInfo info = (ItemInfo) cell.getTag(); 917 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 918 cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 919 lp.cellX = mTargetCell[0]; 920 lp.cellY = mTargetCell[1]; 921 cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, 922 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 923 924 LauncherModel.moveItemInDatabase(mLauncher, info, 925 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 926 lp.cellX, lp.cellY); 927 } 928 } 929 } 930 931 public void onDragEnter(DragSource source, int x, int y, int xOffset, 932 int yOffset, DragView dragView, Object dragInfo) { 933 getCurrentDropLayout().onDragEnter(dragView); 934 } 935 936 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 937 DragView dragView, Object dragInfo) { 938 939 if (mIsSmall) { 940 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 941 return null; 942 } 943 // We may need to delegate the drag to a child view. If a 1x1 item 944 // would land in a cell occupied by a DragTarget (e.g. a Folder), 945 // then drag events should be handled by that child. 946 947 ItemInfo item = (ItemInfo)dragInfo; 948 CellLayout currentLayout = getCurrentDropLayout(); 949 950 int dragPointX, dragPointY; 951 if (item.spanX == 1 && item.spanY == 1) { 952 // For a 1x1, calculate the drop cell exactly as in onDragOver 953 dragPointX = x - xOffset; 954 dragPointY = y - yOffset; 955 } else { 956 // Otherwise, use the exact drag coordinates 957 dragPointX = x; 958 dragPointY = y; 959 } 960 dragPointX += mScrollX - currentLayout.getLeft(); 961 dragPointY += mScrollY - currentLayout.getTop(); 962 963 // If we are dragging over a cell that contains a DropTarget that will 964 // accept the drop, delegate to that DropTarget. 965 final int[] cellXY = mTempCell; 966 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 967 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 968 if (child instanceof DropTarget) { 969 DropTarget target = (DropTarget)child; 970 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 971 return target; 972 } 973 } 974 return null; 975 } 976 977 private void mapPointGlobalToLocal(View v, float[] xy) { 978 xy[0] = xy[0] + mScrollX - v.getLeft(); 979 xy[1] = xy[1] + mScrollY - v.getTop(); 980 v.getMatrix().invert(mTempInverseMatrix); 981 mTempInverseMatrix.mapPoints(xy); 982 } 983 984 // xy = upper left corner of item being dragged 985 // bottomRightXy = lower right corner of item being dragged 986 // This method will see which mini-screen is most overlapped by the item being dragged, and 987 // return it. It will also transform the parameters xy and bottomRightXy into the local 988 // coordinate space of the returned screen 989 private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) { 990 float x = originX + dragView.getScaledDragRegionXOffset(); 991 float y = originY + dragView.getScaledDragRegionYOffset(); 992 float right = x + dragView.getScaledDragRegionWidth(); 993 float bottom = y + dragView.getScaledDragRegionHeight(); 994 995 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 996 // with the item being dragged. 997 final int screenCount = getChildCount(); 998 CellLayout bestMatchingScreen = null; 999 float smallestDistSoFar = Float.MAX_VALUE; 1000 final float[] xy = mTempDragCoordinates; 1001 final float[] bottomRightXy = mTempDragBottomRightCoordinates; 1002 for (int i = 0; i < screenCount; i++) { 1003 CellLayout cl = (CellLayout)getChildAt(i); 1004 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1005 float left = cl.getLeft(); 1006 float top = cl.getTop(); 1007 xy[0] = x + mScrollX - left; 1008 xy[1] = y + mScrollY - top; 1009 1010 bottomRightXy[0] = right + mScrollX - left; 1011 bottomRightXy[1] = bottom + mScrollY - top; 1012 1013 cl.getMatrix().invert(mTempInverseMatrix); 1014 mTempInverseMatrix.mapPoints(xy); 1015 mTempInverseMatrix.mapPoints(bottomRightXy); 1016 1017 float dragRegionX = xy[0]; 1018 float dragRegionY = xy[1]; 1019 float dragRegionRight = bottomRightXy[0]; 1020 float dragRegionBottom = bottomRightXy[1]; 1021 float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f; 1022 float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f; 1023 1024 // Find the overlapping region 1025 float overlapLeft = Math.max(0f, dragRegionX); 1026 float overlapTop = Math.max(0f, dragRegionY); 1027 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 1028 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 1029 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 1030 (overlapTop >= 0 && overlapBottom <= cl.getHeight())) { 1031 // Calculate the distance between the two centers 1032 float distX = dragRegionCenterX - cl.getWidth()/2; 1033 float distY = dragRegionCenterY - cl.getHeight()/2; 1034 float dist = distX * distX + distY * distY; 1035 1036 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 1037 1038 // Calculate the closest overlapping region 1039 if (overlap > 0 && dist < smallestDistSoFar) { 1040 smallestDistSoFar = dist; 1041 bestMatchingScreen = cl; 1042 } 1043 } 1044 } 1045 1046 if (bestMatchingScreen != mDragTargetLayout) { 1047 if (mDragTargetLayout != null) { 1048 mDragTargetLayout.onDragExit(); 1049 } 1050 mDragTargetLayout = bestMatchingScreen; 1051 } 1052 return bestMatchingScreen; 1053 } 1054 1055 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1056 DragView dragView, Object dragInfo) { 1057 CellLayout currentLayout; 1058 int originX = x - xOffset; 1059 int originY = y - yOffset; 1060 if (mIsSmall) { 1061 currentLayout = findMatchingPageForDragOver(dragView, originX, originY); 1062 1063 if (currentLayout == null) { 1064 return; 1065 } 1066 1067 currentLayout.setHover(true); 1068 // get originX and originY in the local coordinate system of the screen 1069 mTempOriginXY[0] = originX; 1070 mTempOriginXY[1] = originY; 1071 mapPointGlobalToLocal(currentLayout, mTempOriginXY); 1072 originX = (int)mTempOriginXY[0]; 1073 originY = (int)mTempOriginXY[1]; 1074 } else { 1075 currentLayout = getCurrentDropLayout(); 1076 } 1077 1078 final ItemInfo item = (ItemInfo)dragInfo; 1079 1080 if (dragInfo instanceof LauncherAppWidgetInfo) { 1081 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1082 1083 if (widgetInfo.spanX == -1) { 1084 // Calculate the grid spans needed to fit this widget 1085 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 1086 item.spanX = spans[0]; 1087 item.spanY = spans[1]; 1088 } 1089 } 1090 1091 if (source != this) { 1092 // This is a hack to fix the point used to determine which cell an icon from the all 1093 // apps screen is over 1094 if (item != null && item.spanX == 1 && currentLayout != null) { 1095 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 1096 1097 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1098 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 1099 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 1100 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 1101 } 1102 } 1103 } 1104 if (currentLayout != mDragTargetLayout) { 1105 if (mDragTargetLayout != null) { 1106 mDragTargetLayout.onDragExit(); 1107 currentLayout.onDragEnter(dragView); 1108 } 1109 mDragTargetLayout = currentLayout; 1110 } 1111 1112 // only visualize the drop locations for moving icons within the home screen on tablet 1113 // on phone, we also visualize icons dragged in from All Apps 1114 if ((!LauncherApplication.isScreenXLarge() || source == this) 1115 && mDragTargetLayout != null) { 1116 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1117 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 1118 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 1119 mDragTargetLayout.visualizeDropLocation( 1120 child, localOriginX, localOriginY, item.spanX, item.spanY); 1121 } 1122 } 1123 1124 public void onDragExit(DragSource source, int x, int y, int xOffset, 1125 int yOffset, DragView dragView, Object dragInfo) { 1126 if (mDragTargetLayout != null) { 1127 mDragTargetLayout.onDragExit(); 1128 mDragTargetLayout = null; 1129 } 1130 } 1131 1132 private void onDropExternal(int x, int y, Object dragInfo, 1133 CellLayout cellLayout) { 1134 onDropExternal(x, y, dragInfo, cellLayout, false); 1135 } 1136 1137 /** 1138 * Add the item specified by dragInfo to the given layout. 1139 * This is basically the equivalent of onDropExternal, except it's not initiated 1140 * by drag and drop. 1141 * @return true if successful 1142 */ 1143 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1144 CellLayout cl = (CellLayout) layout; 1145 ItemInfo info = (ItemInfo) dragInfo; 1146 1147 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1148 onDropExternal(0, 0, dragInfo, cl, false); 1149 return true; 1150 } 1151 mLauncher.showOutOfSpaceMessage(); 1152 return false; 1153 } 1154 1155 // Drag from somewhere else 1156 private void onDropExternal(int x, int y, Object dragInfo, 1157 CellLayout cellLayout, boolean insertAtFirst) { 1158 int screen = indexOfChild(cellLayout); 1159 if (dragInfo instanceof PendingAddItemInfo) { 1160 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1161 // When dragging and dropping from customization tray, we deal with creating 1162 // widgets/shortcuts/folders in a slightly different way 1163 int[] touchXY = new int[2]; 1164 touchXY[0] = x; 1165 touchXY[1] = y; 1166 switch (info.itemType) { 1167 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1168 mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); 1169 break; 1170 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1171 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 1172 break; 1173 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1174 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 1175 break; 1176 default: 1177 throw new IllegalStateException("Unknown item type: " + info.itemType); 1178 } 1179 cellLayout.onDragExit(); 1180 return; 1181 } 1182 1183 // This is for other drag/drop cases, like dragging from All Apps 1184 ItemInfo info = (ItemInfo) dragInfo; 1185 1186 View view = null; 1187 1188 switch (info.itemType) { 1189 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1190 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1191 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1192 // Came from all apps -- make a copy 1193 info = new ShortcutInfo((ApplicationInfo) info); 1194 } 1195 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1196 (ShortcutInfo) info); 1197 break; 1198 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1199 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1200 cellLayout, ((UserFolderInfo) info)); 1201 break; 1202 default: 1203 throw new IllegalStateException("Unknown item type: " + info.itemType); 1204 } 1205 1206 // If the view is null, it has already been added. 1207 if (view == null) { 1208 cellLayout.onDragExit(); 1209 } else { 1210 mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell); 1211 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1212 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1213 cellLayout.onDropChild(view); 1214 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1215 1216 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1217 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1218 lp.cellX, lp.cellY); 1219 } 1220 } 1221 1222 /** 1223 * Return the current {@link CellLayout}, correctly picking the destination 1224 * screen while a scroll is in progress. 1225 */ 1226 private CellLayout getCurrentDropLayout() { 1227 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 1228 return (CellLayout) getChildAt(index); 1229 } 1230 1231 /** 1232 * Return the current CellInfo describing our current drag; this method exists 1233 * so that Launcher can sync this object with the correct info when the activity is created/ 1234 * destroyed 1235 * 1236 */ 1237 public CellLayout.CellInfo getDragInfo() { 1238 return mDragInfo; 1239 } 1240 1241 /** 1242 * {@inheritDoc} 1243 */ 1244 public boolean acceptDrop(DragSource source, int x, int y, 1245 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1246 CellLayout layout; 1247 if (mIsSmall) { 1248 layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset); 1249 if (layout == null) { 1250 // cancel the drag if we're not over a mini-screen at time of drop 1251 return false; 1252 } 1253 } else { 1254 layout = getCurrentDropLayout(); 1255 } 1256 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1257 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1258 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1259 1260 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1261 1262 if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1263 return true; 1264 } else { 1265 mLauncher.showOutOfSpaceMessage(); 1266 return false; 1267 } 1268 } 1269 1270 /** 1271 * Calculate the nearest cell where the given object would be dropped. 1272 */ 1273 private int[] findNearestVacantArea(int pixelX, int pixelY, 1274 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1275 1276 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 1277 int localPixelY = pixelY - (layout.getTop() - mScrollY); 1278 1279 // Find the best target drop location 1280 return layout.findNearestVacantArea( 1281 localPixelX, localPixelY, spanX, spanY, ignoreView, recycle); 1282 } 1283 1284 /** 1285 * Estimate the size that a child with the given dimensions will take in the current screen. 1286 */ 1287 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1288 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 1289 } 1290 1291 void setLauncher(Launcher launcher) { 1292 mLauncher = launcher; 1293 } 1294 1295 public void setDragController(DragController dragController) { 1296 mDragController = dragController; 1297 } 1298 1299 public void onDropCompleted(View target, boolean success) { 1300 if (success) { 1301 if (target != this && mDragInfo != null) { 1302 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1303 cellLayout.removeView(mDragInfo.cell); 1304 if (mDragInfo.cell instanceof DropTarget) { 1305 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1306 } 1307 // final Object tag = mDragInfo.cell.getTag(); 1308 } 1309 } else { 1310 if (mDragInfo != null) { 1311 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1312 cellLayout.onDropAborted(mDragInfo.cell); 1313 } 1314 } 1315 1316 mDragInfo = null; 1317 } 1318 1319 public boolean isDropEnabled() { 1320 return true; 1321 } 1322 1323 @Override 1324 protected void onRestoreInstanceState(Parcelable state) { 1325 super.onRestoreInstanceState(state); 1326 Launcher.setScreen(mCurrentPage); 1327 } 1328 1329 @Override 1330 public void scrollLeft() { 1331 if (!mIsSmall) { 1332 super.scrollLeft(); 1333 } 1334 } 1335 1336 @Override 1337 public void scrollRight() { 1338 if (!mIsSmall) { 1339 super.scrollRight(); 1340 } 1341 } 1342 1343 public Folder getFolderForTag(Object tag) { 1344 final int screenCount = getChildCount(); 1345 for (int screen = 0; screen < screenCount; screen++) { 1346 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1347 int count = currentScreen.getChildCount(); 1348 for (int i = 0; i < count; i++) { 1349 View child = currentScreen.getChildAt(i); 1350 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1351 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1352 Folder f = (Folder) child; 1353 if (f.getInfo() == tag && f.getInfo().opened) { 1354 return f; 1355 } 1356 } 1357 } 1358 } 1359 return null; 1360 } 1361 1362 public View getViewForTag(Object tag) { 1363 int screenCount = getChildCount(); 1364 for (int screen = 0; screen < screenCount; screen++) { 1365 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1366 int count = currentScreen.getChildCount(); 1367 for (int i = 0; i < count; i++) { 1368 View child = currentScreen.getChildAt(i); 1369 if (child.getTag() == tag) { 1370 return child; 1371 } 1372 } 1373 } 1374 return null; 1375 } 1376 1377 1378 void removeItems(final ArrayList<ApplicationInfo> apps) { 1379 final int screenCount = getChildCount(); 1380 final PackageManager manager = getContext().getPackageManager(); 1381 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1382 1383 final HashSet<String> packageNames = new HashSet<String>(); 1384 final int appCount = apps.size(); 1385 for (int i = 0; i < appCount; i++) { 1386 packageNames.add(apps.get(i).componentName.getPackageName()); 1387 } 1388 1389 for (int i = 0; i < screenCount; i++) { 1390 final CellLayout layout = (CellLayout) getChildAt(i); 1391 1392 // Avoid ANRs by treating each screen separately 1393 post(new Runnable() { 1394 public void run() { 1395 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1396 childrenToRemove.clear(); 1397 1398 int childCount = layout.getChildCount(); 1399 for (int j = 0; j < childCount; j++) { 1400 final View view = layout.getChildAt(j); 1401 Object tag = view.getTag(); 1402 1403 if (tag instanceof ShortcutInfo) { 1404 final ShortcutInfo info = (ShortcutInfo) tag; 1405 final Intent intent = info.intent; 1406 final ComponentName name = intent.getComponent(); 1407 1408 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1409 for (String packageName: packageNames) { 1410 if (packageName.equals(name.getPackageName())) { 1411 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1412 childrenToRemove.add(view); 1413 } 1414 } 1415 } 1416 } else if (tag instanceof UserFolderInfo) { 1417 final UserFolderInfo info = (UserFolderInfo) tag; 1418 final ArrayList<ShortcutInfo> contents = info.contents; 1419 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1420 final int contentsCount = contents.size(); 1421 boolean removedFromFolder = false; 1422 1423 for (int k = 0; k < contentsCount; k++) { 1424 final ShortcutInfo appInfo = contents.get(k); 1425 final Intent intent = appInfo.intent; 1426 final ComponentName name = intent.getComponent(); 1427 1428 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1429 for (String packageName: packageNames) { 1430 if (packageName.equals(name.getPackageName())) { 1431 toRemove.add(appInfo); 1432 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1433 removedFromFolder = true; 1434 } 1435 } 1436 } 1437 } 1438 1439 contents.removeAll(toRemove); 1440 if (removedFromFolder) { 1441 final Folder folder = getOpenFolder(); 1442 if (folder != null) 1443 folder.notifyDataSetChanged(); 1444 } 1445 } else if (tag instanceof LiveFolderInfo) { 1446 final LiveFolderInfo info = (LiveFolderInfo) tag; 1447 final Uri uri = info.uri; 1448 final ProviderInfo providerInfo = manager.resolveContentProvider( 1449 uri.getAuthority(), 0); 1450 1451 if (providerInfo != null) { 1452 for (String packageName: packageNames) { 1453 if (packageName.equals(providerInfo.packageName)) { 1454 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1455 childrenToRemove.add(view); 1456 } 1457 } 1458 } 1459 } else if (tag instanceof LauncherAppWidgetInfo) { 1460 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1461 final AppWidgetProviderInfo provider = 1462 widgets.getAppWidgetInfo(info.appWidgetId); 1463 if (provider != null) { 1464 for (String packageName: packageNames) { 1465 if (packageName.equals(provider.provider.getPackageName())) { 1466 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1467 childrenToRemove.add(view); 1468 } 1469 } 1470 } 1471 } 1472 } 1473 1474 childCount = childrenToRemove.size(); 1475 for (int j = 0; j < childCount; j++) { 1476 View child = childrenToRemove.get(j); 1477 layout.removeViewInLayout(child); 1478 if (child instanceof DropTarget) { 1479 mDragController.removeDropTarget((DropTarget)child); 1480 } 1481 } 1482 1483 if (childCount > 0) { 1484 layout.requestLayout(); 1485 layout.invalidate(); 1486 } 1487 } 1488 }); 1489 } 1490 } 1491 1492 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1493 final int screenCount = getChildCount(); 1494 for (int i = 0; i < screenCount; i++) { 1495 final CellLayout layout = (CellLayout) getChildAt(i); 1496 int childCount = layout.getChildCount(); 1497 for (int j = 0; j < childCount; j++) { 1498 final View view = layout.getChildAt(j); 1499 Object tag = view.getTag(); 1500 if (tag instanceof ShortcutInfo) { 1501 ShortcutInfo info = (ShortcutInfo)tag; 1502 // We need to check for ACTION_MAIN otherwise getComponent() might 1503 // return null for some shortcuts (for instance, for shortcuts to 1504 // web pages.) 1505 final Intent intent = info.intent; 1506 final ComponentName name = intent.getComponent(); 1507 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1508 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1509 final int appCount = apps.size(); 1510 for (int k = 0; k < appCount; k++) { 1511 ApplicationInfo app = apps.get(k); 1512 if (app.componentName.equals(name)) { 1513 info.setIcon(mIconCache.getIcon(info.intent)); 1514 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1515 new FastBitmapDrawable(info.getIcon(mIconCache)), 1516 null, null); 1517 } 1518 } 1519 } 1520 } 1521 } 1522 } 1523 } 1524 1525 void moveToDefaultScreen(boolean animate) { 1526 if (animate) { 1527 if (mIsSmall) { 1528 unshrink(mDefaultPage); 1529 } else { 1530 snapToPage(mDefaultPage); 1531 } 1532 } else { 1533 setCurrentPage(mDefaultPage); 1534 } 1535 getChildAt(mDefaultPage).requestFocus(); 1536 } 1537 1538 void setIndicators(Drawable previous, Drawable next) { 1539 mPreviousIndicator = previous; 1540 mNextIndicator = next; 1541 previous.setLevel(mCurrentPage); 1542 next.setLevel(mCurrentPage); 1543 } 1544 1545 @Override 1546 public void syncPages() { 1547 } 1548 1549 @Override 1550 public void syncPageItems(int page) { 1551 } 1552 1553} 1554