Workspace.java revision 1b0aaac0b3abd777ed319341f95a8dfff23c79f4
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 java.util.ArrayList; 20import java.util.HashSet; 21 22import android.animation.Animator; 23import android.animation.Animator.AnimatorListener; 24import android.animation.AnimatorListenerAdapter; 25import android.animation.AnimatorSet; 26import android.animation.ObjectAnimator; 27import android.animation.PropertyValuesHolder; 28import android.app.WallpaperManager; 29import android.appwidget.AppWidgetManager; 30import android.appwidget.AppWidgetProviderInfo; 31import android.content.ClipData; 32import android.content.ClipDescription; 33import android.content.ComponentName; 34import android.content.Context; 35import android.content.Intent; 36import android.content.pm.PackageManager; 37import android.content.pm.ProviderInfo; 38import android.content.res.Resources; 39import android.content.res.TypedArray; 40import android.graphics.Bitmap; 41import android.graphics.Canvas; 42import android.graphics.Color; 43import android.graphics.Matrix; 44import android.graphics.Paint; 45import android.graphics.Rect; 46import android.graphics.RectF; 47import android.graphics.Region.Op; 48import android.graphics.drawable.Drawable; 49import android.net.Uri; 50import android.os.IBinder; 51import android.os.Parcelable; 52import android.util.AttributeSet; 53import android.util.Log; 54import android.view.DragEvent; 55import android.view.MotionEvent; 56import android.view.View; 57import android.widget.TextView; 58import android.widget.Toast; 59 60import com.android.launcher.R; 61 62/** 63 * The workspace is a wide area with a wallpaper and a finite number of pages. 64 * Each page contains a number of icons, folders or widgets the user can 65 * interact with. A workspace is meant to be used with a fixed width only. 66 */ 67public class Workspace extends SmoothPagedView 68 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 69 @SuppressWarnings({"UnusedDeclaration"}) 70 private static final String TAG = "Launcher.Workspace"; 71 72 // This is how much the workspace shrinks when we enter all apps or 73 // customization mode 74 private static final float SHRINK_FACTOR = 0.16f; 75 76 // Y rotation to apply to the workspace screens 77 private static final float WORKSPACE_ROTATION = 12.5f; 78 79 // These are extra scale factors to apply to the mini home screens 80 // so as to achieve the desired transform 81 private static final float EXTRA_SCALE_FACTOR_0 = 0.972f; 82 private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; 83 private static final float EXTRA_SCALE_FACTOR_2 = 1.10f; 84 85 private static final int BACKGROUND_FADE_OUT_DELAY = 300; 86 private static final int BACKGROUND_FADE_OUT_DURATION = 300; 87 private static final int BACKGROUND_FADE_IN_DURATION = 100; 88 89 // These animators are used to fade the background 90 private ObjectAnimator mBackgroundFadeInAnimation; 91 private ObjectAnimator mBackgroundFadeOutAnimation; 92 private float mBackgroundAlpha = 0; 93 94 private final WallpaperManager mWallpaperManager; 95 96 private int mDefaultPage; 97 98 private boolean mPageMoving = false; 99 100 /** 101 * CellInfo for the cell that is currently being dragged 102 */ 103 private CellLayout.CellInfo mDragInfo; 104 105 /** 106 * Target drop area calculated during last acceptDrop call. 107 */ 108 private int[] mTargetCell = null; 109 110 /** 111 * The CellLayout that is currently being dragged over 112 */ 113 private CellLayout mDragTargetLayout = null; 114 115 private Launcher mLauncher; 116 private IconCache mIconCache; 117 private DragController mDragController; 118 119 // These are temporary variables to prevent having to allocate a new object just to 120 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 121 private int[] mTempCell = new int[2]; 122 private int[] mTempEstimate = new int[2]; 123 private float[] mTempOriginXY = new float[2]; 124 private float[] mTempDragCoordinates = new float[2]; 125 private float[] mTempTouchCoordinates = new float[2]; 126 private float[] mTempCellLayoutCenterCoordinates = new float[2]; 127 private float[] mTempDragBottomRightCoordinates = new float[2]; 128 private Matrix mTempInverseMatrix = new Matrix(); 129 130 private static final int DEFAULT_CELL_COUNT_X = 4; 131 private static final int DEFAULT_CELL_COUNT_Y = 4; 132 133 private Drawable mPreviousIndicator; 134 private Drawable mNextIndicator; 135 136 // State variable that indicates whether the pages are small (ie when you're 137 // in all apps or customize mode) 138 private boolean mIsSmall = false; 139 private boolean mIsInUnshrinkAnimation = false; 140 private AnimatorListener mUnshrinkAnimationListener; 141 private enum ShrinkPosition { 142 SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE }; 143 private ShrinkPosition mShrunkenState; 144 private boolean mWaitingToShrink = false; 145 private ShrinkPosition mWaitingToShrinkPosition; 146 147 private boolean mInScrollArea = false; 148 private boolean mInDragMode = false; 149 150 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper(); 151 private Bitmap mDragOutline = null; 152 private final Rect mTempRect = new Rect(); 153 private final int[] mTempXY = new int[2]; 154 155 // Paint used to draw external drop outline 156 private final Paint mExternalDragOutlinePaint = new Paint(); 157 158 /** 159 * Used to inflate the Workspace from XML. 160 * 161 * @param context The application's context. 162 * @param attrs The attributes set containing the Workspace's customization values. 163 */ 164 public Workspace(Context context, AttributeSet attrs) { 165 this(context, attrs, 0); 166 } 167 168 /** 169 * Used to inflate the Workspace from XML. 170 * 171 * @param context The application's context. 172 * @param attrs The attributes set containing the Workspace's customization values. 173 * @param defStyle Unused. 174 */ 175 public Workspace(Context context, AttributeSet attrs, int defStyle) { 176 super(context, attrs, defStyle); 177 mContentIsRefreshable = false; 178 179 if (!LauncherApplication.isScreenXLarge()) { 180 mFadeInAdjacentScreens = false; 181 } 182 183 mWallpaperManager = WallpaperManager.getInstance(context); 184 185 TypedArray a = context.obtainStyledAttributes(attrs, 186 R.styleable.Workspace, defStyle, 0); 187 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 188 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 189 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); 190 a.recycle(); 191 192 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 193 setHapticFeedbackEnabled(false); 194 195 initWorkspace(); 196 } 197 198 /** 199 * Initializes various states for this workspace. 200 */ 201 protected void initWorkspace() { 202 Context context = getContext(); 203 mCurrentPage = mDefaultPage; 204 Launcher.setScreen(mCurrentPage); 205 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 206 mIconCache = app.getIconCache(); 207 mExternalDragOutlinePaint.setAntiAlias(true); 208 209 mUnshrinkAnimationListener = new AnimatorListenerAdapter() { 210 public void onAnimationStart(Animator animation) { 211 mIsInUnshrinkAnimation = true; 212 } 213 public void onAnimationEnd(Animator animation) { 214 mIsInUnshrinkAnimation = false; 215 } 216 }; 217 mSnapVelocity = 600; 218 } 219 220 @Override 221 protected int getScrollMode() { 222 if (LauncherApplication.isScreenXLarge()) { 223 return SmoothPagedView.QUINTIC_MODE; 224 } else { 225 return SmoothPagedView.OVERSHOOT_MODE; 226 } 227 } 228 229 @Override 230 public void addView(View child, int index, LayoutParams params) { 231 if (!(child instanceof CellLayout)) { 232 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 233 } 234 ((CellLayout) child).setOnInterceptTouchListener(this); 235 super.addView(child, index, params); 236 } 237 238 @Override 239 public void addView(View child) { 240 if (!(child instanceof CellLayout)) { 241 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 242 } 243 ((CellLayout) child).setOnInterceptTouchListener(this); 244 super.addView(child); 245 } 246 247 @Override 248 public void addView(View child, int index) { 249 if (!(child instanceof CellLayout)) { 250 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 251 } 252 ((CellLayout) child).setOnInterceptTouchListener(this); 253 super.addView(child, index); 254 } 255 256 @Override 257 public void addView(View child, int width, int height) { 258 if (!(child instanceof CellLayout)) { 259 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 260 } 261 ((CellLayout) child).setOnInterceptTouchListener(this); 262 super.addView(child, width, height); 263 } 264 265 @Override 266 public void addView(View child, LayoutParams params) { 267 if (!(child instanceof CellLayout)) { 268 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 269 } 270 ((CellLayout) child).setOnInterceptTouchListener(this); 271 super.addView(child, params); 272 } 273 274 /** 275 * @return The open folder on the current screen, or null if there is none 276 */ 277 Folder getOpenFolder() { 278 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 279 int count = currentPage.getChildCount(); 280 for (int i = 0; i < count; i++) { 281 View child = currentPage.getChildAt(i); 282 if (child instanceof Folder) { 283 Folder folder = (Folder) child; 284 if (folder.getInfo().opened) 285 return folder; 286 } 287 } 288 return null; 289 } 290 291 ArrayList<Folder> getOpenFolders() { 292 final int screenCount = getChildCount(); 293 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 294 295 for (int screen = 0; screen < screenCount; screen++) { 296 CellLayout currentPage = (CellLayout) getChildAt(screen); 297 int count = currentPage.getChildCount(); 298 for (int i = 0; i < count; i++) { 299 View child = currentPage.getChildAt(i); 300 if (child instanceof Folder) { 301 Folder folder = (Folder) child; 302 if (folder.getInfo().opened) 303 folders.add(folder); 304 break; 305 } 306 } 307 } 308 return folders; 309 } 310 311 boolean isDefaultPageShowing() { 312 return mCurrentPage == mDefaultPage; 313 } 314 315 /** 316 * Sets the current screen. 317 * 318 * @param currentPage 319 */ 320 @Override 321 void setCurrentPage(int currentPage) { 322 super.setCurrentPage(currentPage); 323 updateWallpaperOffset(mScrollX); 324 } 325 326 /** 327 * Adds the specified child in the specified screen. The position and dimension of 328 * the child are defined by x, y, spanX and spanY. 329 * 330 * @param child The child to add in one of the workspace's screens. 331 * @param screen The screen in which to add the child. 332 * @param x The X position of the child in the screen's grid. 333 * @param y The Y position of the child in the screen's grid. 334 * @param spanX The number of cells spanned horizontally by the child. 335 * @param spanY The number of cells spanned vertically by the child. 336 */ 337 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 338 addInScreen(child, screen, x, y, spanX, spanY, false); 339 } 340 341 void addInFullScreen(View child, int screen) { 342 addInScreen(child, screen, 0, 0, -1, -1); 343 } 344 345 /** 346 * Adds the specified child in the specified screen. The position and dimension of 347 * the child are defined by x, y, spanX and spanY. 348 * 349 * @param child The child to add in one of the workspace's screens. 350 * @param screen The screen in which to add the child. 351 * @param x The X position of the child in the screen's grid. 352 * @param y The Y position of the child in the screen's grid. 353 * @param spanX The number of cells spanned horizontally by the child. 354 * @param spanY The number of cells spanned vertically by the child. 355 * @param insert When true, the child is inserted at the beginning of the children list. 356 */ 357 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 358 if (screen < 0 || screen >= getChildCount()) { 359 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 360 + " (was " + screen + "); skipping child"); 361 return; 362 } 363 364 final CellLayout group = (CellLayout) getChildAt(screen); 365 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 366 if (lp == null) { 367 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 368 } else { 369 lp.cellX = x; 370 lp.cellY = y; 371 lp.cellHSpan = spanX; 372 lp.cellVSpan = spanY; 373 } 374 375 // Get the canonical child id to uniquely represent this view in this screen 376 int childId = LauncherModel.getCellLayoutChildId(-1, screen, x, y, spanX, spanY); 377 boolean markCellsAsOccupied = !(child instanceof Folder); 378 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) { 379 // TODO: This branch occurs when the workspace is adding views 380 // outside of the defined grid 381 // maybe we should be deleting these items from the LauncherModel? 382 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 383 } 384 385 if (!(child instanceof Folder)) { 386 child.setHapticFeedbackEnabled(false); 387 child.setOnLongClickListener(mLongClickListener); 388 } 389 if (child instanceof DropTarget) { 390 mDragController.addDropTarget((DropTarget) child); 391 } 392 } 393 394 public boolean onTouch(View v, MotionEvent event) { 395 // this is an intercepted event being forwarded from a cell layout 396 if (mIsSmall || mIsInUnshrinkAnimation) { 397 mLauncher.onWorkspaceClick((CellLayout) v); 398 return true; 399 } else if (!mPageMoving) { 400 if (v == getChildAt(mCurrentPage - 1)) { 401 snapToPage(mCurrentPage - 1); 402 return true; 403 } else if (v == getChildAt(mCurrentPage + 1)) { 404 snapToPage(mCurrentPage + 1); 405 return true; 406 } 407 } 408 return false; 409 } 410 411 @Override 412 public boolean dispatchUnhandledMove(View focused, int direction) { 413 if (mIsSmall || mIsInUnshrinkAnimation) { 414 // when the home screens are shrunken, shouldn't allow side-scrolling 415 return false; 416 } 417 return super.dispatchUnhandledMove(focused, direction); 418 } 419 420 @Override 421 public boolean onInterceptTouchEvent(MotionEvent ev) { 422 if (mIsSmall || mIsInUnshrinkAnimation) { 423 // when the home screens are shrunken, shouldn't allow side-scrolling 424 return false; 425 } 426 return super.onInterceptTouchEvent(ev); 427 } 428 429 @Override 430 protected void determineScrollingStart(MotionEvent ev) { 431 if (!mIsSmall && !mIsInUnshrinkAnimation) super.determineScrollingStart(ev); 432 } 433 434 protected void onPageBeginMoving() { 435 if (mNextPage != INVALID_PAGE) { 436 // we're snapping to a particular screen 437 enableChildrenCache(mCurrentPage, mNextPage); 438 } else { 439 // this is when user is actively dragging a particular screen, they might 440 // swipe it either left or right (but we won't advance by more than one screen) 441 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); 442 } 443 showOutlines(); 444 mPageMoving = true; 445 } 446 447 protected void onPageEndMoving() { 448 clearChildrenCache(); 449 // Hide the outlines, as long as we're not dragging 450 if (!mDragController.dragging()) { 451 hideOutlines(); 452 } 453 mPageMoving = false; 454 } 455 456 @Override 457 protected void notifyPageSwitchListener() { 458 super.notifyPageSwitchListener(); 459 460 if (mPreviousIndicator != null) { 461 // if we know the next page, we show the indication for it right away; it looks 462 // weird if the indicators are lagging 463 int page = mNextPage; 464 if (page == INVALID_PAGE) { 465 page = mCurrentPage; 466 } 467 mPreviousIndicator.setLevel(page); 468 mNextIndicator.setLevel(page); 469 } 470 Launcher.setScreen(mCurrentPage); 471 }; 472 473 private void updateWallpaperOffset() { 474 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 475 } 476 477 private void updateWallpaperOffset(int scrollRange) { 478 final boolean isStaticWallpaper = (mWallpaperManager != null) && 479 (mWallpaperManager.getWallpaperInfo() == null); 480 if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { 481 IBinder token = getWindowToken(); 482 if (token != null) { 483 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 484 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 485 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 486 } 487 } 488 } 489 490 public void showOutlines() { 491 if (!mIsSmall && !mIsInUnshrinkAnimation) { 492 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 493 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 494 mBackgroundFadeInAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 1.0f); 495 mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION); 496 mBackgroundFadeInAnimation.start(); 497 } 498 } 499 500 public void hideOutlines() { 501 if (!mIsSmall && !mIsInUnshrinkAnimation) { 502 if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel(); 503 if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel(); 504 mBackgroundFadeOutAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 0.0f); 505 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); 506 mBackgroundFadeOutAnimation.setStartDelay(BACKGROUND_FADE_OUT_DELAY); 507 mBackgroundFadeOutAnimation.start(); 508 } 509 } 510 511 public void setBackgroundAlpha(float alpha) { 512 mBackgroundAlpha = alpha; 513 for (int i = 0; i < getChildCount(); i++) { 514 CellLayout cl = (CellLayout) getChildAt(i); 515 cl.setBackgroundAlpha(alpha); 516 } 517 } 518 519 public float getBackgroundAlpha() { 520 return mBackgroundAlpha; 521 } 522 523 @Override 524 protected void screenScrolled(int screenCenter) { 525 final int halfScreenSize = getMeasuredWidth() / 2; 526 for (int i = 0; i < getChildCount(); i++) { 527 CellLayout cl = (CellLayout) getChildAt(i); 528 if (cl != null) { 529 int totalDistance = cl.getMeasuredWidth() + mPageSpacing; 530 int delta = screenCenter - (getChildOffset(i) - 531 getRelativeChildOffset(i) + halfScreenSize); 532 533 float scrollProgress = delta/(totalDistance*1.0f); 534 scrollProgress = Math.min(scrollProgress, 1.0f); 535 scrollProgress = Math.max(scrollProgress, -1.0f); 536 537 float mult = mInDragMode ? 1.0f : Math.abs(scrollProgress); 538 cl.setBackgroundAlphaMultiplier(mult); 539 540 float rotation = WORKSPACE_ROTATION * scrollProgress; 541 cl.setRotationY(rotation); 542 } 543 } 544 } 545 546 protected void onAttachedToWindow() { 547 super.onAttachedToWindow(); 548 computeScroll(); 549 mDragController.setWindowToken(getWindowToken()); 550 } 551 552 @Override 553 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 554 super.onLayout(changed, left, top, right, bottom); 555 556 // if shrinkToBottom() is called on initialization, it has to be deferred 557 // until after the first call to onLayout so that it has the correct width 558 if (mWaitingToShrink) { 559 shrink(mWaitingToShrinkPosition, false); 560 mWaitingToShrink = false; 561 } 562 563 if (LauncherApplication.isInPlaceRotationEnabled()) { 564 // When the device is rotated, the scroll position of the current screen 565 // needs to be refreshed 566 setCurrentPage(getCurrentPage()); 567 } 568 } 569 570 @Override 571 protected void dispatchDraw(Canvas canvas) { 572 if (mIsSmall || mIsInUnshrinkAnimation) { 573 // Draw all the workspaces if we're small 574 final int pageCount = getChildCount(); 575 final long drawingTime = getDrawingTime(); 576 for (int i = 0; i < pageCount; i++) { 577 final View page = (View) getChildAt(i); 578 579 drawChild(canvas, page, drawingTime); 580 } 581 } else { 582 super.dispatchDraw(canvas); 583 } 584 } 585 586 @Override 587 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 588 if (!mLauncher.isAllAppsVisible()) { 589 final Folder openFolder = getOpenFolder(); 590 if (openFolder != null) { 591 return openFolder.requestFocus(direction, previouslyFocusedRect); 592 } else { 593 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 594 } 595 } 596 return false; 597 } 598 599 @Override 600 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 601 if (!mLauncher.isAllAppsVisible()) { 602 final Folder openFolder = getOpenFolder(); 603 if (openFolder != null) { 604 openFolder.addFocusables(views, direction); 605 } else { 606 super.addFocusables(views, direction, focusableMode); 607 } 608 } 609 } 610 611 @Override 612 public boolean dispatchTouchEvent(MotionEvent ev) { 613 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 614 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 615 // ie when you click on a mini-screen, it zooms back to that screen) 616 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 617 return false; 618 } 619 } 620 return super.dispatchTouchEvent(ev); 621 } 622 623 void enableChildrenCache(int fromPage, int toPage) { 624 if (fromPage > toPage) { 625 final int temp = fromPage; 626 fromPage = toPage; 627 toPage = temp; 628 } 629 630 final int screenCount = getChildCount(); 631 632 fromPage = Math.max(fromPage, 0); 633 toPage = Math.min(toPage, screenCount - 1); 634 635 for (int i = fromPage; i <= toPage; i++) { 636 final CellLayout layout = (CellLayout) getChildAt(i); 637 layout.setChildrenDrawnWithCacheEnabled(true); 638 layout.setChildrenDrawingCacheEnabled(true); 639 } 640 } 641 642 void clearChildrenCache() { 643 final int screenCount = getChildCount(); 644 for (int i = 0; i < screenCount; i++) { 645 final CellLayout layout = (CellLayout) getChildAt(i); 646 layout.setChildrenDrawnWithCacheEnabled(false); 647 } 648 } 649 650 @Override 651 public boolean onTouchEvent(MotionEvent ev) { 652 if (mLauncher.isAllAppsVisible()) { 653 // Cancel any scrolling that is in progress. 654 if (!mScroller.isFinished()) { 655 mScroller.abortAnimation(); 656 } 657 setCurrentPage(mCurrentPage); 658 return false; // We don't want the events. Let them fall through to the all apps view. 659 } 660 661 return super.onTouchEvent(ev); 662 } 663 664 public boolean isSmall() { 665 return mIsSmall; 666 } 667 668 void shrinkToTop(boolean animated) { 669 shrink(ShrinkPosition.SHRINK_TO_TOP, animated); 670 } 671 672 void shrinkToMiddle() { 673 shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true); 674 } 675 676 void shrinkToBottom() { 677 shrinkToBottom(true); 678 } 679 680 void shrinkToBottom(boolean animated) { 681 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated); 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 if (mFirstLayout) { 699 // (mFirstLayout == "first layout has not happened yet") 700 // if we get a call to shrink() as part of our initialization (for example, if 701 // Launcher is started in All Apps mode) then we need to wait for a layout call 702 // to get our width so we can layout the mini-screen views correctly 703 mWaitingToShrink = true; 704 mWaitingToShrinkPosition = shrinkPosition; 705 return; 706 } 707 mIsSmall = true; 708 mShrunkenState = shrinkPosition; 709 710 // Stop any scrolling, move to the current page right away 711 setCurrentPage(mCurrentPage); 712 updateWhichPagesAcceptDrops(mShrunkenState); 713 714 // we intercept and reject all touch events when we're small, so be sure to reset the state 715 mTouchState = TOUCH_STATE_REST; 716 mActivePointerId = INVALID_POINTER; 717 718 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 719 currentPage.setBackgroundAlphaMultiplier(1.0f); 720 721 final Resources res = getResources(); 722 final int screenWidth = getWidth(); 723 final int screenHeight = getHeight(); 724 725 // Making the assumption that all pages have the same width as the 0th 726 final int pageWidth = getChildAt(0).getMeasuredWidth(); 727 final int pageHeight = getChildAt(0).getMeasuredHeight(); 728 729 final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth); 730 final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight); 731 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing); 732 733 final int screenCount = getChildCount(); 734 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing; 735 736 boolean isPortrait = getMeasuredHeight() > getMeasuredWidth(); 737 float newY = (isPortrait ? 738 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginPortrait) : 739 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginLandscape)); 740 float finalAlpha = 1.0f; 741 float extraShrinkFactor = 1.0f; 742 if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) { 743 newY = screenHeight - newY - scaledPageHeight; 744 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 745 746 // We shrink and disappear to nothing in the case of all apps 747 // (which is when we shrink to the bottom) 748 newY = screenHeight - newY - scaledPageHeight; 749 finalAlpha = 0.0f; 750 extraShrinkFactor = 0.1f; 751 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) { 752 newY = screenHeight / 2 - scaledPageHeight / 2; 753 finalAlpha = 1.0f; 754 } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) { 755 newY = (isPortrait ? 756 getResources().getDimension(R.dimen.customizeSmallScreenVerticalMarginPortrait) : 757 getResources().getDimension(R.dimen.customizeSmallScreenVerticalMarginLandscape)); 758 } 759 760 // We animate all the screens to the centered position in workspace 761 // At the same time, the screens become greyed/dimmed 762 763 // newX is initialized to the left-most position of the centered screens 764 float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2; 765 766 // We are going to scale about the center of the view, so we need to adjust the positions 767 // of the views accordingly 768 newX -= (pageWidth - scaledPageWidth) / 2.0f; 769 newY -= (pageHeight - scaledPageHeight) / 2.0f; 770 for (int i = 0; i < screenCount; i++) { 771 CellLayout cl = (CellLayout) getChildAt(i); 772 773 float rotation = (-i + 2) * WORKSPACE_ROTATION; 774 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); 775 float rotationScaleY = getYScaleForScreen(i); 776 777 if (animated) { 778 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 779 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(cl, 780 PropertyValuesHolder.ofFloat("x", newX), 781 PropertyValuesHolder.ofFloat("y", newY), 782 PropertyValuesHolder.ofFloat("scaleX", 783 SHRINK_FACTOR * rotationScaleX * extraShrinkFactor), 784 PropertyValuesHolder.ofFloat("scaleY", 785 SHRINK_FACTOR * rotationScaleY * extraShrinkFactor), 786 PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha), 787 PropertyValuesHolder.ofFloat("alpha", finalAlpha), 788 PropertyValuesHolder.ofFloat("rotationY", rotation)); 789 anim.setDuration(duration); 790 anim.start(); 791 } else { 792 cl.setX((int)newX); 793 cl.setY((int)newY); 794 cl.setScaleX(SHRINK_FACTOR * rotationScaleX * extraShrinkFactor); 795 cl.setScaleY(SHRINK_FACTOR * rotationScaleY * extraShrinkFactor); 796 cl.setBackgroundAlpha(finalAlpha); 797 cl.setAlpha(finalAlpha); 798 cl.setRotationY(rotation); 799 } 800 // increment newX for the next screen 801 newX += scaledPageWidth + extraScaledSpacing; 802 } 803 setChildrenDrawnWithCacheEnabled(true); 804 } 805 806 807 private void updateWhichPagesAcceptDrops(ShrinkPosition state) { 808 updateWhichPagesAcceptDropsHelper(state, false, 1, 1); 809 } 810 811 812 private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) { 813 updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY); 814 } 815 816 private void updateWhichPagesAcceptDropsHelper( 817 ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) { 818 final int screenCount = getChildCount(); 819 for (int i = 0; i < screenCount; i++) { 820 CellLayout cl = (CellLayout) getChildAt(i); 821 822 switch (state) { 823 case SHRINK_TO_TOP: 824 if (!isDragHappening) { 825 boolean showDropHighlight = i == mCurrentPage; 826 cl.setAcceptsDrops(showDropHighlight); 827 break; 828 } 829 // otherwise, fall through below and mark non-full screens as accepting drops 830 case SHRINK_TO_BOTTOM_HIDDEN: 831 case SHRINK_TO_BOTTOM_VISIBLE: 832 if (!isDragHappening) { 833 // even if a drag isn't happening, we don't want to show a screen as 834 // accepting drops if it doesn't have at least one free cell 835 spanX = 1; 836 spanY = 1; 837 } 838 // the page accepts drops if we can find at least one empty spot 839 cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY)); 840 break; 841 default: 842 throw new RuntimeException( 843 "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition"); 844 } 845 } 846 } 847 848 /* 849 * 850 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we 851 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace 852 * 853 * These methods mark the appropriate pages as accepting drops (which alters their visual 854 * appearance) and, if the pages are hidden, makes them visible. 855 * 856 */ 857 public void onDragStartedWithItemSpans(int spanX, int spanY) { 858 updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY); 859 if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) { 860 shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true); 861 } 862 } 863 864 public void onDragStartedWithItemMinSize(int minWidth, int minHeight) { 865 int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null); 866 onDragStartedWithItemSpans(spanXY[0], spanXY[1]); 867 } 868 869 // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was 870 // never dragged over 871 public void onDragStopped() { 872 updateWhichPagesAcceptDrops(mShrunkenState); 873 } 874 875 // We call this when we trigger an unshrink by clicking on the CellLayout cl 876 public void unshrink(CellLayout clThatWasClicked) { 877 int newCurrentPage = mCurrentPage; 878 final int screenCount = getChildCount(); 879 for (int i = 0; i < screenCount; i++) { 880 if (getChildAt(i) == clThatWasClicked) { 881 newCurrentPage = i; 882 } 883 } 884 unshrink(newCurrentPage); 885 } 886 887 @Override 888 protected boolean handlePagingClicks() { 889 return true; 890 } 891 892 private void unshrink(int newCurrentPage) { 893 if (mIsSmall) { 894 int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage); 895 int delta = newX - mScrollX; 896 897 final int screenCount = getChildCount(); 898 for (int i = 0; i < screenCount; i++) { 899 CellLayout cl = (CellLayout) getChildAt(i); 900 cl.setX(cl.getX() + delta); 901 } 902 setCurrentPage(newCurrentPage); 903 unshrink(); 904 } 905 } 906 907 void unshrink() { 908 unshrink(true); 909 } 910 911 void unshrink(boolean animated) { 912 if (mIsSmall) { 913 mIsSmall = false; 914 AnimatorSet s = new AnimatorSet(); 915 final int screenCount = getChildCount(); 916 917 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 918 for (int i = 0; i < screenCount; i++) { 919 final CellLayout cl = (CellLayout)getChildAt(i); 920 float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; 921 float rotation = 0.0f; 922 923 if (i < mCurrentPage) { 924 rotation = WORKSPACE_ROTATION; 925 } else if (i > mCurrentPage) { 926 rotation = -WORKSPACE_ROTATION; 927 } 928 929 if (animated) { 930 931 s.playTogether( 932 ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration), 933 ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration), 934 ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration), 935 ObjectAnimator.ofFloat(cl, "scaleY", 1.0f).setDuration(duration), 936 ObjectAnimator.ofFloat(cl, "backgroundAlpha", 0.0f).setDuration(duration), 937 ObjectAnimator.ofFloat(cl, "alpha", finalAlphaValue).setDuration(duration), 938 ObjectAnimator.ofFloat(cl, "rotationY", rotation).setDuration(duration)); 939 } else { 940 cl.setTranslationX(0.0f); 941 cl.setTranslationY(0.0f); 942 cl.setScaleX(1.0f); 943 cl.setScaleY(1.0f); 944 cl.setBackgroundAlpha(0.0f); 945 cl.setAlpha(finalAlphaValue); 946 cl.setRotationY(rotation); 947 } 948 } 949 if (animated) { 950 // If we call this when we're not animated, onAnimationEnd is never called on 951 // the listener; make sure we only use the listener when we're actually animating 952 s.addListener(mUnshrinkAnimationListener); 953 s.start(); 954 } 955 } 956 } 957 958 /** 959 * Draw the View v into the given Canvas. 960 * 961 * @param v the view to draw 962 * @param destCanvas the canvas to draw on 963 * @param padding the horizontal and vertical padding to use when drawing 964 */ 965 private void drawDragView(View v, Canvas destCanvas, int padding) { 966 final Rect clipRect = mTempRect; 967 v.getDrawingRect(clipRect); 968 969 // For a TextView, adjust the clip rect so that we don't include the text label 970 if (v instanceof TextView) { 971 final int iconHeight = ((TextView)v).getCompoundPaddingTop() - v.getPaddingTop(); 972 clipRect.bottom = clipRect.top + iconHeight; 973 } 974 975 // Draw the View into the bitmap. 976 // The translate of scrollX and scrollY is necessary when drawing TextViews, because 977 // they set scrollX and scrollY to large values to achieve centered text 978 979 destCanvas.save(); 980 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2); 981 destCanvas.clipRect(clipRect, Op.REPLACE); 982 v.draw(destCanvas); 983 destCanvas.restore(); 984 } 985 986 /** 987 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 988 * Responsibility for the bitmap is transferred to the caller. 989 */ 990 private Bitmap createDragOutline(View v, Canvas canvas, int padding) { 991 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 992 final Bitmap b = Bitmap.createBitmap( 993 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); 994 995 canvas.setBitmap(b); 996 drawDragView(v, canvas, padding); 997 mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); 998 999 return b; 1000 } 1001 1002 /** 1003 * Creates a drag outline to represent a drop (that we don't have the actual information for 1004 * yet). May be changed in the future to alter the drop outline slightly depending on the 1005 * clip description mime data. 1006 */ 1007 private Bitmap createExternalDragOutline(Canvas canvas, int padding) { 1008 Resources r = getResources(); 1009 final int outlineColor = r.getColor(R.color.drag_outline_color); 1010 final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width); 1011 final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height); 1012 final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius); 1013 final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f); 1014 final Bitmap b = Bitmap.createBitmap( 1015 iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888); 1016 1017 canvas.setBitmap(b); 1018 canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset), 1019 rectRadius, rectRadius, mExternalDragOutlinePaint); 1020 mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true); 1021 1022 return b; 1023 } 1024 1025 /** 1026 * Returns a new bitmap to show when the given View is being dragged around. 1027 * Responsibility for the bitmap is transferred to the caller. 1028 */ 1029 private Bitmap createDragBitmap(View v, Canvas canvas, int padding) { 1030 final int outlineColor = getResources().getColor(R.color.drag_outline_color); 1031 final Bitmap b = Bitmap.createBitmap( 1032 mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888); 1033 1034 canvas.setBitmap(b); 1035 canvas.drawBitmap(mDragOutline, 0, 0, null); 1036 drawDragView(v, canvas, padding); 1037 mOutlineHelper.applyOuterBlur(b, canvas, outlineColor); 1038 1039 return b; 1040 } 1041 1042 void startDrag(CellLayout.CellInfo cellInfo) { 1043 View child = cellInfo.cell; 1044 1045 // Make sure the drag was started by a long press as opposed to a long click. 1046 if (!child.isInTouchMode()) { 1047 return; 1048 } 1049 1050 mDragInfo = cellInfo; 1051 mDragInfo.screen = mCurrentPage; 1052 1053 CellLayout current = getCurrentDropLayout(); 1054 1055 current.onDragChild(child); 1056 1057 child.clearFocus(); 1058 child.setPressed(false); 1059 1060 final Canvas canvas = new Canvas(); 1061 1062 // We need to add extra padding to the bitmap to make room for the glow effect 1063 final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; 1064 1065 // The outline is used to visualize where the item will land if dropped 1066 mDragOutline = createDragOutline(child, canvas, bitmapPadding); 1067 1068 // The drag bitmap follows the touch point around on the screen 1069 final Bitmap b = createDragBitmap(child, canvas, bitmapPadding); 1070 1071 final int bmpWidth = b.getWidth(); 1072 final int bmpHeight = b.getHeight(); 1073 child.getLocationOnScreen(mTempXY); 1074 final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2; 1075 final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2; 1076 mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this, 1077 child.getTag(), DragController.DRAG_ACTION_MOVE, null); 1078 b.recycle(); 1079 } 1080 1081 void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, 1082 boolean insertAtFirst, int intersectX, int intersectY) { 1083 final CellLayout cellLayout = (CellLayout) getChildAt(screen); 1084 View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info); 1085 1086 final int[] cellXY = new int[2]; 1087 cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 1088 addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 1089 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1090 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1091 cellXY[0], cellXY[1]); 1092 } 1093 1094 private void setPositionForDropAnimation( 1095 View dragView, int dragViewX, int dragViewY, View parent, View child) { 1096 final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1097 1098 // Based on the position of the drag view, find the top left of the original view 1099 int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2; 1100 int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2; 1101 viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX); 1102 viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY); 1103 1104 // Set its old pos (in the new parent's coordinates); the CellLayout will 1105 // animate it from this position during the next layout pass 1106 lp.oldX = viewX - (parent.getLeft() - mScrollX); 1107 lp.oldY = viewY - (parent.getTop() - mScrollY); 1108 } 1109 1110 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1111 DragView dragView, Object dragInfo) { 1112 1113 int originX = x - xOffset; 1114 int originY = y - yOffset; 1115 1116 if (mDragTargetLayout == null) { 1117 // Cancel the drag if we're not over a screen at time of drop 1118 if (mDragInfo != null) { 1119 // Set its position so the parent can animate it back 1120 final View parent = getChildAt(mDragInfo.screen); 1121 setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell); 1122 } 1123 return; 1124 } 1125 1126 if (mIsSmall || mIsInUnshrinkAnimation) { 1127 // get originX and originY in the local coordinate system of the screen 1128 mTempOriginXY[0] = originX; 1129 mTempOriginXY[1] = originY; 1130 mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY); 1131 originX = (int)mTempOriginXY[0]; 1132 originY = (int)mTempOriginXY[1]; 1133 } 1134 1135 if (source != this) { 1136 onDropExternal(originX, originY, dragInfo, mDragTargetLayout); 1137 } else if (mDragInfo != null) { 1138 // Move internally 1139 final View cell = mDragInfo.cell; 1140 mTargetCell = findNearestVacantArea(originX, originY, 1141 mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout, 1142 mTargetCell); 1143 1144 int screen = indexOfChild(mDragTargetLayout); 1145 if (screen != mDragInfo.screen) { 1146 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1147 originalCellLayout.removeView(cell); 1148 addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], 1149 mDragInfo.spanX, mDragInfo.spanY); 1150 } 1151 mDragTargetLayout.onDropChild(cell); 1152 1153 // update the item's position after drop 1154 final ItemInfo info = (ItemInfo) cell.getTag(); 1155 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 1156 mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); 1157 lp.cellX = mTargetCell[0]; 1158 lp.cellY = mTargetCell[1]; 1159 cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, 1160 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 1161 1162 LauncherModel.moveItemInDatabase(mLauncher, info, 1163 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1164 lp.cellX, lp.cellY); 1165 1166 // Prepare it to be animated into its new position 1167 // This must be called after the view has been re-parented 1168 setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell); 1169 } 1170 } 1171 1172 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1173 int yOffset, DragView dragView, Object dragInfo) { 1174 mDragTargetLayout = null; // Reset the drag state 1175 1176 if (!mIsSmall) { 1177 mDragTargetLayout = getCurrentDropLayout(); 1178 mDragTargetLayout.onDragEnter(); 1179 showOutlines(); 1180 mInDragMode = true; 1181 CellLayout cl = (CellLayout) getChildAt(mCurrentPage); 1182 cl.setBackgroundAlphaMultiplier(1.0f); 1183 } 1184 } 1185 1186 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, 1187 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1188 1189 if (mIsSmall || mIsInUnshrinkAnimation) { 1190 // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens 1191 return null; 1192 } 1193 // We may need to delegate the drag to a child view. If a 1x1 item 1194 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1195 // then drag events should be handled by that child. 1196 1197 ItemInfo item = (ItemInfo)dragInfo; 1198 CellLayout currentLayout = getCurrentDropLayout(); 1199 1200 int dragPointX, dragPointY; 1201 if (item.spanX == 1 && item.spanY == 1) { 1202 // For a 1x1, calculate the drop cell exactly as in onDragOver 1203 dragPointX = x - xOffset; 1204 dragPointY = y - yOffset; 1205 } else { 1206 // Otherwise, use the exact drag coordinates 1207 dragPointX = x; 1208 dragPointY = y; 1209 } 1210 dragPointX += mScrollX - currentLayout.getLeft(); 1211 dragPointY += mScrollY - currentLayout.getTop(); 1212 1213 // If we are dragging over a cell that contains a DropTarget that will 1214 // accept the drop, delegate to that DropTarget. 1215 final int[] cellXY = mTempCell; 1216 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1217 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1218 if (child instanceof DropTarget) { 1219 DropTarget target = (DropTarget)child; 1220 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1221 return target; 1222 } 1223 } 1224 return null; 1225 } 1226 1227 /** 1228 * Global drag and drop handler 1229 */ 1230 @Override 1231 public boolean onDragEvent(DragEvent event) { 1232 final CellLayout layout = (CellLayout) getChildAt(mCurrentPage); 1233 final int[] pos = new int[2]; 1234 layout.getLocationOnScreen(pos); 1235 // We need to offset the drag coordinates to layout coordinate space 1236 final int x = (int) event.getX() - pos[0]; 1237 final int y = (int) event.getY() - pos[1]; 1238 1239 switch (event.getAction()) { 1240 case DragEvent.ACTION_DRAG_STARTED: 1241 // Check if we have enough space on this screen to add a new shortcut 1242 if (!layout.findCellForSpan(pos, 1, 1)) { 1243 Toast.makeText(mContext, mContext.getString(R.string.out_of_space), 1244 Toast.LENGTH_SHORT).show(); 1245 return false; 1246 } 1247 1248 ClipDescription desc = event.getClipDescription(); 1249 if (desc.filterMimeTypes(ClipDescription.MIMETYPE_TEXT_INTENT) != null) { 1250 // Create the drag outline 1251 // We need to add extra padding to the bitmap to make room for the glow effect 1252 final Canvas canvas = new Canvas(); 1253 final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS; 1254 mDragOutline = createExternalDragOutline(canvas, bitmapPadding); 1255 1256 // Show the current page outlines to indicate that we can accept this drop 1257 showOutlines(); 1258 layout.setHover(true); 1259 layout.onDragEnter(); 1260 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); 1261 1262 return true; 1263 } 1264 break; 1265 case DragEvent.ACTION_DRAG_LOCATION: 1266 // Visualize the drop location 1267 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1); 1268 return true; 1269 case DragEvent.ACTION_DROP: 1270 // Check if we have enough space on this screen to add a new shortcut 1271 if (!layout.findCellForSpan(pos, 1, 1)) { 1272 Toast.makeText(mContext, mContext.getString(R.string.out_of_space), 1273 Toast.LENGTH_SHORT).show(); 1274 return false; 1275 } 1276 1277 // Try and add any shortcuts 1278 int newDropCount = 0; 1279 final LauncherModel model = mLauncher.getModel(); 1280 final ClipData data = event.getClipData(); 1281 final int itemCount = data.getItemCount(); 1282 for (int i = 0; i < itemCount; ++i) { 1283 final Intent intent = data.getItem(i).getIntent(); 1284 if (intent != null && model.validateShortcutIntent(intent)) { 1285 ShortcutInfo info = model.infoFromShortcutIntent(mContext, intent, data. 1286 getIcon()); 1287 onDropExternal(x, y, info, layout); 1288 newDropCount++; 1289 } 1290 } 1291 1292 // Show error message if we couldn't accept any of the items 1293 if (newDropCount <= 0) { 1294 Toast.makeText(mContext, "Only Shortcut Intents accepted.", 1295 Toast.LENGTH_SHORT).show(); 1296 } 1297 1298 return true; 1299 case DragEvent.ACTION_DRAG_ENDED: 1300 // Hide the page outlines after the drop 1301 layout.setHover(false); 1302 layout.onDragExit(); 1303 hideOutlines(); 1304 return true; 1305 } 1306 return super.onDragEvent(event); 1307 } 1308 1309 /* 1310 * 1311 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1312 * coordinate space. The argument xy is modified with the return result. 1313 * 1314 */ 1315 void mapPointFromSelfToChild(View v, float[] xy) { 1316 mapPointFromSelfToChild(v, xy, null); 1317 } 1318 1319 /* 1320 * 1321 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 1322 * coordinate space. The argument xy is modified with the return result. 1323 * 1324 * if cachedInverseMatrix is not null, this method will just use that matrix instead of 1325 * computing it itself; we use this to avoid redundant matrix inversions in 1326 * findMatchingPageForDragOver 1327 * 1328 */ 1329 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) { 1330 if (cachedInverseMatrix == null) { 1331 v.getMatrix().invert(mTempInverseMatrix); 1332 cachedInverseMatrix = mTempInverseMatrix; 1333 } 1334 xy[0] = xy[0] + mScrollX - v.getLeft(); 1335 xy[1] = xy[1] + mScrollY - v.getTop(); 1336 cachedInverseMatrix.mapPoints(xy); 1337 } 1338 1339 /* 1340 * 1341 * Convert the 2D coordinate xy from this CellLayout's coordinate space to 1342 * the parent View's coordinate space. The argument xy is modified with the return result. 1343 * 1344 */ 1345 void mapPointFromChildToSelf(View v, float[] xy) { 1346 v.getMatrix().mapPoints(xy); 1347 xy[0] -= (mScrollX - v.getLeft()); 1348 xy[1] -= (mScrollY - v.getTop()); 1349 } 1350 1351 static private float squaredDistance(float[] point1, float[] point2) { 1352 float distanceX = point1[0] - point2[0]; 1353 float distanceY = point2[1] - point2[1]; 1354 return distanceX * distanceX + distanceY * distanceY; 1355 } 1356 1357 /* 1358 * 1359 * Returns true if the passed CellLayout cl overlaps with dragView 1360 * 1361 */ 1362 boolean overlaps(CellLayout cl, DragView dragView, 1363 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) { 1364 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1365 final float[] draggedItemTopLeft = mTempDragCoordinates; 1366 draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset(); 1367 draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset(); 1368 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates; 1369 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth(); 1370 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight(); 1371 1372 // Transform the dragged item's top left coordinates 1373 // to the CellLayout's local coordinates 1374 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix); 1375 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]); 1376 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]); 1377 1378 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) { 1379 // Transform the dragged item's bottom right coordinates 1380 // to the CellLayout's local coordinates 1381 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix); 1382 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]); 1383 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]); 1384 1385 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) { 1386 float overlap = (overlapRegionRight - overlapRegionLeft) * 1387 (overlapRegionBottom - overlapRegionTop); 1388 if (overlap > 0) { 1389 return true; 1390 } 1391 } 1392 } 1393 return false; 1394 } 1395 1396 /* 1397 * 1398 * This method returns the CellLayout that is currently being dragged to. In order to drag 1399 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second 1400 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one 1401 * 1402 * Return null if no CellLayout is currently being dragged over 1403 * 1404 */ 1405 private CellLayout findMatchingPageForDragOver( 1406 DragView dragView, int originX, int originY, int offsetX, int offsetY) { 1407 // We loop through all the screens (ie CellLayouts) and see which ones overlap 1408 // with the item being dragged and then choose the one that's closest to the touch point 1409 final int screenCount = getChildCount(); 1410 CellLayout bestMatchingScreen = null; 1411 float smallestDistSoFar = Float.MAX_VALUE; 1412 1413 for (int i = 0; i < screenCount; i++) { 1414 CellLayout cl = (CellLayout)getChildAt(i); 1415 1416 final float[] touchXy = mTempTouchCoordinates; 1417 touchXy[0] = originX + offsetX; 1418 touchXy[1] = originY + offsetY; 1419 1420 // Transform the touch coordinates to the CellLayout's local coordinates 1421 // If the touch point is within the bounds of the cell layout, we can return immediately 1422 cl.getMatrix().invert(mTempInverseMatrix); 1423 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); 1424 1425 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && 1426 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { 1427 return cl; 1428 } 1429 1430 if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) { 1431 // Get the center of the cell layout in screen coordinates 1432 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates; 1433 cellLayoutCenter[0] = cl.getWidth()/2; 1434 cellLayoutCenter[1] = cl.getHeight()/2; 1435 mapPointFromChildToSelf(cl, cellLayoutCenter); 1436 1437 touchXy[0] = originX + offsetX; 1438 touchXy[1] = originY + offsetY; 1439 1440 // Calculate the distance between the center of the CellLayout 1441 // and the touch point 1442 float dist = squaredDistance(touchXy, cellLayoutCenter); 1443 1444 if (dist < smallestDistSoFar) { 1445 smallestDistSoFar = dist; 1446 bestMatchingScreen = cl; 1447 } 1448 } 1449 } 1450 return bestMatchingScreen; 1451 } 1452 1453 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1454 DragView dragView, Object dragInfo) { 1455 // When touch is inside the scroll area, skip dragOver actions for the current screen 1456 if (!mInScrollArea) { 1457 CellLayout layout; 1458 int originX = x - xOffset; 1459 int originY = y - yOffset; 1460 if (mIsSmall || mIsInUnshrinkAnimation) { 1461 layout = findMatchingPageForDragOver( 1462 dragView, originX, originY, xOffset, yOffset); 1463 1464 if (layout != mDragTargetLayout) { 1465 if (mDragTargetLayout != null) { 1466 mDragTargetLayout.setHover(false); 1467 } 1468 mDragTargetLayout = layout; 1469 if (mDragTargetLayout != null) { 1470 mDragTargetLayout.setHover(true); 1471 } 1472 } 1473 } else { 1474 layout = getCurrentDropLayout(); 1475 1476 final ItemInfo item = (ItemInfo)dragInfo; 1477 if (dragInfo instanceof LauncherAppWidgetInfo) { 1478 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1479 1480 if (widgetInfo.spanX == -1) { 1481 // Calculate the grid spans needed to fit this widget 1482 int[] spans = layout.rectToCell( 1483 widgetInfo.minWidth, widgetInfo.minHeight, null); 1484 item.spanX = spans[0]; 1485 item.spanY = spans[1]; 1486 } 1487 } 1488 1489 if (source instanceof AllAppsPagedView) { 1490 // This is a hack to fix the point used to determine which cell an icon from 1491 // the all apps screen is over 1492 if (item != null && item.spanX == 1 && layout != null) { 1493 int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2; 1494 1495 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1496 if (dragView.getDragRegionWidth() != layout.getCellWidth()) { 1497 dragView.setDragRegion(dragView.getDragRegionLeft(), 1498 dragView.getDragRegionTop(), 1499 layout.getCellWidth(), 1500 dragView.getDragRegionHeight()); 1501 } 1502 } 1503 } 1504 1505 if (layout != mDragTargetLayout) { 1506 if (mDragTargetLayout != null) { 1507 mDragTargetLayout.onDragExit(); 1508 } 1509 layout.onDragEnter(); 1510 mDragTargetLayout = layout; 1511 } 1512 1513 // only visualize the drop locations for moving icons within the home screen on 1514 // tablet on phone, we also visualize icons dragged in from All Apps 1515 if ((!LauncherApplication.isScreenXLarge() || source == this) 1516 && mDragTargetLayout != null) { 1517 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1518 int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX); 1519 int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY); 1520 mDragTargetLayout.visualizeDropLocation(child, mDragOutline, 1521 localOriginX, localOriginY, item.spanX, item.spanY); 1522 } 1523 } 1524 } 1525 } 1526 1527 public void onDragExit(DragSource source, int x, int y, int xOffset, 1528 int yOffset, DragView dragView, Object dragInfo) { 1529 if (mDragTargetLayout != null) { 1530 mDragTargetLayout.onDragExit(); 1531 } 1532 if (!mIsPageMoving) { 1533 hideOutlines(); 1534 mInDragMode = false; 1535 } 1536 clearAllHovers(); 1537 } 1538 1539 private void onDropExternal(int x, int y, Object dragInfo, 1540 CellLayout cellLayout) { 1541 onDropExternal(x, y, dragInfo, cellLayout, false); 1542 } 1543 1544 /** 1545 * Add the item specified by dragInfo to the given layout. 1546 * This is basically the equivalent of onDropExternal, except it's not initiated 1547 * by drag and drop. 1548 * @return true if successful 1549 */ 1550 public boolean addExternalItemToScreen(Object dragInfo, View layout) { 1551 CellLayout cl = (CellLayout) layout; 1552 ItemInfo info = (ItemInfo) dragInfo; 1553 1554 if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) { 1555 onDropExternal(0, 0, dragInfo, cl, false); 1556 return true; 1557 } 1558 mLauncher.showOutOfSpaceMessage(); 1559 return false; 1560 } 1561 1562 // Drag from somewhere else 1563 // NOTE: This can also be called when we are outside of a drag event, when we want 1564 // to add an item to one of the workspace screens. 1565 private void onDropExternal(int x, int y, Object dragInfo, 1566 CellLayout cellLayout, boolean insertAtFirst) { 1567 int screen = indexOfChild(cellLayout); 1568 if (dragInfo instanceof PendingAddItemInfo) { 1569 PendingAddItemInfo info = (PendingAddItemInfo) dragInfo; 1570 // When dragging and dropping from customization tray, we deal with creating 1571 // widgets/shortcuts/folders in a slightly different way 1572 int[] touchXY = new int[2]; 1573 touchXY[0] = x; 1574 touchXY[1] = y; 1575 switch (info.itemType) { 1576 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1577 mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY); 1578 break; 1579 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER: 1580 mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY); 1581 break; 1582 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1583 mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY); 1584 break; 1585 default: 1586 throw new IllegalStateException("Unknown item type: " + info.itemType); 1587 } 1588 cellLayout.onDragExit(); 1589 cellLayout.animateDrop(); 1590 return; 1591 } 1592 1593 // This is for other drag/drop cases, like dragging from All Apps 1594 ItemInfo info = (ItemInfo) dragInfo; 1595 1596 View view = null; 1597 1598 switch (info.itemType) { 1599 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1600 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1601 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1602 // Came from all apps -- make a copy 1603 info = new ShortcutInfo((ApplicationInfo) info); 1604 } 1605 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1606 (ShortcutInfo) info); 1607 break; 1608 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1609 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1610 cellLayout, ((UserFolderInfo) info)); 1611 break; 1612 default: 1613 throw new IllegalStateException("Unknown item type: " + info.itemType); 1614 } 1615 1616 // If the view is null, it has already been added. 1617 if (view == null) { 1618 cellLayout.onDragExit(); 1619 } else { 1620 mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell); 1621 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1622 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1623 cellLayout.onDropChild(view); 1624 cellLayout.animateDrop(); 1625 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1626 1627 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1628 LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, 1629 lp.cellX, lp.cellY); 1630 } 1631 } 1632 1633 /** 1634 * Return the current {@link CellLayout}, correctly picking the destination 1635 * screen while a scroll is in progress. 1636 */ 1637 private CellLayout getCurrentDropLayout() { 1638 // if we're currently small, use findMatchingPageForDragOver instead 1639 if (mIsSmall) return null; 1640 int index = mScroller.isFinished() ? mCurrentPage : mNextPage; 1641 return (CellLayout) getChildAt(index); 1642 } 1643 1644 /** 1645 * Return the current CellInfo describing our current drag; this method exists 1646 * so that Launcher can sync this object with the correct info when the activity is created/ 1647 * destroyed 1648 * 1649 */ 1650 public CellLayout.CellInfo getDragInfo() { 1651 return mDragInfo; 1652 } 1653 1654 /** 1655 * {@inheritDoc} 1656 */ 1657 public boolean acceptDrop(DragSource source, int x, int y, 1658 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1659 if (mDragTargetLayout == null) { 1660 // cancel the drag if we're not over a screen at time of drop 1661 return false; 1662 } 1663 1664 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1665 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1666 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1667 1668 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1669 1670 if (mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) { 1671 return true; 1672 } else { 1673 mLauncher.showOutOfSpaceMessage(); 1674 return false; 1675 } 1676 } 1677 1678 /** 1679 * Calculate the nearest cell where the given object would be dropped. 1680 */ 1681 private int[] findNearestVacantArea(int pixelX, int pixelY, 1682 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1683 1684 int localPixelX = pixelX - (layout.getLeft() - mScrollX); 1685 int localPixelY = pixelY - (layout.getTop() - mScrollY); 1686 1687 // Find the best target drop location 1688 return layout.findNearestVacantArea( 1689 localPixelX, localPixelY, spanX, spanY, ignoreView, recycle); 1690 } 1691 1692 /** 1693 * Estimate the size that a child with the given dimensions will take in the current screen. 1694 */ 1695 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1696 ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result); 1697 } 1698 1699 void setLauncher(Launcher launcher) { 1700 mLauncher = launcher; 1701 } 1702 1703 public void setDragController(DragController dragController) { 1704 mDragController = dragController; 1705 } 1706 1707 public void onDropCompleted(View target, boolean success) { 1708 if (success) { 1709 if (target != this && mDragInfo != null) { 1710 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1711 cellLayout.removeView(mDragInfo.cell); 1712 if (mDragInfo.cell instanceof DropTarget) { 1713 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1714 } 1715 // final Object tag = mDragInfo.cell.getTag(); 1716 } 1717 } else if (mDragInfo != null) { 1718 ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell); 1719 } 1720 1721 mDragOutline = null; 1722 mDragInfo = null; 1723 } 1724 1725 public boolean isDropEnabled() { 1726 return true; 1727 } 1728 1729 @Override 1730 protected void onRestoreInstanceState(Parcelable state) { 1731 super.onRestoreInstanceState(state); 1732 Launcher.setScreen(mCurrentPage); 1733 } 1734 1735 @Override 1736 public void scrollLeft() { 1737 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1738 super.scrollLeft(); 1739 } 1740 } 1741 1742 @Override 1743 public void scrollRight() { 1744 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1745 super.scrollRight(); 1746 } 1747 } 1748 1749 @Override 1750 public void onEnterScrollArea(int direction) { 1751 if (!mIsSmall && !mIsInUnshrinkAnimation) { 1752 mInScrollArea = true; 1753 final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1); 1754 if (0 <= screen && screen < getChildCount()) { 1755 ((CellLayout) getChildAt(screen)).setHover(true); 1756 } 1757 1758 if (mDragTargetLayout != null) { 1759 mDragTargetLayout.onDragExit(); 1760 mDragTargetLayout = null; 1761 } 1762 } 1763 } 1764 1765 private void clearAllHovers() { 1766 final int childCount = getChildCount(); 1767 for (int i = 0; i < childCount; i++) { 1768 ((CellLayout) getChildAt(i)).setHover(false); 1769 } 1770 } 1771 1772 @Override 1773 public void onExitScrollArea() { 1774 if (mInScrollArea) { 1775 mInScrollArea = false; 1776 clearAllHovers(); 1777 } 1778 } 1779 1780 public Folder getFolderForTag(Object tag) { 1781 final int screenCount = getChildCount(); 1782 for (int screen = 0; screen < screenCount; screen++) { 1783 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1784 int count = currentScreen.getChildCount(); 1785 for (int i = 0; i < count; i++) { 1786 View child = currentScreen.getChildAt(i); 1787 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1788 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1789 Folder f = (Folder) child; 1790 if (f.getInfo() == tag && f.getInfo().opened) { 1791 return f; 1792 } 1793 } 1794 } 1795 } 1796 return null; 1797 } 1798 1799 public View getViewForTag(Object tag) { 1800 int screenCount = getChildCount(); 1801 for (int screen = 0; screen < screenCount; screen++) { 1802 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1803 int count = currentScreen.getChildCount(); 1804 for (int i = 0; i < count; i++) { 1805 View child = currentScreen.getChildAt(i); 1806 if (child.getTag() == tag) { 1807 return child; 1808 } 1809 } 1810 } 1811 return null; 1812 } 1813 1814 1815 void removeItems(final ArrayList<ApplicationInfo> apps) { 1816 final int screenCount = getChildCount(); 1817 final PackageManager manager = getContext().getPackageManager(); 1818 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1819 1820 final HashSet<String> packageNames = new HashSet<String>(); 1821 final int appCount = apps.size(); 1822 for (int i = 0; i < appCount; i++) { 1823 packageNames.add(apps.get(i).componentName.getPackageName()); 1824 } 1825 1826 for (int i = 0; i < screenCount; i++) { 1827 final CellLayout layout = (CellLayout) getChildAt(i); 1828 1829 // Avoid ANRs by treating each screen separately 1830 post(new Runnable() { 1831 public void run() { 1832 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1833 childrenToRemove.clear(); 1834 1835 int childCount = layout.getChildCount(); 1836 for (int j = 0; j < childCount; j++) { 1837 final View view = layout.getChildAt(j); 1838 Object tag = view.getTag(); 1839 1840 if (tag instanceof ShortcutInfo) { 1841 final ShortcutInfo info = (ShortcutInfo) tag; 1842 final Intent intent = info.intent; 1843 final ComponentName name = intent.getComponent(); 1844 1845 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1846 for (String packageName: packageNames) { 1847 if (packageName.equals(name.getPackageName())) { 1848 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1849 childrenToRemove.add(view); 1850 } 1851 } 1852 } 1853 } else if (tag instanceof UserFolderInfo) { 1854 final UserFolderInfo info = (UserFolderInfo) tag; 1855 final ArrayList<ShortcutInfo> contents = info.contents; 1856 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1857 final int contentsCount = contents.size(); 1858 boolean removedFromFolder = false; 1859 1860 for (int k = 0; k < contentsCount; k++) { 1861 final ShortcutInfo appInfo = contents.get(k); 1862 final Intent intent = appInfo.intent; 1863 final ComponentName name = intent.getComponent(); 1864 1865 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1866 for (String packageName: packageNames) { 1867 if (packageName.equals(name.getPackageName())) { 1868 toRemove.add(appInfo); 1869 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1870 removedFromFolder = true; 1871 } 1872 } 1873 } 1874 } 1875 1876 contents.removeAll(toRemove); 1877 if (removedFromFolder) { 1878 final Folder folder = getOpenFolder(); 1879 if (folder != null) 1880 folder.notifyDataSetChanged(); 1881 } 1882 } else if (tag instanceof LiveFolderInfo) { 1883 final LiveFolderInfo info = (LiveFolderInfo) tag; 1884 final Uri uri = info.uri; 1885 final ProviderInfo providerInfo = manager.resolveContentProvider( 1886 uri.getAuthority(), 0); 1887 1888 if (providerInfo != null) { 1889 for (String packageName: packageNames) { 1890 if (packageName.equals(providerInfo.packageName)) { 1891 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1892 childrenToRemove.add(view); 1893 } 1894 } 1895 } 1896 } else if (tag instanceof LauncherAppWidgetInfo) { 1897 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1898 final AppWidgetProviderInfo provider = 1899 widgets.getAppWidgetInfo(info.appWidgetId); 1900 if (provider != null) { 1901 for (String packageName: packageNames) { 1902 if (packageName.equals(provider.provider.getPackageName())) { 1903 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1904 childrenToRemove.add(view); 1905 } 1906 } 1907 } 1908 } 1909 } 1910 1911 childCount = childrenToRemove.size(); 1912 for (int j = 0; j < childCount; j++) { 1913 View child = childrenToRemove.get(j); 1914 layout.removeViewInLayout(child); 1915 if (child instanceof DropTarget) { 1916 mDragController.removeDropTarget((DropTarget)child); 1917 } 1918 } 1919 1920 if (childCount > 0) { 1921 layout.requestLayout(); 1922 layout.invalidate(); 1923 } 1924 } 1925 }); 1926 } 1927 } 1928 1929 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1930 final int screenCount = getChildCount(); 1931 for (int i = 0; i < screenCount; i++) { 1932 final CellLayout layout = (CellLayout) getChildAt(i); 1933 int childCount = layout.getChildCount(); 1934 for (int j = 0; j < childCount; j++) { 1935 final View view = layout.getChildAt(j); 1936 Object tag = view.getTag(); 1937 if (tag instanceof ShortcutInfo) { 1938 ShortcutInfo info = (ShortcutInfo)tag; 1939 // We need to check for ACTION_MAIN otherwise getComponent() might 1940 // return null for some shortcuts (for instance, for shortcuts to 1941 // web pages.) 1942 final Intent intent = info.intent; 1943 final ComponentName name = intent.getComponent(); 1944 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1945 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1946 final int appCount = apps.size(); 1947 for (int k = 0; k < appCount; k++) { 1948 ApplicationInfo app = apps.get(k); 1949 if (app.componentName.equals(name)) { 1950 info.setIcon(mIconCache.getIcon(info.intent)); 1951 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1952 new FastBitmapDrawable(info.getIcon(mIconCache)), 1953 null, null); 1954 } 1955 } 1956 } 1957 } 1958 } 1959 } 1960 } 1961 1962 void moveToDefaultScreen(boolean animate) { 1963 if (mIsSmall || mIsInUnshrinkAnimation) { 1964 mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage)); 1965 } else if (animate) { 1966 snapToPage(mDefaultPage); 1967 } else { 1968 setCurrentPage(mDefaultPage); 1969 } 1970 getChildAt(mDefaultPage).requestFocus(); 1971 } 1972 1973 void setIndicators(Drawable previous, Drawable next) { 1974 mPreviousIndicator = previous; 1975 mNextIndicator = next; 1976 previous.setLevel(mCurrentPage); 1977 next.setLevel(mCurrentPage); 1978 } 1979 1980 @Override 1981 public void syncPages() { 1982 } 1983 1984 @Override 1985 public void syncPageItems(int page) { 1986 } 1987 1988} 1989