Workspace.java revision a63c452f5bd491ba9b28c332ccedc6c6c7e2f3cc
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; 20import com.android.launcher2.CellLayout.CellInfo; 21 22import android.animation.Animatable; 23import android.animation.PropertyAnimator; 24import android.animation.Sequencer; 25import android.animation.Animatable.AnimatableListener; 26import android.app.WallpaperManager; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProviderInfo; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.pm.PackageManager; 33import android.content.pm.ProviderInfo; 34import android.content.res.Resources; 35import android.content.res.TypedArray; 36import android.graphics.Canvas; 37import android.graphics.Matrix; 38import android.graphics.Paint; 39import android.graphics.Rect; 40import android.graphics.drawable.Drawable; 41import android.net.Uri; 42import android.os.IBinder; 43import android.os.Parcel; 44import android.os.Parcelable; 45import android.util.AttributeSet; 46import android.util.Log; 47import android.view.MotionEvent; 48import android.view.VelocityTracker; 49import android.view.View; 50import android.view.ViewConfiguration; 51import android.view.ViewGroup; 52import android.view.ViewParent; 53import android.view.animation.Interpolator; 54import android.widget.Scroller; 55import android.widget.TextView; 56import android.widget.Toast; 57 58import java.util.ArrayList; 59import java.util.HashSet; 60 61/** 62 * The workspace is a wide area with a wallpaper and a finite number of screens. 63 * Each screen contains a number of icons, folders or widgets the user can 64 * interact with. A workspace is meant to be used with a fixed width only. 65 */ 66public class Workspace extends ViewGroup 67 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 68 @SuppressWarnings({"UnusedDeclaration"}) 69 private static final String TAG = "Launcher.Workspace"; 70 private static final int INVALID_SCREEN = -1; 71 // This is how much the workspace shrinks when we enter all apps or 72 // customization mode 73 private static final float SHRINK_FACTOR = 0.16f; 74 private static final int SHRINK_TO_TOP = 0; 75 private static final int SHRINK_TO_MIDDLE = 1; 76 private static final int SHRINK_TO_BOTTOM = 2; 77 78 /** 79 * The velocity at which a fling gesture will cause us to snap to the next 80 * screen 81 */ 82 private static final int SNAP_VELOCITY = 600; 83 84 private final WallpaperManager mWallpaperManager; 85 86 private int mDefaultScreen; 87 88 private boolean mFirstLayout = true; 89 private boolean mWaitingToShrinkToBottom = false; 90 91 private int mCurrentScreen; 92 private int mNextScreen = INVALID_SCREEN; 93 private Scroller mScroller; 94 private VelocityTracker mVelocityTracker; 95 96 /** 97 * CellInfo for the cell that is currently being dragged 98 */ 99 private CellLayout.CellInfo mDragInfo; 100 101 /** 102 * Target drop area calculated during last acceptDrop call. 103 */ 104 private int[] mTargetCell = null; 105 106 /** 107 * The CellLayout that is currently being dragged over 108 */ 109 private CellLayout mDragTargetLayout = null; 110 111 private float mLastMotionX; 112 private float mLastMotionY; 113 114 private final static int TOUCH_STATE_REST = 0; 115 private final static int TOUCH_STATE_SCROLLING = 1; 116 117 private int mTouchState = TOUCH_STATE_REST; 118 119 private OnLongClickListener mLongClickListener; 120 121 private Launcher mLauncher; 122 private IconCache mIconCache; 123 private DragController mDragController; 124 125 126 private int[] mTempCell = new int[2]; 127 private int[] mTempEstimate = new int[2]; 128 private float[] mTempDragCoordinates = new float[2]; 129 private float[] mTempDragBottomRightCoordinates = new float[2]; 130 131 private boolean mAllowLongPress = true; 132 133 private int mTouchSlop; 134 private int mMaximumVelocity; 135 136 private static final int INVALID_POINTER = -1; 137 private static final int DEFAULT_CELL_COUNT_X = 4; 138 private static final int DEFAULT_CELL_COUNT_Y = 4; 139 140 private int mActivePointerId = INVALID_POINTER; 141 142 private Drawable mPreviousIndicator; 143 private Drawable mNextIndicator; 144 145 private static final float NANOTIME_DIV = 1000000000.0f; 146 private static final float SMOOTHING_SPEED = 0.75f; 147 private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); 148 private float mSmoothingTime; 149 private float mTouchX; 150 151 private WorkspaceOvershootInterpolator mScrollInterpolator; 152 153 private static final float BASELINE_FLING_VELOCITY = 2500.f; 154 private static final float FLING_VELOCITY_INFLUENCE = 0.4f; 155 156 private Paint mDropIndicatorPaint; 157 158 // State variable that indicated whether the screens are small (ie when you're 159 // in all apps or customize mode) 160 private boolean mIsSmall; 161 private AnimatableListener mUnshrinkAnimationListener; 162 163 private static class WorkspaceOvershootInterpolator implements Interpolator { 164 private static final float DEFAULT_TENSION = 1.3f; 165 private float mTension; 166 167 public WorkspaceOvershootInterpolator() { 168 mTension = DEFAULT_TENSION; 169 } 170 171 public void setDistance(int distance) { 172 mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; 173 } 174 175 public void disableSettle() { 176 mTension = 0.f; 177 } 178 179 public float getInterpolation(float t) { 180 // _o(t) = t * t * ((tension + 1) * t + tension) 181 // o(t) = _o(t - 1) + 1 182 t -= 1.0f; 183 return t * t * ((mTension + 1) * t + mTension) + 1.0f; 184 } 185 } 186 187 /** 188 * Used to inflate the Workspace from XML. 189 * 190 * @param context The application's context. 191 * @param attrs The attribtues set containing the Workspace's customization values. 192 */ 193 public Workspace(Context context, AttributeSet attrs) { 194 this(context, attrs, 0); 195 } 196 197 /** 198 * Used to inflate the Workspace from XML. 199 * 200 * @param context The application's context. 201 * @param attrs The attribtues set containing the Workspace's customization values. 202 * @param defStyle Unused. 203 */ 204 public Workspace(Context context, AttributeSet attrs, int defStyle) { 205 super(context, attrs, defStyle); 206 207 mWallpaperManager = WallpaperManager.getInstance(context); 208 209 TypedArray a = context.obtainStyledAttributes(attrs, 210 R.styleable.Workspace, defStyle, 0); 211 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 212 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 213 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); 214 a.recycle(); 215 216 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 217 setHapticFeedbackEnabled(false); 218 initWorkspace(); 219 } 220 221 /** 222 * Initializes various states for this workspace. 223 */ 224 private void initWorkspace() { 225 Context context = getContext(); 226 mScrollInterpolator = new WorkspaceOvershootInterpolator(); 227 mScroller = new Scroller(context, mScrollInterpolator); 228 mCurrentScreen = mDefaultScreen; 229 Launcher.setScreen(mCurrentScreen); 230 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 231 mIconCache = app.getIconCache(); 232 233 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 234 mTouchSlop = configuration.getScaledTouchSlop(); 235 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 236 mUnshrinkAnimationListener = new AnimatableListener() { 237 public void onAnimationStart(Animatable animation) {} 238 public void onAnimationEnd(Animatable animation) { 239 mIsSmall = false; 240 } 241 public void onAnimationCancel(Animatable animation) {} 242 public void onAnimationRepeat(Animatable animation) {} 243 }; 244 } 245 246 @Override 247 public void addView(View child, int index, LayoutParams params) { 248 if (!(child instanceof CellLayout)) { 249 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 250 } 251 super.addView(child, index, params); 252 } 253 254 @Override 255 public void addView(View child) { 256 if (!(child instanceof CellLayout)) { 257 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 258 } 259 super.addView(child); 260 } 261 262 @Override 263 public void addView(View child, int index) { 264 if (!(child instanceof CellLayout)) { 265 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 266 } 267 super.addView(child, index); 268 } 269 270 @Override 271 public void addView(View child, int width, int height) { 272 if (!(child instanceof CellLayout)) { 273 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 274 } 275 super.addView(child, width, height); 276 } 277 278 @Override 279 public void addView(View child, LayoutParams params) { 280 if (!(child instanceof CellLayout)) { 281 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 282 } 283 super.addView(child, params); 284 } 285 286 /** 287 * @return The open folder on the current screen, or null if there is none 288 */ 289 Folder getOpenFolder() { 290 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); 291 int count = currentScreen.getChildCount(); 292 for (int i = 0; i < count; i++) { 293 View child = currentScreen.getChildAt(i); 294 if (child instanceof Folder) { 295 Folder folder = (Folder) child; 296 if (folder.getInfo().opened) 297 return folder; 298 } 299 } 300 return null; 301 } 302 303 ArrayList<Folder> getOpenFolders() { 304 final int screenCount = getChildCount(); 305 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 306 307 for (int screen = 0; screen < screenCount; screen++) { 308 CellLayout currentScreen = (CellLayout) getChildAt(screen); 309 int count = currentScreen.getChildCount(); 310 for (int i = 0; i < count; i++) { 311 View child = currentScreen.getChildAt(i); 312 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child 313 .getLayoutParams(); 314 if (child instanceof Folder) { 315 Folder folder = (Folder) child; 316 if (folder.getInfo().opened) 317 folders.add(folder); 318 break; 319 } 320 } 321 } 322 323 return folders; 324 } 325 326 boolean isDefaultScreenShowing() { 327 return mCurrentScreen == mDefaultScreen; 328 } 329 330 /** 331 * Returns the index of the currently displayed screen. 332 * 333 * @return The index of the currently displayed screen. 334 */ 335 int getCurrentScreen() { 336 return mCurrentScreen; 337 } 338 339 /** 340 * Sets the current screen. 341 * 342 * @param currentScreen 343 */ 344 void setCurrentScreen(int currentScreen) { 345 setCurrentScreen(currentScreen, true); 346 } 347 348 void setCurrentScreen(int currentScreen, boolean animateScrolling) { 349 setCurrentScreen(currentScreen, animateScrolling, getWidth()); 350 } 351 352 void setCurrentScreen(int currentScreen, boolean animateScrolling, int screenWidth) { 353 if (!mScroller.isFinished()) 354 mScroller.abortAnimation(); 355 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); 356 if (mPreviousIndicator != null) { 357 mPreviousIndicator.setLevel(mCurrentScreen); 358 mNextIndicator.setLevel(mCurrentScreen); 359 } 360 if (animateScrolling) { 361 scrollTo(mCurrentScreen * screenWidth, 0); 362 } else { 363 mScrollX = mCurrentScreen * screenWidth; 364 } 365 updateWallpaperOffset(screenWidth * (getChildCount() - 1)); 366 invalidate(); 367 } 368 369 /** 370 * Adds the specified child in the current screen. The position and dimension of 371 * the child are defined by x, y, spanX and spanY. 372 * 373 * @param child The child to add in one of the workspace's screens. 374 * @param x The X position of the child in the screen's grid. 375 * @param y The Y position of the child in the screen's grid. 376 * @param spanX The number of cells spanned horizontally by the child. 377 * @param spanY The number of cells spanned vertically by the child. 378 */ 379 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { 380 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false); 381 } 382 383 /** 384 * Adds the specified child in the current screen. The position and dimension of 385 * the child are defined by x, y, spanX and spanY. 386 * 387 * @param child The child to add in one of the workspace's screens. 388 * @param x The X position of the child in the screen's grid. 389 * @param y The Y position of the child in the screen's grid. 390 * @param spanX The number of cells spanned horizontally by the child. 391 * @param spanY The number of cells spanned vertically by the child. 392 * @param insert When true, the child is inserted at the beginning of the children list. 393 */ 394 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { 395 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert); 396 } 397 398 /** 399 * Adds the specified child in the specified screen. The position and dimension of 400 * the child are defined by x, y, spanX and spanY. 401 * 402 * @param child The child to add in one of the workspace's screens. 403 * @param screen The screen in which to add the child. 404 * @param x The X position of the child in the screen's grid. 405 * @param y The Y position of the child in the screen's grid. 406 * @param spanX The number of cells spanned horizontally by the child. 407 * @param spanY The number of cells spanned vertically by the child. 408 */ 409 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 410 addInScreen(child, screen, x, y, spanX, spanY, false); 411 } 412 413 void addInFullScreen(View child, int screen) { 414 addInScreen(child, screen, 0, 0, -1, -1); 415 } 416 417 /** 418 * Adds the specified child in the specified screen. The position and dimension of 419 * the child are defined by x, y, spanX and spanY. 420 * 421 * @param child The child to add in one of the workspace's screens. 422 * @param screen The screen in which to add the child. 423 * @param x The X position of the child in the screen's grid. 424 * @param y The Y position of the child in the screen's grid. 425 * @param spanX The number of cells spanned horizontally by the child. 426 * @param spanY The number of cells spanned vertically by the child. 427 * @param insert When true, the child is inserted at the beginning of the children list. 428 */ 429 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 430 if (screen < 0 || screen >= getChildCount()) { 431 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 432 + " (was " + screen + "); skipping child"); 433 return; 434 } 435 436 final CellLayout group = (CellLayout) getChildAt(screen); 437 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 438 if (lp == null) { 439 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 440 } else { 441 lp.cellX = x; 442 lp.cellY = y; 443 lp.cellHSpan = spanX; 444 lp.cellVSpan = spanY; 445 } 446 447 // Get the canonical child id to uniquely represent this view in this screen 448 int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); 449 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { 450 // TODO: This branch occurs when the workspace is adding views 451 // outside of the defined grid 452 // maybe we should be deleting these items from the LauncherModel? 453 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 454 } 455 456 if (!(child instanceof Folder)) { 457 child.setHapticFeedbackEnabled(false); 458 child.setOnLongClickListener(mLongClickListener); 459 } 460 if (child instanceof DropTarget) { 461 mDragController.addDropTarget((DropTarget) child); 462 } 463 } 464 465 CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) { 466 CellLayout group = (CellLayout) getChildAt(mCurrentScreen); 467 if (group != null) { 468 return group.updateOccupiedCells(occupied, null); 469 } 470 return null; 471 } 472 473 public boolean onTouch(View v, MotionEvent event) { 474 // this is an intercepted event being forwarded from a cell layout 475 if (mIsSmall) { 476 unshrink((CellLayout)v); 477 mLauncher.onWorkspaceUnshrink(); 478 return true; 479 } 480 return false; 481 } 482 483 /** 484 * Registers the specified listener on each screen contained in this workspace. 485 * 486 * @param l The listener used to respond to long clicks. 487 */ 488 @Override 489 public void setOnLongClickListener(OnLongClickListener l) { 490 mLongClickListener = l; 491 final int screenCount = getChildCount(); 492 for (int i = 0; i < screenCount; i++) { 493 getChildAt(i).setOnLongClickListener(l); 494 } 495 } 496 497 private void updateWallpaperOffset() { 498 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 499 } 500 501 private void updateWallpaperOffset(int scrollRange) { 502 IBinder token = getWindowToken(); 503 if (token != null) { 504 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 505 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 506 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 507 } 508 } 509 510 @Override 511 public void scrollTo(int x, int y) { 512 super.scrollTo(x, y); 513 mTouchX = x; 514 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 515 } 516 517 @Override 518 public void computeScroll() { 519 if (mScroller.computeScrollOffset()) { 520 mTouchX = mScrollX = mScroller.getCurrX(); 521 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 522 mScrollY = mScroller.getCurrY(); 523 updateWallpaperOffset(); 524 postInvalidate(); 525 } else if (mNextScreen != INVALID_SCREEN) { 526 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); 527 if (mPreviousIndicator != null) { 528 mPreviousIndicator.setLevel(mCurrentScreen); 529 mNextIndicator.setLevel(mCurrentScreen); 530 } 531 Launcher.setScreen(mCurrentScreen); 532 mNextScreen = INVALID_SCREEN; 533 clearChildrenCache(); 534 } else if (mTouchState == TOUCH_STATE_SCROLLING) { 535 final float now = System.nanoTime() / NANOTIME_DIV; 536 final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT); 537 final float dx = mTouchX - mScrollX; 538 mScrollX += dx * e; 539 mSmoothingTime = now; 540 541 // Keep generating points as long as we're more than 1px away from the target 542 if (dx > 1.f || dx < -1.f) { 543 updateWallpaperOffset(); 544 postInvalidate(); 545 } 546 } 547 } 548 549 @Override 550 protected void dispatchDraw(Canvas canvas) { 551 boolean restore = false; 552 int restoreCount = 0; 553 554 // ViewGroup.dispatchDraw() supports many features we don't need: 555 // clip to padding, layout animation, animation listener, disappearing 556 // children, etc. The following implementation attempts to fast-track 557 // the drawing dispatch by drawing only what we know needs to be drawn. 558 559 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN; 560 561 // if the screens are all small, we need to draw all the screens since 562 // they're most likely all visible 563 if (mIsSmall) { 564 final int screenCount = getChildCount(); 565 for (int i = 0; i < screenCount; i++) { 566 CellLayout cl = (CellLayout)getChildAt(i); 567 drawChild(canvas, cl, getDrawingTime()); 568 } 569 } else if (fastDraw) { 570 // If we are not scrolling or flinging, draw only the current screen 571 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); 572 } else { 573 final long drawingTime = getDrawingTime(); 574 final float scrollPos = (float) mScrollX / getWidth(); 575 final int leftScreen = (int) scrollPos; 576 final int rightScreen = leftScreen + 1; 577 if (leftScreen >= 0) { 578 drawChild(canvas, getChildAt(leftScreen), drawingTime); 579 } 580 if (scrollPos != leftScreen && rightScreen < getChildCount()) { 581 drawChild(canvas, getChildAt(rightScreen), drawingTime); 582 } 583 } 584 585 if (restore) { 586 canvas.restoreToCount(restoreCount); 587 } 588 } 589 590 protected void onAttachedToWindow() { 591 super.onAttachedToWindow(); 592 computeScroll(); 593 mDragController.setWindowToken(getWindowToken()); 594 } 595 596 @Override 597 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 598 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 599 600 final int width = MeasureSpec.getSize(widthMeasureSpec); 601 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 602 if (widthMode != MeasureSpec.EXACTLY) { 603 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 604 } 605 606 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 607 if (heightMode != MeasureSpec.EXACTLY) { 608 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 609 } 610 611 // The children are given the same width and height as the workspace 612 final int screenCount = getChildCount(); 613 for (int i = 0; i < screenCount; i++) { 614 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 615 } 616 617 if (mFirstLayout) { 618 setHorizontalScrollBarEnabled(false); 619 setCurrentScreen(mCurrentScreen, false, width); 620 setHorizontalScrollBarEnabled(true); 621 } 622 } 623 624 @Override 625 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 626 if (mFirstLayout) { 627 mFirstLayout = false; 628 } 629 int childLeft = 0; 630 final int screenCount = getChildCount(); 631 for (int i = 0; i < screenCount; i++) { 632 final View child = getChildAt(i); 633 if (child.getVisibility() != View.GONE) { 634 final int childWidth = child.getMeasuredWidth(); 635 child.layout(childLeft, 0, 636 childLeft + childWidth, child.getMeasuredHeight()); 637 childLeft += childWidth; 638 } 639 } 640 641 // if shrinkToBottom() is called on initialization, it has to be deferred 642 // until after the first call to onLayout so that it has the correct width 643 if (mWaitingToShrinkToBottom) { 644 shrinkToBottom(false); 645 mWaitingToShrinkToBottom = false; 646 } 647 648 if (LauncherApplication.isInPlaceRotationEnabled()) { 649 // When the device is rotated, the scroll position of the current screen 650 // needs to be refreshed 651 setCurrentScreen(getCurrentScreen()); 652 } 653 } 654 655 @Override 656 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 657 int screen = indexOfChild(child); 658 if (screen != mCurrentScreen || !mScroller.isFinished()) { 659 if (!mLauncher.isWorkspaceLocked()) { 660 snapToScreen(screen); 661 } 662 return true; 663 } 664 return false; 665 } 666 667 @Override 668 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 669 if (!mLauncher.isAllAppsVisible()) { 670 final Folder openFolder = getOpenFolder(); 671 if (openFolder != null) { 672 return openFolder.requestFocus(direction, previouslyFocusedRect); 673 } else { 674 int focusableScreen; 675 if (mNextScreen != INVALID_SCREEN) { 676 focusableScreen = mNextScreen; 677 } else { 678 focusableScreen = mCurrentScreen; 679 } 680 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); 681 } 682 } 683 return false; 684 } 685 686 @Override 687 public boolean dispatchUnhandledMove(View focused, int direction) { 688 if (direction == View.FOCUS_LEFT) { 689 if (getCurrentScreen() > 0) { 690 snapToScreen(getCurrentScreen() - 1); 691 return true; 692 } 693 } else if (direction == View.FOCUS_RIGHT) { 694 if (getCurrentScreen() < getChildCount() - 1) { 695 snapToScreen(getCurrentScreen() + 1); 696 return true; 697 } 698 } 699 return super.dispatchUnhandledMove(focused, direction); 700 } 701 702 @Override 703 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 704 if (!mLauncher.isAllAppsVisible()) { 705 final Folder openFolder = getOpenFolder(); 706 if (openFolder == null) { 707 getChildAt(mCurrentScreen).addFocusables(views, direction); 708 if (direction == View.FOCUS_LEFT) { 709 if (mCurrentScreen > 0) { 710 getChildAt(mCurrentScreen - 1).addFocusables(views, direction); 711 } 712 } else if (direction == View.FOCUS_RIGHT) { 713 if (mCurrentScreen < getChildCount() - 1) { 714 getChildAt(mCurrentScreen + 1).addFocusables(views, direction); 715 } 716 } 717 } else { 718 openFolder.addFocusables(views, direction); 719 } 720 } 721 } 722 723 @Override 724 public boolean dispatchTouchEvent(MotionEvent ev) { 725 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 726 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 727 // ie when you click on a mini-screen, it zooms back to that screen) 728 if (mLauncher.isWorkspaceLocked() || 729 (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible())) { 730 return false; 731 } 732 } 733 return super.dispatchTouchEvent(ev); 734 } 735 736 /** 737 * {@inheritDoc} 738 */ 739 @Override 740 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 741 if (disallowIntercept) { 742 // We need to make sure to cancel our long press if 743 // a scrollable widget takes over touch events 744 final View currentScreen = getChildAt(mCurrentScreen); 745 currentScreen.cancelLongPress(); 746 } 747 super.requestDisallowInterceptTouchEvent(disallowIntercept); 748 } 749 750 @Override 751 public boolean onInterceptTouchEvent(MotionEvent ev) { 752 final boolean workspaceLocked = mLauncher.isWorkspaceLocked(); 753 final boolean allAppsVisible = mLauncher.isAllAppsVisible(); 754 755 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 756 // ie when you click on a mini-screen, it zooms back to that screen) 757 if (workspaceLocked || (!LauncherApplication.isScreenXLarge() && allAppsVisible)) { 758 return false; // We don't want the events. Let them fall through to the all apps view. 759 } 760 761 /* 762 * This method JUST determines whether we want to intercept the motion. 763 * If we return true, onTouchEvent will be called and we do the actual 764 * scrolling there. 765 */ 766 767 /* 768 * Shortcut the most recurring case: the user is in the dragging 769 * state and he is moving his finger. We want to intercept this 770 * motion. 771 */ 772 final int action = ev.getAction(); 773 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { 774 return true; 775 } 776 777 if (mVelocityTracker == null) { 778 mVelocityTracker = VelocityTracker.obtain(); 779 } 780 mVelocityTracker.addMovement(ev); 781 782 switch (action & MotionEvent.ACTION_MASK) { 783 case MotionEvent.ACTION_MOVE: { 784 /* 785 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 786 * whether the user has moved far enough from his original down touch. 787 */ 788 789 /* 790 * Locally do absolute value. mLastMotionX is set to the y value 791 * of the down event. 792 */ 793 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 794 final float x = ev.getX(pointerIndex); 795 final float y = ev.getY(pointerIndex); 796 final int xDiff = (int) Math.abs(x - mLastMotionX); 797 final int yDiff = (int) Math.abs(y - mLastMotionY); 798 799 final int touchSlop = mTouchSlop; 800 boolean xMoved = xDiff > touchSlop; 801 boolean yMoved = yDiff > touchSlop; 802 803 if (xMoved || yMoved) { 804 805 if (xMoved) { 806 // Scroll if the user moved far enough along the X axis 807 mTouchState = TOUCH_STATE_SCROLLING; 808 mLastMotionX = x; 809 mTouchX = mScrollX; 810 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 811 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 812 } 813 // Either way, cancel any pending longpress 814 if (mAllowLongPress) { 815 mAllowLongPress = false; 816 // Try canceling the long press. It could also have been scheduled 817 // by a distant descendant, so use the mAllowLongPress flag to block 818 // everything 819 final View currentScreen = getChildAt(mCurrentScreen); 820 currentScreen.cancelLongPress(); 821 } 822 } 823 break; 824 } 825 826 case MotionEvent.ACTION_DOWN: { 827 final float x = ev.getX(); 828 final float y = ev.getY(); 829 // Remember location of down touch 830 mLastMotionX = x; 831 mLastMotionY = y; 832 mActivePointerId = ev.getPointerId(0); 833 mAllowLongPress = true; 834 835 /* 836 * If being flinged and user touches the screen, initiate drag; 837 * otherwise don't. mScroller.isFinished should be false when 838 * being flinged. 839 */ 840 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 841 break; 842 } 843 844 case MotionEvent.ACTION_CANCEL: 845 case MotionEvent.ACTION_UP: 846 847 if (mTouchState != TOUCH_STATE_SCROLLING) { 848 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 849 if (!currentScreen.lastDownOnOccupiedCell()) { 850 getLocationOnScreen(mTempCell); 851 // Send a tap to the wallpaper if the last down was on empty space 852 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 853 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 854 "android.wallpaper.tap", 855 mTempCell[0] + (int) ev.getX(pointerIndex), 856 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); 857 } 858 } 859 860 // Release the drag 861 clearChildrenCache(); 862 mTouchState = TOUCH_STATE_REST; 863 mActivePointerId = INVALID_POINTER; 864 mAllowLongPress = false; 865 866 if (mVelocityTracker != null) { 867 mVelocityTracker.recycle(); 868 mVelocityTracker = null; 869 } 870 871 break; 872 873 case MotionEvent.ACTION_POINTER_UP: 874 onSecondaryPointerUp(ev); 875 break; 876 } 877 878 /* 879 * The only time we want to intercept motion events is if we are in the 880 * drag mode. 881 */ 882 return mTouchState != TOUCH_STATE_REST; 883 } 884 885 private void onSecondaryPointerUp(MotionEvent ev) { 886 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 887 MotionEvent.ACTION_POINTER_INDEX_SHIFT; 888 final int pointerId = ev.getPointerId(pointerIndex); 889 if (pointerId == mActivePointerId) { 890 // This was our active pointer going up. Choose a new 891 // active pointer and adjust accordingly. 892 // TODO: Make this decision more intelligent. 893 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 894 mLastMotionX = ev.getX(newPointerIndex); 895 mLastMotionY = ev.getY(newPointerIndex); 896 mActivePointerId = ev.getPointerId(newPointerIndex); 897 if (mVelocityTracker != null) { 898 mVelocityTracker.clear(); 899 } 900 } 901 } 902 903 /** 904 * If one of our descendant views decides that it could be focused now, only 905 * pass that along if it's on the current screen. 906 * 907 * This happens when live folders requery, and if they're off screen, they 908 * end up calling requestFocus, which pulls it on screen. 909 */ 910 @Override 911 public void focusableViewAvailable(View focused) { 912 View current = getChildAt(mCurrentScreen); 913 View v = focused; 914 while (true) { 915 if (v == current) { 916 super.focusableViewAvailable(focused); 917 return; 918 } 919 if (v == this) { 920 return; 921 } 922 ViewParent parent = v.getParent(); 923 if (parent instanceof View) { 924 v = (View) v.getParent(); 925 } else { 926 return; 927 } 928 } 929 } 930 931 void enableChildrenCache(int fromScreen, int toScreen) { 932 if (fromScreen > toScreen) { 933 final int temp = fromScreen; 934 fromScreen = toScreen; 935 toScreen = temp; 936 } 937 938 final int screenCount = getChildCount(); 939 940 fromScreen = Math.max(fromScreen, 0); 941 toScreen = Math.min(toScreen, screenCount - 1); 942 943 for (int i = fromScreen; i <= toScreen; i++) { 944 final CellLayout layout = (CellLayout) getChildAt(i); 945 layout.setChildrenDrawnWithCacheEnabled(true); 946 layout.setChildrenDrawingCacheEnabled(true); 947 } 948 } 949 950 void clearChildrenCache() { 951 final int screenCount = getChildCount(); 952 for (int i = 0; i < screenCount; i++) { 953 final CellLayout layout = (CellLayout) getChildAt(i); 954 layout.setChildrenDrawnWithCacheEnabled(false); 955 } 956 } 957 958 @Override 959 public boolean onTouchEvent(MotionEvent ev) { 960 961 if (mLauncher.isWorkspaceLocked()) { 962 return false; // We don't want the events. Let them fall through to the all apps view. 963 } 964 if (mLauncher.isAllAppsVisible()) { 965 // Cancel any scrolling that is in progress. 966 if (!mScroller.isFinished()) { 967 mScroller.abortAnimation(); 968 } 969 snapToScreen(mCurrentScreen); 970 return false; // We don't want the events. Let them fall through to the all apps view. 971 } 972 973 if (mVelocityTracker == null) { 974 mVelocityTracker = VelocityTracker.obtain(); 975 } 976 mVelocityTracker.addMovement(ev); 977 978 final int action = ev.getAction(); 979 980 switch (action & MotionEvent.ACTION_MASK) { 981 case MotionEvent.ACTION_DOWN: 982 /* 983 * If being flinged and user touches, stop the fling. isFinished 984 * will be false if being flinged. 985 */ 986 if (!mScroller.isFinished()) { 987 mScroller.abortAnimation(); 988 } 989 990 // Remember where the motion event started 991 mLastMotionX = ev.getX(); 992 mActivePointerId = ev.getPointerId(0); 993 if (mTouchState == TOUCH_STATE_SCROLLING) { 994 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 995 } 996 break; 997 case MotionEvent.ACTION_MOVE: 998 if (mTouchState == TOUCH_STATE_SCROLLING) { 999 // Scroll to follow the motion event 1000 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1001 final float x = ev.getX(pointerIndex); 1002 final float deltaX = mLastMotionX - x; 1003 mLastMotionX = x; 1004 1005 if (deltaX < 0) { 1006 if (mTouchX > 0) { 1007 mTouchX += Math.max(-mTouchX, deltaX); 1008 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1009 invalidate(); 1010 } 1011 } else if (deltaX > 0) { 1012 final float availableToScroll = getChildAt(getChildCount() - 1).getRight() - 1013 mTouchX - getWidth(); 1014 if (availableToScroll > 0) { 1015 mTouchX += Math.min(availableToScroll, deltaX); 1016 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1017 invalidate(); 1018 } 1019 } else { 1020 awakenScrollBars(); 1021 } 1022 } 1023 break; 1024 case MotionEvent.ACTION_UP: 1025 if (mTouchState == TOUCH_STATE_SCROLLING) { 1026 final VelocityTracker velocityTracker = mVelocityTracker; 1027 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1028 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); 1029 1030 final int screenWidth = getWidth(); 1031 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; 1032 final float scrolledPos = (float) mScrollX / screenWidth; 1033 1034 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 1035 // Fling hard enough to move left. 1036 // Don't fling across more than one screen at a time. 1037 final int bound = scrolledPos < whichScreen ? 1038 mCurrentScreen - 1 : mCurrentScreen; 1039 snapToScreen(Math.min(whichScreen, bound), velocityX, true); 1040 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { 1041 // Fling hard enough to move right 1042 // Don't fling across more than one screen at a time. 1043 final int bound = scrolledPos > whichScreen ? 1044 mCurrentScreen + 1 : mCurrentScreen; 1045 snapToScreen(Math.max(whichScreen, bound), velocityX, true); 1046 } else { 1047 snapToScreen(whichScreen, 0, true); 1048 } 1049 1050 if (mVelocityTracker != null) { 1051 mVelocityTracker.recycle(); 1052 mVelocityTracker = null; 1053 } 1054 } 1055 mTouchState = TOUCH_STATE_REST; 1056 mActivePointerId = INVALID_POINTER; 1057 break; 1058 case MotionEvent.ACTION_CANCEL: 1059 mTouchState = TOUCH_STATE_REST; 1060 mActivePointerId = INVALID_POINTER; 1061 break; 1062 case MotionEvent.ACTION_POINTER_UP: 1063 onSecondaryPointerUp(ev); 1064 break; 1065 } 1066 1067 return true; 1068 } 1069 1070 public boolean isSmall() { 1071 return mIsSmall; 1072 } 1073 1074 void shrinkToTop() { 1075 shrink(SHRINK_TO_TOP, true); 1076 } 1077 1078 void shrinkToMiddle() { 1079 shrink(SHRINK_TO_MIDDLE, true); 1080 } 1081 1082 void shrinkToBottom() { 1083 shrinkToBottom(true); 1084 } 1085 1086 void shrinkToBottom(boolean animated) { 1087 if (mFirstLayout) { 1088 // (mFirstLayout == "first layout has not happened yet") 1089 // if we get a call to shrink() as part of our initialization (for example, if 1090 // Launcher is started in All Apps mode) then we need to wait for a layout call 1091 // to get our width so we can layout the mini-screen views correctly 1092 mWaitingToShrinkToBottom = true; 1093 } else { 1094 shrink(SHRINK_TO_BOTTOM, animated); 1095 } 1096 } 1097 1098 // we use this to shrink the workspace for the all apps view and the customize view 1099 private void shrink(int shrinkPosition, boolean animated) { 1100 mIsSmall = true; 1101 final Resources res = getResources(); 1102 final int screenWidth = getWidth(); 1103 final int screenHeight = getHeight(); 1104 final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth); 1105 final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight); 1106 final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing); 1107 1108 final int screenCount = getChildCount(); 1109 float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing; 1110 1111 float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); 1112 if (shrinkPosition == SHRINK_TO_BOTTOM) { 1113 newY = screenHeight - newY - scaledScreenHeight; 1114 } else if (shrinkPosition == SHRINK_TO_MIDDLE) { 1115 newY = screenHeight / 2 - scaledScreenHeight / 2; 1116 } 1117 1118 // We animate all the screens to the centered position in workspace 1119 // At the same time, the screens become greyed/dimmed 1120 1121 // newX is initialized to the left-most position of the centered screens 1122 float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2; 1123 Sequencer s = new Sequencer(); 1124 for (int i = 0; i < screenCount; i++) { 1125 CellLayout cl = (CellLayout) getChildAt(i); 1126 cl.setPivotX(0.0f); 1127 cl.setPivotY(0.0f); 1128 if (animated) { 1129 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 1130 s.playTogether( 1131 new PropertyAnimator(duration, cl, "x", newX), 1132 new PropertyAnimator(duration, cl, "y", newY), 1133 new PropertyAnimator(duration, cl, "scaleX", SHRINK_FACTOR), 1134 new PropertyAnimator(duration, cl, "scaleY", SHRINK_FACTOR), 1135 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 1.0f)); 1136 } else { 1137 cl.setX((int)newX); 1138 cl.setY((int)newY); 1139 cl.setScaleX(SHRINK_FACTOR); 1140 cl.setScaleY(SHRINK_FACTOR); 1141 cl.setDimmedBitmapAlpha(1.0f); 1142 } 1143 // increment newX for the next screen 1144 newX += scaledScreenWidth + scaledSpacing; 1145 cl.setOnInterceptTouchListener(this); 1146 } 1147 setChildrenDrawnWithCacheEnabled(true); 1148 if (animated) s.start(); 1149 } 1150 1151 // We call this when we trigger an unshrink by clicking on the CellLayout cl 1152 private void unshrink(CellLayout clThatWasClicked) { 1153 int newCurrentScreen = mCurrentScreen; 1154 final int screenCount = getChildCount(); 1155 for (int i = 0; i < screenCount; i++) { 1156 if (getChildAt(i) == clThatWasClicked) { 1157 newCurrentScreen = i; 1158 } 1159 } 1160 unshrink(newCurrentScreen); 1161 } 1162 1163 private void unshrink(int newCurrentScreen) { 1164 if (mIsSmall) { 1165 int delta = (newCurrentScreen - mCurrentScreen)*getWidth(); 1166 1167 final int screenCount = getChildCount(); 1168 for (int i = 0; i < screenCount; i++) { 1169 CellLayout cl = (CellLayout) getChildAt(i); 1170 cl.setX(cl.getX() + delta); 1171 } 1172 mScrollX = newCurrentScreen * getWidth(); 1173 1174 unshrink(); 1175 setCurrentScreen(newCurrentScreen); 1176 } 1177 } 1178 1179 void unshrink() { 1180 unshrink(true); 1181 } 1182 1183 void unshrink(boolean animated) { 1184 if (mIsSmall) { 1185 Sequencer s = new Sequencer(); 1186 final int screenCount = getChildCount(); 1187 1188 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 1189 for (int i = 0; i < screenCount; i++) { 1190 final CellLayout cl = (CellLayout)getChildAt(i); 1191 cl.setPivotX(0.0f); 1192 cl.setPivotY(0.0f); 1193 if (animated) { 1194 s.playTogether( 1195 new PropertyAnimator(duration, cl, "translationX", 0.0f), 1196 new PropertyAnimator(duration, cl, "translationY", 0.0f), 1197 new PropertyAnimator(duration, cl, "scaleX", 1.0f), 1198 new PropertyAnimator(duration, cl, "scaleY", 1.0f), 1199 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f)); 1200 } else { 1201 cl.setTranslationX(0.0f); 1202 cl.setTranslationY(0.0f); 1203 cl.setScaleX(1.0f); 1204 cl.setScaleY(1.0f); 1205 cl.setDimmedBitmapAlpha(0.0f); 1206 } 1207 } 1208 s.addListener(mUnshrinkAnimationListener); 1209 s.start(); 1210 } 1211 } 1212 1213 void snapToScreen(int whichScreen) { 1214 snapToScreen(whichScreen, 0, false); 1215 } 1216 1217 private void snapToScreen(int whichScreen, int velocity, boolean settle) { 1218 // if (!mScroller.isFinished()) return; 1219 1220 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 1221 1222 enableChildrenCache(mCurrentScreen, whichScreen); 1223 1224 mNextScreen = whichScreen; 1225 1226 if (mPreviousIndicator != null) { 1227 mPreviousIndicator.setLevel(mNextScreen); 1228 mNextIndicator.setLevel(mNextScreen); 1229 } 1230 1231 View focusedChild = getFocusedChild(); 1232 if (focusedChild != null && whichScreen != mCurrentScreen && 1233 focusedChild == getChildAt(mCurrentScreen)) { 1234 focusedChild.clearFocus(); 1235 } 1236 1237 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); 1238 final int newX = whichScreen * getWidth(); 1239 final int delta = newX - mScrollX; 1240 int duration = (screenDelta + 1) * 100; 1241 1242 if (!mScroller.isFinished()) { 1243 mScroller.abortAnimation(); 1244 } 1245 1246 if (settle) { 1247 mScrollInterpolator.setDistance(screenDelta); 1248 } else { 1249 mScrollInterpolator.disableSettle(); 1250 } 1251 1252 velocity = Math.abs(velocity); 1253 if (velocity > 0) { 1254 duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) 1255 * FLING_VELOCITY_INFLUENCE; 1256 } else { 1257 duration += 100; 1258 } 1259 1260 awakenScrollBars(duration); 1261 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 1262 invalidate(); 1263 } 1264 1265 void startDrag(CellLayout.CellInfo cellInfo) { 1266 View child = cellInfo.cell; 1267 1268 // Make sure the drag was started by a long press as opposed to a long click. 1269 if (!child.isInTouchMode()) { 1270 return; 1271 } 1272 1273 mDragInfo = cellInfo; 1274 mDragInfo.screen = mCurrentScreen; 1275 1276 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); 1277 1278 current.onDragChild(child); 1279 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 1280 invalidate(); 1281 } 1282 1283 @Override 1284 protected Parcelable onSaveInstanceState() { 1285 final SavedState state = new SavedState(super.onSaveInstanceState()); 1286 state.currentScreen = mCurrentScreen; 1287 return state; 1288 } 1289 1290 @Override 1291 protected void onRestoreInstanceState(Parcelable state) { 1292 SavedState savedState = (SavedState) state; 1293 super.onRestoreInstanceState(savedState.getSuperState()); 1294 if (savedState.currentScreen != -1) { 1295 setCurrentScreen(savedState.currentScreen, false); 1296 Launcher.setScreen(mCurrentScreen); 1297 } 1298 } 1299 1300 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { 1301 addApplicationShortcut(info, cellInfo, false); 1302 } 1303 1304 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, 1305 boolean insertAtFirst) { 1306 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); 1307 final int[] result = new int[2]; 1308 1309 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); 1310 onDropExternal(result[0], result[1], info, layout, insertAtFirst); 1311 } 1312 1313 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1314 DragView dragView, Object dragInfo) { 1315 CellLayout cellLayout = getCurrentDropLayout(); 1316 int originX = x - xOffset; 1317 int originY = y - yOffset; 1318 if (mIsSmall) { 1319 // find out which target layout is over 1320 final float[] localXY = mTempDragCoordinates; 1321 localXY[0] = originX; 1322 localXY[1] = originY; 1323 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1324 // we need to subtract left/top here because DragController already adds 1325 // dragRegionLeft/Top to xOffset and yOffset 1326 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1327 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1328 cellLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1329 if (cellLayout == null) { 1330 // cancel the drag if we're not over a mini-screen at time of drop 1331 // TODO: maybe add a nice fade here? 1332 return; 1333 } 1334 // localXY will be transformed into the local screen's coordinate space; save that info 1335 originX = (int)localXY[0]; 1336 originY = (int)localXY[1]; 1337 } 1338 if (source != this) { 1339 onDropExternal(originX, originY, dragInfo, cellLayout); 1340 } else { 1341 // Move internally 1342 if (mDragInfo != null) { 1343 final View cell = mDragInfo.cell; 1344 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1345 if (index != mDragInfo.screen) { 1346 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1347 originalCellLayout.removeView(cell); 1348 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 1349 mDragInfo.spanX, mDragInfo.spanY); 1350 } 1351 1352 mTargetCell = estimateDropCell(originX, originY, 1353 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 1354 mTargetCell); 1355 cellLayout.onDropChild(cell); 1356 1357 // update the item's position after drop 1358 final ItemInfo info = (ItemInfo) cell.getTag(); 1359 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell 1360 .getLayoutParams(); 1361 lp.cellX = mTargetCell[0]; 1362 lp.cellY = mTargetCell[1]; 1363 1364 LauncherModel.moveItemInDatabase(mLauncher, info, 1365 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 1366 lp.cellX, lp.cellY); 1367 } 1368 } 1369 } 1370 1371 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1372 int yOffset, DragView dragView, Object dragInfo) { 1373 } 1374 1375 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 1376 DragView dragView, Object dragInfo) { 1377 1378 // We may need to delegate the drag to a child view. If a 1x1 item 1379 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1380 // then drag events should be handled by that child. 1381 1382 ItemInfo item = (ItemInfo)dragInfo; 1383 CellLayout currentLayout = getCurrentDropLayout(); 1384 1385 int dragPointX, dragPointY; 1386 if (item.spanX == 1 && item.spanY == 1) { 1387 // For a 1x1, calculate the drop cell exactly as in onDragOver 1388 dragPointX = x - xOffset; 1389 dragPointY = y - yOffset; 1390 } else { 1391 // Otherwise, use the exact drag coordinates 1392 dragPointX = x; 1393 dragPointY = y; 1394 } 1395 1396 // If we are dragging over a cell that contains a DropTarget that will 1397 // accept the drop, delegate to that DropTarget. 1398 final int[] cellXY = mTempCell; 1399 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1400 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1401 if (child instanceof DropTarget) { 1402 DropTarget target = (DropTarget)child; 1403 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1404 return target; 1405 } 1406 } 1407 return null; 1408 } 1409 1410 // xy = upper left corner of item being dragged 1411 // bottomRightXy = lower right corner of item being dragged 1412 // This method will see which mini-screen is most overlapped by the item being dragged, and 1413 // return it. It will also transform the parameters xy and bottomRightXy into the local 1414 // coordinate space of the returned screen 1415 private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) { 1416 float x = xy[0]; 1417 float y = xy[1]; 1418 float right = bottomRightXy[0]; 1419 float bottom = bottomRightXy[1]; 1420 1421 float bestX = 0; 1422 float bestY = 0; 1423 float bestRight = 0; 1424 float bestBottom = 0; 1425 1426 Matrix inverseMatrix = new Matrix(); 1427 1428 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 1429 // with the item being dragged. 1430 final int screenCount = getChildCount(); 1431 CellLayout bestMatchingScreen = null; 1432 float bestOverlapSoFar = 0; 1433 for (int i = 0; i < screenCount; i++) { 1434 CellLayout cl = (CellLayout)getChildAt(i); 1435 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1436 float left = cl.getLeft(); 1437 float top = cl.getTop(); 1438 xy[0] = x + mScrollX - left; 1439 xy[1] = y + mScrollY - top; 1440 cl.getMatrix().invert(inverseMatrix); 1441 1442 bottomRightXy[0] = right + mScrollX - left; 1443 bottomRightXy[1] = bottom + mScrollY - top; 1444 1445 inverseMatrix.mapPoints(xy); 1446 inverseMatrix.mapPoints(bottomRightXy); 1447 1448 float dragRegionX = xy[0]; 1449 float dragRegionY = xy[1]; 1450 float dragRegionRight = bottomRightXy[0]; 1451 float dragRegionBottom = bottomRightXy[1]; 1452 1453 // Find the overlapping region 1454 float overlapLeft = Math.max(0f, dragRegionX); 1455 float overlapTop = Math.max(0f, dragRegionY); 1456 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 1457 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 1458 1459 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 1460 overlapTop >= 0 && overlapBottom <= cl.getHeight()) { 1461 // Calculate the size of the overlapping region 1462 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 1463 if (overlap > bestOverlapSoFar) { 1464 bestOverlapSoFar = overlap; 1465 bestMatchingScreen = cl; 1466 bestX = xy[0]; 1467 bestY = xy[1]; 1468 bestRight = bottomRightXy[0]; 1469 bestBottom = bottomRightXy[1]; 1470 } 1471 } 1472 } 1473 if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { 1474 if (mDragTargetLayout != null) { 1475 mDragTargetLayout.onDragComplete(); 1476 } 1477 mDragTargetLayout = bestMatchingScreen; 1478 } 1479 xy[0] = bestX; 1480 xy[1] = bestY; 1481 bottomRightXy[0] = bestRight; 1482 bottomRightXy[1] = bestBottom; 1483 return bestMatchingScreen; 1484 } 1485 1486 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1487 DragView dragView, Object dragInfo) { 1488 1489 final ItemInfo item = (ItemInfo)dragInfo; 1490 CellLayout currentLayout = getCurrentDropLayout(); 1491 1492 if (dragInfo instanceof LauncherAppWidgetInfo) { 1493 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1494 1495 if (widgetInfo.spanX == -1) { 1496 // Calculate the grid spans needed to fit this widget 1497 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 1498 item.spanX = spans[0]; 1499 item.spanY = spans[1]; 1500 } 1501 } 1502 int originX = x - xOffset; 1503 int originY = y - yOffset; 1504 if (mIsSmall) { 1505 // find out which mini screen the dragged item is over 1506 final float[] localXY = mTempDragCoordinates; 1507 localXY[0] = originX; 1508 localXY[1] = originY; 1509 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1510 1511 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1512 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1513 currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1514 if (currentLayout != null) { 1515 currentLayout.setHover(true); 1516 } 1517 1518 originX = (int)localXY[0]; 1519 originY = (int)localXY[1]; 1520 } 1521 1522 if (source != this) { 1523 // This is a hack to fix the point used to determine which cell an icon from the all 1524 // apps screen is over 1525 if (item != null && item.spanX == 1 && currentLayout != null) { 1526 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 1527 1528 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1529 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 1530 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 1531 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 1532 } 1533 } 1534 } 1535 if (currentLayout != mDragTargetLayout) { 1536 if (mDragTargetLayout != null) { 1537 mDragTargetLayout.onDragComplete(); 1538 } 1539 mDragTargetLayout = currentLayout; 1540 } 1541 1542 // only visualize the drop locations for moving icons within the home screen on tablet 1543 // on phone, we also visualize icons dragged in from All Apps 1544 if ((!LauncherApplication.isScreenXLarge() || source == this) 1545 && mDragTargetLayout != null) { 1546 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1547 mDragTargetLayout.visualizeDropLocation( 1548 child, originX, originY, item.spanX, item.spanY); 1549 } 1550 } 1551 1552 public void onDragExit(DragSource source, int x, int y, int xOffset, 1553 int yOffset, DragView dragView, Object dragInfo) { 1554 if (mDragTargetLayout != null) { 1555 mDragTargetLayout.onDragComplete(); 1556 mDragTargetLayout = null; 1557 } 1558 } 1559 1560 private void onDropExternal(int x, int y, Object dragInfo, 1561 CellLayout cellLayout) { 1562 onDropExternal(x, y, dragInfo, cellLayout, false); 1563 } 1564 1565 private void onDropExternal(int x, int y, Object dragInfo, 1566 CellLayout cellLayout, boolean insertAtFirst) { 1567 // Drag from somewhere else 1568 ItemInfo info = (ItemInfo) dragInfo; 1569 1570 View view = null; 1571 1572 switch (info.itemType) { 1573 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1574 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1575 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1576 // Came from all apps -- make a copy 1577 info = new ShortcutInfo((ApplicationInfo) info); 1578 } 1579 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1580 (ShortcutInfo) info); 1581 break; 1582 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1583 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1584 (ViewGroup) getChildAt(mCurrentScreen), 1585 ((UserFolderInfo) info)); 1586 break; 1587 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1588 cellLayout.setTagToCellInfoForPoint(x, y); 1589 int[] position = new int[2]; 1590 position[0] = x; 1591 position[1] = y; 1592 mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, 1593 cellLayout.getTag(), position); 1594 break; 1595 default: 1596 throw new IllegalStateException("Unknown item type: " 1597 + info.itemType); 1598 } 1599 1600 // If the view is null, it has already been added. 1601 if (view == null) { 1602 cellLayout.onDragComplete(); 1603 } else { 1604 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); 1605 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1606 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1607 cellLayout.onDropChild(view); 1608 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1609 1610 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1611 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, 1612 lp.cellX, lp.cellY); 1613 } 1614 } 1615 1616 /** 1617 * Return the current {@link CellLayout}, correctly picking the destination 1618 * screen while a scroll is in progress. 1619 */ 1620 private CellLayout getCurrentDropLayout() { 1621 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1622 return (CellLayout) getChildAt(index); 1623 } 1624 1625 /** 1626 * {@inheritDoc} 1627 */ 1628 public boolean acceptDrop(DragSource source, int x, int y, 1629 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1630 final CellLayout layout = getCurrentDropLayout(); 1631 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1632 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1633 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1634 1635 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1636 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1637 1638 if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { 1639 return true; 1640 } else { 1641 Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1642 return false; 1643 } 1644 } 1645 1646 /** 1647 * {@inheritDoc} 1648 */ 1649 public Rect estimateDropLocation(DragSource source, int x, int y, 1650 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { 1651 final CellLayout layout = getCurrentDropLayout(); 1652 1653 final CellLayout.CellInfo cellInfo = mDragInfo; 1654 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1655 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1656 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1657 1658 final Rect location = recycle != null ? recycle : new Rect(); 1659 1660 // Find drop cell and convert into rectangle 1661 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, 1662 spanY, ignoreView, layout, mTempCell); 1663 1664 if (dropCell == null) { 1665 return null; 1666 } 1667 1668 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); 1669 location.left = mTempEstimate[0]; 1670 location.top = mTempEstimate[1]; 1671 1672 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); 1673 location.right = mTempEstimate[0]; 1674 location.bottom = mTempEstimate[1]; 1675 1676 return location; 1677 } 1678 1679 /** 1680 * Calculate the nearest cell where the given object would be dropped. 1681 */ 1682 private int[] estimateDropCell(int pixelX, int pixelY, 1683 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1684 1685 final int[] cellXY = mTempCell; 1686 layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY); 1687 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1688 1689 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1690 // Find the best target drop location 1691 return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); 1692 } 1693 1694 /** 1695 * Estimate the size that a child with the given dimensions will take in the current screen. 1696 */ 1697 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1698 ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result); 1699 } 1700 1701 void setLauncher(Launcher launcher) { 1702 mLauncher = launcher; 1703 } 1704 1705 public void setDragController(DragController dragController) { 1706 mDragController = dragController; 1707 } 1708 1709 public void onDropCompleted(View target, boolean success) { 1710 if (success) { 1711 if (target != this && mDragInfo != null) { 1712 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1713 cellLayout.removeView(mDragInfo.cell); 1714 if (mDragInfo.cell instanceof DropTarget) { 1715 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1716 } 1717 // final Object tag = mDragInfo.cell.getTag(); 1718 } 1719 } else { 1720 if (mDragInfo != null) { 1721 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1722 cellLayout.onDropAborted(mDragInfo.cell); 1723 } 1724 } 1725 1726 mDragInfo = null; 1727 } 1728 1729 public void scrollLeft() { 1730 if (mScroller.isFinished()) { 1731 if (mCurrentScreen > 0) 1732 snapToScreen(mCurrentScreen - 1); 1733 } else { 1734 if (mNextScreen > 0) 1735 snapToScreen(mNextScreen - 1); 1736 } 1737 } 1738 1739 public void scrollRight() { 1740 if (mScroller.isFinished()) { 1741 if (mCurrentScreen < getChildCount() - 1) 1742 snapToScreen(mCurrentScreen + 1); 1743 } else { 1744 if (mNextScreen < getChildCount() - 1) 1745 snapToScreen(mNextScreen + 1); 1746 } 1747 } 1748 1749 public int getScreenForView(View v) { 1750 int result = -1; 1751 if (v != null) { 1752 ViewParent vp = v.getParent(); 1753 final int screenCount = getChildCount(); 1754 for (int i = 0; i < screenCount; i++) { 1755 if (vp == getChildAt(i)) { 1756 return i; 1757 } 1758 } 1759 } 1760 return result; 1761 } 1762 1763 public Folder getFolderForTag(Object tag) { 1764 final int screenCount = getChildCount(); 1765 for (int screen = 0; screen < screenCount; screen++) { 1766 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1767 int count = currentScreen.getChildCount(); 1768 for (int i = 0; i < count; i++) { 1769 View child = currentScreen.getChildAt(i); 1770 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1771 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1772 Folder f = (Folder) child; 1773 if (f.getInfo() == tag && f.getInfo().opened) { 1774 return f; 1775 } 1776 } 1777 } 1778 } 1779 return null; 1780 } 1781 1782 public View getViewForTag(Object tag) { 1783 int screenCount = getChildCount(); 1784 for (int screen = 0; screen < screenCount; screen++) { 1785 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1786 int count = currentScreen.getChildCount(); 1787 for (int i = 0; i < count; i++) { 1788 View child = currentScreen.getChildAt(i); 1789 if (child.getTag() == tag) { 1790 return child; 1791 } 1792 } 1793 } 1794 return null; 1795 } 1796 1797 /** 1798 * @return True is long presses are still allowed for the current touch 1799 */ 1800 public boolean allowLongPress() { 1801 return mAllowLongPress; 1802 } 1803 1804 /** 1805 * Set true to allow long-press events to be triggered, usually checked by 1806 * {@link Launcher} to accept or block dpad-initiated long-presses. 1807 */ 1808 public void setAllowLongPress(boolean allowLongPress) { 1809 mAllowLongPress = allowLongPress; 1810 } 1811 1812 void removeItems(final ArrayList<ApplicationInfo> apps) { 1813 final int screenCount = getChildCount(); 1814 final PackageManager manager = getContext().getPackageManager(); 1815 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1816 1817 final HashSet<String> packageNames = new HashSet<String>(); 1818 final int appCount = apps.size(); 1819 for (int i = 0; i < appCount; i++) { 1820 packageNames.add(apps.get(i).componentName.getPackageName()); 1821 } 1822 1823 for (int i = 0; i < screenCount; i++) { 1824 final CellLayout layout = (CellLayout) getChildAt(i); 1825 1826 // Avoid ANRs by treating each screen separately 1827 post(new Runnable() { 1828 public void run() { 1829 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1830 childrenToRemove.clear(); 1831 1832 int childCount = layout.getChildCount(); 1833 for (int j = 0; j < childCount; j++) { 1834 final View view = layout.getChildAt(j); 1835 Object tag = view.getTag(); 1836 1837 if (tag instanceof ShortcutInfo) { 1838 final ShortcutInfo info = (ShortcutInfo) tag; 1839 final Intent intent = info.intent; 1840 final ComponentName name = intent.getComponent(); 1841 1842 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1843 for (String packageName: packageNames) { 1844 if (packageName.equals(name.getPackageName())) { 1845 // TODO: This should probably be done on a worker thread 1846 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1847 childrenToRemove.add(view); 1848 } 1849 } 1850 } 1851 } else if (tag instanceof UserFolderInfo) { 1852 final UserFolderInfo info = (UserFolderInfo) tag; 1853 final ArrayList<ShortcutInfo> contents = info.contents; 1854 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1855 final int contentsCount = contents.size(); 1856 boolean removedFromFolder = false; 1857 1858 for (int k = 0; k < contentsCount; k++) { 1859 final ShortcutInfo appInfo = contents.get(k); 1860 final Intent intent = appInfo.intent; 1861 final ComponentName name = intent.getComponent(); 1862 1863 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1864 for (String packageName: packageNames) { 1865 if (packageName.equals(name.getPackageName())) { 1866 toRemove.add(appInfo); 1867 // TODO: This should probably be done on a worker thread 1868 LauncherModel.deleteItemFromDatabase( 1869 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 // TODO: This should probably be done on a worker thread 1892 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1893 childrenToRemove.add(view); 1894 } 1895 } 1896 } 1897 } else if (tag instanceof LauncherAppWidgetInfo) { 1898 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1899 final AppWidgetProviderInfo provider = 1900 widgets.getAppWidgetInfo(info.appWidgetId); 1901 if (provider != null) { 1902 for (String packageName: packageNames) { 1903 if (packageName.equals(provider.provider.getPackageName())) { 1904 // TODO: This should probably be done on a worker thread 1905 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1906 childrenToRemove.add(view); 1907 } 1908 } 1909 } 1910 } 1911 } 1912 1913 childCount = childrenToRemove.size(); 1914 for (int j = 0; j < childCount; j++) { 1915 View child = childrenToRemove.get(j); 1916 layout.removeViewInLayout(child); 1917 if (child instanceof DropTarget) { 1918 mDragController.removeDropTarget((DropTarget)child); 1919 } 1920 } 1921 1922 if (childCount > 0) { 1923 layout.requestLayout(); 1924 layout.invalidate(); 1925 } 1926 } 1927 }); 1928 } 1929 } 1930 1931 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1932 final PackageManager pm = mLauncher.getPackageManager(); 1933 1934 final int screenCount = getChildCount(); 1935 for (int i = 0; i < screenCount; i++) { 1936 final CellLayout layout = (CellLayout) getChildAt(i); 1937 int childCount = layout.getChildCount(); 1938 for (int j = 0; j < childCount; j++) { 1939 final View view = layout.getChildAt(j); 1940 Object tag = view.getTag(); 1941 if (tag instanceof ShortcutInfo) { 1942 ShortcutInfo info = (ShortcutInfo)tag; 1943 // We need to check for ACTION_MAIN otherwise getComponent() might 1944 // return null for some shortcuts (for instance, for shortcuts to 1945 // web pages.) 1946 final Intent intent = info.intent; 1947 final ComponentName name = intent.getComponent(); 1948 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1949 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1950 final int appCount = apps.size(); 1951 for (int k = 0; k < appCount; k++) { 1952 ApplicationInfo app = apps.get(k); 1953 if (app.componentName.equals(name)) { 1954 info.setIcon(mIconCache.getIcon(info.intent)); 1955 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1956 new FastBitmapDrawable(info.getIcon(mIconCache)), 1957 null, null); 1958 } 1959 } 1960 } 1961 } 1962 } 1963 } 1964 } 1965 1966 void moveToDefaultScreen(boolean animate) { 1967 if (animate) { 1968 if (mIsSmall) { 1969 unshrink(mDefaultScreen); 1970 } else { 1971 snapToScreen(mDefaultScreen); 1972 } 1973 } else { 1974 setCurrentScreen(mDefaultScreen); 1975 } 1976 getChildAt(mDefaultScreen).requestFocus(); 1977 } 1978 1979 void setIndicators(Drawable previous, Drawable next) { 1980 mPreviousIndicator = previous; 1981 mNextIndicator = next; 1982 previous.setLevel(mCurrentScreen); 1983 next.setLevel(mCurrentScreen); 1984 } 1985 1986 public static class SavedState extends BaseSavedState { 1987 int currentScreen = -1; 1988 1989 SavedState(Parcelable superState) { 1990 super(superState); 1991 } 1992 1993 private SavedState(Parcel in) { 1994 super(in); 1995 currentScreen = in.readInt(); 1996 } 1997 1998 @Override 1999 public void writeToParcel(Parcel out, int flags) { 2000 super.writeToParcel(out, flags); 2001 out.writeInt(currentScreen); 2002 } 2003 2004 public static final Parcelable.Creator<SavedState> CREATOR = 2005 new Parcelable.Creator<SavedState>() { 2006 public SavedState createFromParcel(Parcel in) { 2007 return new SavedState(in); 2008 } 2009 2010 public SavedState[] newArray(int size) { 2011 return new SavedState[size]; 2012 } 2013 }; 2014 } 2015} 2016