Workspace.java revision d40613a693ea00bd767d60bfdbdcebe4cbf7bbdd
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.app.WallpaperManager; 23import android.appwidget.AppWidgetManager; 24import android.appwidget.AppWidgetProviderInfo; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.pm.PackageManager; 29import android.content.pm.ProviderInfo; 30import android.content.res.TypedArray; 31import android.graphics.Canvas; 32import android.graphics.Rect; 33import android.graphics.drawable.Drawable; 34import android.net.Uri; 35import android.os.IBinder; 36import android.os.Parcel; 37import android.os.Parcelable; 38import android.util.AttributeSet; 39import android.view.MotionEvent; 40import android.view.VelocityTracker; 41import android.view.View; 42import android.view.ViewConfiguration; 43import android.view.ViewGroup; 44import android.view.ViewParent; 45import android.view.animation.Interpolator; 46import android.widget.Scroller; 47import android.widget.TextView; 48 49import com.android.launcher.R; 50 51/** 52 * The workspace is a wide area with a wallpaper and a finite number of screens. Each 53 * screen contains a number of icons, folders or widgets the user can interact with. 54 * A workspace is meant to be used with a fixed width only. 55 */ 56public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller { 57 @SuppressWarnings({"UnusedDeclaration"}) 58 private static final String TAG = "Launcher.Workspace"; 59 private static final int INVALID_SCREEN = -1; 60 61 /** 62 * The velocity at which a fling gesture will cause us to snap to the next screen 63 */ 64 private static final int SNAP_VELOCITY = 600; 65 66 private final WallpaperManager mWallpaperManager; 67 68 private int mDefaultScreen; 69 70 private boolean mFirstLayout = true; 71 72 private int mCurrentScreen; 73 private int mNextScreen = INVALID_SCREEN; 74 private Scroller mScroller; 75 private VelocityTracker mVelocityTracker; 76 77 /** 78 * CellInfo for the cell that is currently being dragged 79 */ 80 private CellLayout.CellInfo mDragInfo; 81 82 /** 83 * Target drop area calculated during last acceptDrop call. 84 */ 85 private int[] mTargetCell = null; 86 87 private float mLastMotionX; 88 private float mLastMotionY; 89 90 private final static int TOUCH_STATE_REST = 0; 91 private final static int TOUCH_STATE_SCROLLING = 1; 92 93 private int mTouchState = TOUCH_STATE_REST; 94 95 private OnLongClickListener mLongClickListener; 96 97 private Launcher mLauncher; 98 private IconCache mIconCache; 99 private DragController mDragController; 100 101 /** 102 * Cache of vacant cells, used during drag events and invalidated as needed. 103 */ 104 private CellLayout.CellInfo mVacantCache = null; 105 106 private int[] mTempCell = new int[2]; 107 private int[] mTempEstimate = new int[2]; 108 109 private boolean mAllowLongPress = true; 110 111 private int mTouchSlop; 112 private int mPagingTouchSlop; 113 private int mMaximumVelocity; 114 115 private static final int INVALID_POINTER = -1; 116 117 private int mActivePointerId = INVALID_POINTER; 118 119 private Drawable mPreviousIndicator; 120 private Drawable mNextIndicator; 121 122 private WorkspaceOvershootInterpolator mScrollInterpolator; 123 124 private static final float BASELINE_FLING_VELOCITY = 2500.f; 125 private static final float FLING_VELOCITY_INFLUENCE = 0.4f; 126 127 private static class WorkspaceOvershootInterpolator implements Interpolator { 128 private static final float DEFAULT_TENSION = 1.3f; 129 private float mTension; 130 131 public WorkspaceOvershootInterpolator() { 132 mTension = DEFAULT_TENSION; 133 } 134 135 public void setDistance(int distance) { 136 mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; 137 } 138 139 public void disableSettle() { 140 mTension = 0.f; 141 } 142 143 public float getInterpolation(float t) { 144 // _o(t) = t * t * ((tension + 1) * t + tension) 145 // o(t) = _o(t - 1) + 1 146 t -= 1.0f; 147 return t * t * ((mTension + 1) * t + mTension) + 1.0f; 148 } 149 } 150 151 /** 152 * Used to inflate the Workspace from XML. 153 * 154 * @param context The application's context. 155 * @param attrs The attribtues set containing the Workspace's customization values. 156 */ 157 public Workspace(Context context, AttributeSet attrs) { 158 this(context, attrs, 0); 159 } 160 161 /** 162 * Used to inflate the Workspace from XML. 163 * 164 * @param context The application's context. 165 * @param attrs The attribtues set containing the Workspace's customization values. 166 * @param defStyle Unused. 167 */ 168 public Workspace(Context context, AttributeSet attrs, int defStyle) { 169 super(context, attrs, defStyle); 170 171 mWallpaperManager = WallpaperManager.getInstance(context); 172 173 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); 174 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); 175 a.recycle(); 176 177 setHapticFeedbackEnabled(false); 178 initWorkspace(); 179 } 180 181 /** 182 * Initializes various states for this workspace. 183 */ 184 private void initWorkspace() { 185 Context context = getContext(); 186 mScrollInterpolator = new WorkspaceOvershootInterpolator(); 187 mScroller = new Scroller(context, mScrollInterpolator); 188 mCurrentScreen = mDefaultScreen; 189 Launcher.setScreen(mCurrentScreen); 190 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 191 mIconCache = app.getIconCache(); 192 193 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 194 mTouchSlop = configuration.getScaledTouchSlop(); 195 mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); 196 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 197 } 198 199 @Override 200 public void addView(View child, int index, LayoutParams params) { 201 if (!(child instanceof CellLayout)) { 202 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 203 } 204 super.addView(child, index, params); 205 } 206 207 @Override 208 public void addView(View child) { 209 if (!(child instanceof CellLayout)) { 210 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 211 } 212 super.addView(child); 213 } 214 215 @Override 216 public void addView(View child, int index) { 217 if (!(child instanceof CellLayout)) { 218 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 219 } 220 super.addView(child, index); 221 } 222 223 @Override 224 public void addView(View child, int width, int height) { 225 if (!(child instanceof CellLayout)) { 226 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 227 } 228 super.addView(child, width, height); 229 } 230 231 @Override 232 public void addView(View child, LayoutParams params) { 233 if (!(child instanceof CellLayout)) { 234 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 235 } 236 super.addView(child, params); 237 } 238 239 /** 240 * @return The open folder on the current screen, or null if there is none 241 */ 242 Folder getOpenFolder() { 243 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); 244 int count = currentScreen.getChildCount(); 245 for (int i = 0; i < count; i++) { 246 View child = currentScreen.getChildAt(i); 247 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 248 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 249 return (Folder) child; 250 } 251 } 252 return null; 253 } 254 255 ArrayList<Folder> getOpenFolders() { 256 final int screens = getChildCount(); 257 ArrayList<Folder> folders = new ArrayList<Folder>(screens); 258 259 for (int screen = 0; screen < screens; screen++) { 260 CellLayout currentScreen = (CellLayout) getChildAt(screen); 261 int count = currentScreen.getChildCount(); 262 for (int i = 0; i < count; i++) { 263 View child = currentScreen.getChildAt(i); 264 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 265 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 266 folders.add((Folder) child); 267 break; 268 } 269 } 270 } 271 272 return folders; 273 } 274 275 boolean isDefaultScreenShowing() { 276 return mCurrentScreen == mDefaultScreen; 277 } 278 279 /** 280 * Returns the index of the currently displayed screen. 281 * 282 * @return The index of the currently displayed screen. 283 */ 284 int getCurrentScreen() { 285 return mCurrentScreen; 286 } 287 288 /** 289 * Sets the current screen. 290 * 291 * @param currentScreen 292 */ 293 void setCurrentScreen(int currentScreen) { 294 if (!mScroller.isFinished()) mScroller.abortAnimation(); 295 clearVacantCache(); 296 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); 297 mPreviousIndicator.setLevel(mCurrentScreen); 298 mNextIndicator.setLevel(mCurrentScreen); 299 scrollTo(mCurrentScreen * getWidth(), 0); 300 updateWallpaperOffset(); 301 invalidate(); 302 } 303 304 /** 305 * Adds the specified child in the current screen. The position and dimension of 306 * the child are defined by x, y, spanX and spanY. 307 * 308 * @param child The child to add in one of the workspace's screens. 309 * @param x The X position of the child in the screen's grid. 310 * @param y The Y position of the child in the screen's grid. 311 * @param spanX The number of cells spanned horizontally by the child. 312 * @param spanY The number of cells spanned vertically by the child. 313 */ 314 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { 315 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false); 316 } 317 318 /** 319 * Adds the specified child in the current screen. The position and dimension of 320 * the child are defined by x, y, spanX and spanY. 321 * 322 * @param child The child to add in one of the workspace's screens. 323 * @param x The X position of the child in the screen's grid. 324 * @param y The Y position of the child in the screen's grid. 325 * @param spanX The number of cells spanned horizontally by the child. 326 * @param spanY The number of cells spanned vertically by the child. 327 * @param insert When true, the child is inserted at the beginning of the children list. 328 */ 329 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { 330 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert); 331 } 332 333 /** 334 * Adds the specified child in the specified screen. The position and dimension of 335 * the child are defined by x, y, spanX and spanY. 336 * 337 * @param child The child to add in one of the workspace's screens. 338 * @param screen The screen in which to add the child. 339 * @param x The X position of the child in the screen's grid. 340 * @param y The Y position of the child in the screen's grid. 341 * @param spanX The number of cells spanned horizontally by the child. 342 * @param spanY The number of cells spanned vertically by the child. 343 */ 344 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 345 addInScreen(child, screen, x, y, spanX, spanY, false); 346 } 347 348 /** 349 * Adds the specified child in the specified screen. The position and dimension of 350 * the child are defined by x, y, spanX and spanY. 351 * 352 * @param child The child to add in one of the workspace's screens. 353 * @param screen The screen in which to add the child. 354 * @param x The X position of the child in the screen's grid. 355 * @param y The Y position of the child in the screen's grid. 356 * @param spanX The number of cells spanned horizontally by the child. 357 * @param spanY The number of cells spanned vertically by the child. 358 * @param insert When true, the child is inserted at the beginning of the children list. 359 */ 360 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 361 if (screen < 0 || screen >= getChildCount()) { 362 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount()); 363 } 364 365 clearVacantCache(); 366 367 final CellLayout group = (CellLayout) getChildAt(screen); 368 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 369 if (lp == null) { 370 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 371 } else { 372 lp.cellX = x; 373 lp.cellY = y; 374 lp.cellHSpan = spanX; 375 lp.cellVSpan = spanY; 376 } 377 group.addView(child, insert ? 0 : -1, lp); 378 if (!(child instanceof Folder)) { 379 child.setHapticFeedbackEnabled(false); 380 child.setOnLongClickListener(mLongClickListener); 381 } 382 if (child instanceof DropTarget) { 383 mDragController.addDropTarget((DropTarget)child); 384 } 385 } 386 387 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) { 388 CellLayout group = (CellLayout) getChildAt(mCurrentScreen); 389 if (group != null) { 390 return group.findAllVacantCells(occupied, null); 391 } 392 return null; 393 } 394 395 private void clearVacantCache() { 396 if (mVacantCache != null) { 397 mVacantCache.clearVacantCells(); 398 mVacantCache = null; 399 } 400 } 401 402 /** 403 * Registers the specified listener on each screen contained in this workspace. 404 * 405 * @param l The listener used to respond to long clicks. 406 */ 407 @Override 408 public void setOnLongClickListener(OnLongClickListener l) { 409 mLongClickListener = l; 410 final int count = getChildCount(); 411 for (int i = 0; i < count; i++) { 412 getChildAt(i).setOnLongClickListener(l); 413 } 414 } 415 416 private void updateWallpaperOffset() { 417 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 418 } 419 420 private void updateWallpaperOffset(int scrollRange) { 421 IBinder token = getWindowToken(); 422 if (token != null) { 423 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 424 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 425 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 426 } 427 } 428 429 @Override 430 public void computeScroll() { 431 if (mScroller.computeScrollOffset()) { 432 mScrollX = mScroller.getCurrX(); 433 mScrollY = mScroller.getCurrY(); 434 updateWallpaperOffset(); 435 postInvalidate(); 436 } else if (mNextScreen != INVALID_SCREEN) { 437 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); 438 mPreviousIndicator.setLevel(mCurrentScreen); 439 mNextIndicator.setLevel(mCurrentScreen); 440 Launcher.setScreen(mCurrentScreen); 441 mNextScreen = INVALID_SCREEN; 442 clearChildrenCache(); 443 } 444 } 445 446 @Override 447 protected void dispatchDraw(Canvas canvas) { 448 boolean restore = false; 449 int restoreCount = 0; 450 451 // ViewGroup.dispatchDraw() supports many features we don't need: 452 // clip to padding, layout animation, animation listener, disappearing 453 // children, etc. The following implementation attempts to fast-track 454 // the drawing dispatch by drawing only what we know needs to be drawn. 455 456 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN; 457 // If we are not scrolling or flinging, draw only the current screen 458 if (fastDraw) { 459 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); 460 } else { 461 final long drawingTime = getDrawingTime(); 462 final float scrollPos = (float) mScrollX / getWidth(); 463 final int leftScreen = (int) scrollPos; 464 final int rightScreen = leftScreen + 1; 465 if (leftScreen >= 0) { 466 drawChild(canvas, getChildAt(leftScreen), drawingTime); 467 } 468 if (scrollPos != leftScreen && rightScreen < getChildCount()) { 469 drawChild(canvas, getChildAt(rightScreen), drawingTime); 470 } 471 } 472 473 if (restore) { 474 canvas.restoreToCount(restoreCount); 475 } 476 } 477 478 protected void onAttachedToWindow() { 479 super.onAttachedToWindow(); 480 computeScroll(); 481 mDragController.setWindowToken(getWindowToken()); 482 } 483 484 @Override 485 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 486 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 487 488 final int width = MeasureSpec.getSize(widthMeasureSpec); 489 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 490 if (widthMode != MeasureSpec.EXACTLY) { 491 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 492 } 493 494 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 495 if (heightMode != MeasureSpec.EXACTLY) { 496 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 497 } 498 499 // The children are given the same width and height as the workspace 500 final int count = getChildCount(); 501 for (int i = 0; i < count; i++) { 502 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 503 } 504 505 506 if (mFirstLayout) { 507 setHorizontalScrollBarEnabled(false); 508 scrollTo(mCurrentScreen * width, 0); 509 setHorizontalScrollBarEnabled(true); 510 updateWallpaperOffset(width * (getChildCount() - 1)); 511 mFirstLayout = false; 512 } 513 } 514 515 @Override 516 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 517 int childLeft = 0; 518 519 final int count = getChildCount(); 520 for (int i = 0; i < count; i++) { 521 final View child = getChildAt(i); 522 if (child.getVisibility() != View.GONE) { 523 final int childWidth = child.getMeasuredWidth(); 524 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 525 childLeft += childWidth; 526 } 527 } 528 } 529 530 @Override 531 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 532 int screen = indexOfChild(child); 533 if (screen != mCurrentScreen || !mScroller.isFinished()) { 534 if (!mLauncher.isWorkspaceLocked()) { 535 snapToScreen(screen); 536 } 537 return true; 538 } 539 return false; 540 } 541 542 @Override 543 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 544 if (!mLauncher.isAllAppsVisible()) { 545 final Folder openFolder = getOpenFolder(); 546 if (openFolder != null) { 547 return openFolder.requestFocus(direction, previouslyFocusedRect); 548 } else { 549 int focusableScreen; 550 if (mNextScreen != INVALID_SCREEN) { 551 focusableScreen = mNextScreen; 552 } else { 553 focusableScreen = mCurrentScreen; 554 } 555 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); 556 } 557 } 558 return false; 559 } 560 561 @Override 562 public boolean dispatchUnhandledMove(View focused, int direction) { 563 if (direction == View.FOCUS_LEFT) { 564 if (getCurrentScreen() > 0) { 565 snapToScreen(getCurrentScreen() - 1); 566 return true; 567 } 568 } else if (direction == View.FOCUS_RIGHT) { 569 if (getCurrentScreen() < getChildCount() - 1) { 570 snapToScreen(getCurrentScreen() + 1); 571 return true; 572 } 573 } 574 return super.dispatchUnhandledMove(focused, direction); 575 } 576 577 @Override 578 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 579 if (!mLauncher.isAllAppsVisible()) { 580 final Folder openFolder = getOpenFolder(); 581 if (openFolder == null) { 582 getChildAt(mCurrentScreen).addFocusables(views, direction); 583 if (direction == View.FOCUS_LEFT) { 584 if (mCurrentScreen > 0) { 585 getChildAt(mCurrentScreen - 1).addFocusables(views, direction); 586 } 587 } else if (direction == View.FOCUS_RIGHT){ 588 if (mCurrentScreen < getChildCount() - 1) { 589 getChildAt(mCurrentScreen + 1).addFocusables(views, direction); 590 } 591 } 592 } else { 593 openFolder.addFocusables(views, direction); 594 } 595 } 596 } 597 598 @Override 599 public boolean dispatchTouchEvent(MotionEvent ev) { 600 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 601 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) { 602 return false; 603 } 604 } 605 return super.dispatchTouchEvent(ev); 606 } 607 608 @Override 609 public boolean onInterceptTouchEvent(MotionEvent ev) { 610 final boolean workspaceLocked = mLauncher.isWorkspaceLocked(); 611 final boolean allAppsVisible = mLauncher.isAllAppsVisible(); 612 if (workspaceLocked || allAppsVisible) { 613 return false; // We don't want the events. Let them fall through to the all apps view. 614 } 615 616 /* 617 * This method JUST determines whether we want to intercept the motion. 618 * If we return true, onTouchEvent will be called and we do the actual 619 * scrolling there. 620 */ 621 622 /* 623 * Shortcut the most recurring case: the user is in the dragging 624 * state and he is moving his finger. We want to intercept this 625 * motion. 626 */ 627 final int action = ev.getAction(); 628 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { 629 return true; 630 } 631 632 if (mVelocityTracker == null) { 633 mVelocityTracker = VelocityTracker.obtain(); 634 } 635 mVelocityTracker.addMovement(ev); 636 637 switch (action & MotionEvent.ACTION_MASK) { 638 case MotionEvent.ACTION_MOVE: { 639 /* 640 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 641 * whether the user has moved far enough from his original down touch. 642 */ 643 644 /* 645 * Locally do absolute value. mLastMotionX is set to the y value 646 * of the down event. 647 */ 648 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 649 final float x = ev.getX(pointerIndex); 650 final float y = ev.getY(pointerIndex); 651 final int xDiff = (int) Math.abs(x - mLastMotionX); 652 final int yDiff = (int) Math.abs(y - mLastMotionY); 653 654 final int touchSlop = mTouchSlop; 655 boolean xPaged = xDiff > mPagingTouchSlop; 656 boolean xMoved = xDiff > touchSlop; 657 boolean yMoved = yDiff > touchSlop; 658 659 if (xMoved || yMoved) { 660 661 if (xPaged) { 662 // Scroll if the user moved far enough along the X axis 663 mTouchState = TOUCH_STATE_SCROLLING; 664 mLastMotionX = x; 665 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 666 } 667 // Either way, cancel any pending longpress 668 if (mAllowLongPress) { 669 mAllowLongPress = false; 670 // Try canceling the long press. It could also have been scheduled 671 // by a distant descendant, so use the mAllowLongPress flag to block 672 // everything 673 final View currentScreen = getChildAt(mCurrentScreen); 674 currentScreen.cancelLongPress(); 675 } 676 } 677 break; 678 } 679 680 case MotionEvent.ACTION_DOWN: { 681 final float x = ev.getX(); 682 final float y = ev.getY(); 683 // Remember location of down touch 684 mLastMotionX = x; 685 mLastMotionY = y; 686 mActivePointerId = ev.getPointerId(0); 687 mAllowLongPress = true; 688 689 /* 690 * If being flinged and user touches the screen, initiate drag; 691 * otherwise don't. mScroller.isFinished should be false when 692 * being flinged. 693 */ 694 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 695 break; 696 } 697 698 case MotionEvent.ACTION_CANCEL: 699 case MotionEvent.ACTION_UP: 700 701 if (mTouchState != TOUCH_STATE_SCROLLING) { 702 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 703 if (!currentScreen.lastDownOnOccupiedCell()) { 704 getLocationOnScreen(mTempCell); 705 // Send a tap to the wallpaper if the last down was on empty space 706 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 707 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 708 "android.wallpaper.tap", 709 mTempCell[0] + (int) ev.getX(pointerIndex), 710 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); 711 } 712 } 713 714 // Release the drag 715 clearChildrenCache(); 716 mTouchState = TOUCH_STATE_REST; 717 mActivePointerId = INVALID_POINTER; 718 mAllowLongPress = false; 719 720 if (mVelocityTracker != null) { 721 mVelocityTracker.recycle(); 722 mVelocityTracker = null; 723 } 724 725 break; 726 727 case MotionEvent.ACTION_POINTER_UP: 728 onSecondaryPointerUp(ev); 729 break; 730 } 731 732 /* 733 * The only time we want to intercept motion events is if we are in the 734 * drag mode. 735 */ 736 return mTouchState != TOUCH_STATE_REST; 737 } 738 739 private void onSecondaryPointerUp(MotionEvent ev) { 740 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 741 MotionEvent.ACTION_POINTER_INDEX_SHIFT; 742 final int pointerId = ev.getPointerId(pointerIndex); 743 if (pointerId == mActivePointerId) { 744 // This was our active pointer going up. Choose a new 745 // active pointer and adjust accordingly. 746 // TODO: Make this decision more intelligent. 747 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 748 mLastMotionX = ev.getX(newPointerIndex); 749 mLastMotionY = ev.getY(newPointerIndex); 750 mActivePointerId = ev.getPointerId(newPointerIndex); 751 if (mVelocityTracker != null) { 752 mVelocityTracker.clear(); 753 } 754 } 755 } 756 757 /** 758 * If one of our descendant views decides that it could be focused now, only 759 * pass that along if it's on the current screen. 760 * 761 * This happens when live folders requery, and if they're off screen, they 762 * end up calling requestFocus, which pulls it on screen. 763 */ 764 @Override 765 public void focusableViewAvailable(View focused) { 766 View current = getChildAt(mCurrentScreen); 767 View v = focused; 768 while (true) { 769 if (v == current) { 770 super.focusableViewAvailable(focused); 771 return; 772 } 773 if (v == this) { 774 return; 775 } 776 ViewParent parent = v.getParent(); 777 if (parent instanceof View) { 778 v = (View)v.getParent(); 779 } else { 780 return; 781 } 782 } 783 } 784 785 void enableChildrenCache(int fromScreen, int toScreen) { 786 if (fromScreen > toScreen) { 787 final int temp = fromScreen; 788 fromScreen = toScreen - 1; 789 toScreen = temp; 790 } else { 791 toScreen++; 792 } 793 794 final int count = getChildCount(); 795 796 fromScreen = Math.max(fromScreen, 0); 797 toScreen = Math.min(toScreen, count - 1); 798 799 for (int i = fromScreen; i <= toScreen; i++) { 800 final CellLayout layout = (CellLayout) getChildAt(i); 801 layout.setChildrenDrawnWithCacheEnabled(true); 802 layout.setChildrenDrawingCacheEnabled(true); 803 } 804 } 805 806 void clearChildrenCache() { 807 final int count = getChildCount(); 808 for (int i = 0; i < count; i++) { 809 final CellLayout layout = (CellLayout) getChildAt(i); 810 layout.setChildrenDrawnWithCacheEnabled(false); 811 } 812 } 813 814 @Override 815 public boolean onTouchEvent(MotionEvent ev) { 816 817 if (mLauncher.isWorkspaceLocked()) { 818 return false; // We don't want the events. Let them fall through to the all apps view. 819 } 820 if (mLauncher.isAllAppsVisible()) { 821 // Cancel any scrolling that is in progress. 822 if (!mScroller.isFinished()) { 823 mScroller.abortAnimation(); 824 } 825 snapToScreen(mCurrentScreen); 826 return false; // We don't want the events. Let them fall through to the all apps view. 827 } 828 829 if (mVelocityTracker == null) { 830 mVelocityTracker = VelocityTracker.obtain(); 831 } 832 mVelocityTracker.addMovement(ev); 833 834 final int action = ev.getAction(); 835 836 switch (action & MotionEvent.ACTION_MASK) { 837 case MotionEvent.ACTION_DOWN: 838 /* 839 * If being flinged and user touches, stop the fling. isFinished 840 * will be false if being flinged. 841 */ 842 if (!mScroller.isFinished()) { 843 mScroller.abortAnimation(); 844 } 845 846 // Remember where the motion event started 847 mLastMotionX = ev.getX(); 848 mActivePointerId = ev.getPointerId(0); 849 break; 850 case MotionEvent.ACTION_MOVE: 851 if (mTouchState == TOUCH_STATE_SCROLLING) { 852 // Scroll to follow the motion event 853 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 854 final float x = ev.getX(pointerIndex); 855 final int deltaX = (int) (mLastMotionX - x); 856 mLastMotionX = x; 857 858 if (deltaX < 0) { 859 if (mScrollX > 0) { 860 scrollBy(Math.max(-mScrollX, deltaX), 0); 861 updateWallpaperOffset(); 862 } 863 } else if (deltaX > 0) { 864 final int availableToScroll = getChildAt(getChildCount() - 1).getRight() - 865 mScrollX - getWidth(); 866 if (availableToScroll > 0) { 867 scrollBy(Math.min(availableToScroll, deltaX), 0); 868 updateWallpaperOffset(); 869 } 870 } else { 871 awakenScrollBars(); 872 } 873 } 874 break; 875 case MotionEvent.ACTION_UP: 876 if (mTouchState == TOUCH_STATE_SCROLLING) { 877 final VelocityTracker velocityTracker = mVelocityTracker; 878 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 879 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); 880 881 final int screenWidth = getWidth(); 882 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; 883 final float scrolledPos = (float) mScrollX / screenWidth; 884 885 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 886 // Fling hard enough to move left. 887 // Don't fling across more than one screen at a time. 888 final int bound = scrolledPos < whichScreen ? 889 mCurrentScreen - 1 : mCurrentScreen; 890 snapToScreen(Math.min(whichScreen, bound), velocityX, true); 891 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { 892 // Fling hard enough to move right 893 // Don't fling across more than one screen at a time. 894 final int bound = scrolledPos > whichScreen ? 895 mCurrentScreen + 1 : mCurrentScreen; 896 snapToScreen(Math.max(whichScreen, bound), velocityX, true); 897 } else { 898 snapToScreen(whichScreen, 0, true); 899 } 900 901 if (mVelocityTracker != null) { 902 mVelocityTracker.recycle(); 903 mVelocityTracker = null; 904 } 905 } 906 mTouchState = TOUCH_STATE_REST; 907 mActivePointerId = INVALID_POINTER; 908 break; 909 case MotionEvent.ACTION_CANCEL: 910 mTouchState = TOUCH_STATE_REST; 911 mActivePointerId = INVALID_POINTER; 912 break; 913 case MotionEvent.ACTION_POINTER_UP: 914 onSecondaryPointerUp(ev); 915 break; 916 } 917 918 return true; 919 } 920 921 void snapToScreen(int whichScreen) { 922 snapToScreen(whichScreen, 0, false); 923 } 924 925 private void snapToScreen(int whichScreen, int velocity, boolean settle) { 926 //if (!mScroller.isFinished()) return; 927 928 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 929 930 clearVacantCache(); 931 enableChildrenCache(mCurrentScreen, whichScreen); 932 933 mNextScreen = whichScreen; 934 935 mPreviousIndicator.setLevel(mNextScreen); 936 mNextIndicator.setLevel(mNextScreen); 937 938 View focusedChild = getFocusedChild(); 939 if (focusedChild != null && whichScreen != mCurrentScreen && 940 focusedChild == getChildAt(mCurrentScreen)) { 941 focusedChild.clearFocus(); 942 } 943 944 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); 945 final int newX = whichScreen * getWidth(); 946 final int delta = newX - mScrollX; 947 int duration = (screenDelta + 1) * 100; 948 949 if (!mScroller.isFinished()) { 950 mScroller.abortAnimation(); 951 } 952 953 if (settle) { 954 mScrollInterpolator.setDistance(screenDelta); 955 } else { 956 mScrollInterpolator.disableSettle(); 957 } 958 959 velocity = Math.abs(velocity); 960 if (velocity > 0) { 961 duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) 962 * FLING_VELOCITY_INFLUENCE; 963 } else { 964 duration += 100; 965 } 966 967 awakenScrollBars(duration); 968 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 969 invalidate(); 970 } 971 972 void startDrag(CellLayout.CellInfo cellInfo) { 973 View child = cellInfo.cell; 974 975 // Make sure the drag was started by a long press as opposed to a long click. 976 if (!child.isInTouchMode()) { 977 return; 978 } 979 980 mDragInfo = cellInfo; 981 mDragInfo.screen = mCurrentScreen; 982 983 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); 984 985 current.onDragChild(child); 986 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 987 invalidate(); 988 } 989 990 @Override 991 protected Parcelable onSaveInstanceState() { 992 final SavedState state = new SavedState(super.onSaveInstanceState()); 993 state.currentScreen = mCurrentScreen; 994 return state; 995 } 996 997 @Override 998 protected void onRestoreInstanceState(Parcelable state) { 999 SavedState savedState = (SavedState) state; 1000 super.onRestoreInstanceState(savedState.getSuperState()); 1001 if (savedState.currentScreen != -1) { 1002 mCurrentScreen = savedState.currentScreen; 1003 Launcher.setScreen(mCurrentScreen); 1004 } 1005 } 1006 1007 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { 1008 addApplicationShortcut(info, cellInfo, false); 1009 } 1010 1011 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, 1012 boolean insertAtFirst) { 1013 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); 1014 final int[] result = new int[2]; 1015 1016 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); 1017 onDropExternal(result[0], result[1], info, layout, insertAtFirst); 1018 } 1019 1020 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1021 DragView dragView, Object dragInfo) { 1022 final CellLayout cellLayout = getCurrentDropLayout(); 1023 if (source != this) { 1024 onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout); 1025 } else { 1026 // Move internally 1027 if (mDragInfo != null) { 1028 final View cell = mDragInfo.cell; 1029 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1030 if (index != mDragInfo.screen) { 1031 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1032 originalCellLayout.removeView(cell); 1033 cellLayout.addView(cell); 1034 } 1035 mTargetCell = estimateDropCell(x - xOffset, y - yOffset, 1036 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell); 1037 cellLayout.onDropChild(cell, mTargetCell); 1038 1039 final ItemInfo info = (ItemInfo) cell.getTag(); 1040 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 1041 LauncherModel.moveItemInDatabase(mLauncher, info, 1042 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY); 1043 } 1044 } 1045 } 1046 1047 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, 1048 DragView dragView, Object dragInfo) { 1049 clearVacantCache(); 1050 } 1051 1052 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1053 DragView dragView, Object dragInfo) { 1054 } 1055 1056 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, 1057 DragView dragView, Object dragInfo) { 1058 clearVacantCache(); 1059 } 1060 1061 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) { 1062 onDropExternal(x, y, dragInfo, cellLayout, false); 1063 } 1064 1065 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout, 1066 boolean insertAtFirst) { 1067 // Drag from somewhere else 1068 ItemInfo info = (ItemInfo) dragInfo; 1069 1070 View view; 1071 1072 switch (info.itemType) { 1073 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1074 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1075 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1076 // Came from all apps -- make a copy 1077 info = new ShortcutInfo((ApplicationInfo)info); 1078 } 1079 view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info); 1080 break; 1081 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1082 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1083 (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info)); 1084 break; 1085 default: 1086 throw new IllegalStateException("Unknown item type: " + info.itemType); 1087 } 1088 1089 cellLayout.addView(view, insertAtFirst ? 0 : -1); 1090 view.setHapticFeedbackEnabled(false); 1091 view.setOnLongClickListener(mLongClickListener); 1092 if (view instanceof DropTarget) { 1093 mDragController.addDropTarget((DropTarget) view); 1094 } 1095 1096 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); 1097 cellLayout.onDropChild(view, mTargetCell); 1098 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1099 1100 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1101 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY); 1102 } 1103 1104 /** 1105 * Return the current {@link CellLayout}, correctly picking the destination 1106 * screen while a scroll is in progress. 1107 */ 1108 private CellLayout getCurrentDropLayout() { 1109 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1110 return (CellLayout) getChildAt(index); 1111 } 1112 1113 /** 1114 * {@inheritDoc} 1115 */ 1116 public boolean acceptDrop(DragSource source, int x, int y, 1117 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1118 final CellLayout layout = getCurrentDropLayout(); 1119 final CellLayout.CellInfo cellInfo = mDragInfo; 1120 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1121 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1122 1123 if (mVacantCache == null) { 1124 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1125 mVacantCache = layout.findAllVacantCells(null, ignoreView); 1126 } 1127 1128 return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false); 1129 } 1130 1131 /** 1132 * {@inheritDoc} 1133 */ 1134 public Rect estimateDropLocation(DragSource source, int x, int y, 1135 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { 1136 final CellLayout layout = getCurrentDropLayout(); 1137 1138 final CellLayout.CellInfo cellInfo = mDragInfo; 1139 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1140 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1141 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1142 1143 final Rect location = recycle != null ? recycle : new Rect(); 1144 1145 // Find drop cell and convert into rectangle 1146 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, 1147 spanX, spanY, ignoreView, layout, mTempCell); 1148 1149 if (dropCell == null) { 1150 return null; 1151 } 1152 1153 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); 1154 location.left = mTempEstimate[0]; 1155 location.top = mTempEstimate[1]; 1156 1157 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); 1158 location.right = mTempEstimate[0]; 1159 location.bottom = mTempEstimate[1]; 1160 1161 return location; 1162 } 1163 1164 /** 1165 * Calculate the nearest cell where the given object would be dropped. 1166 */ 1167 private int[] estimateDropCell(int pixelX, int pixelY, 1168 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1169 // Create vacant cell cache if none exists 1170 if (mVacantCache == null) { 1171 mVacantCache = layout.findAllVacantCells(null, ignoreView); 1172 } 1173 1174 // Find the best target drop location 1175 return layout.findNearestVacantArea(pixelX, pixelY, 1176 spanX, spanY, mVacantCache, recycle); 1177 } 1178 1179 void setLauncher(Launcher launcher) { 1180 mLauncher = launcher; 1181 } 1182 1183 public void setDragController(DragController dragController) { 1184 mDragController = dragController; 1185 } 1186 1187 public void onDropCompleted(View target, boolean success) { 1188 clearVacantCache(); 1189 1190 if (success){ 1191 if (target != this && mDragInfo != null) { 1192 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1193 cellLayout.removeView(mDragInfo.cell); 1194 if (mDragInfo.cell instanceof DropTarget) { 1195 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1196 } 1197 //final Object tag = mDragInfo.cell.getTag(); 1198 } 1199 } else { 1200 if (mDragInfo != null) { 1201 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1202 cellLayout.onDropAborted(mDragInfo.cell); 1203 } 1204 } 1205 1206 mDragInfo = null; 1207 } 1208 1209 public void scrollLeft() { 1210 clearVacantCache(); 1211 if (mScroller.isFinished()) { 1212 if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1); 1213 } else { 1214 if (mNextScreen > 0) snapToScreen(mNextScreen - 1); 1215 } 1216 } 1217 1218 public void scrollRight() { 1219 clearVacantCache(); 1220 if (mScroller.isFinished()) { 1221 if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1); 1222 } else { 1223 if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1); 1224 } 1225 } 1226 1227 public int getScreenForView(View v) { 1228 int result = -1; 1229 if (v != null) { 1230 ViewParent vp = v.getParent(); 1231 int count = getChildCount(); 1232 for (int i = 0; i < count; i++) { 1233 if (vp == getChildAt(i)) { 1234 return i; 1235 } 1236 } 1237 } 1238 return result; 1239 } 1240 1241 public Folder getFolderForTag(Object tag) { 1242 int screenCount = getChildCount(); 1243 for (int screen = 0; screen < screenCount; screen++) { 1244 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1245 int count = currentScreen.getChildCount(); 1246 for (int i = 0; i < count; i++) { 1247 View child = currentScreen.getChildAt(i); 1248 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1249 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1250 Folder f = (Folder) child; 1251 if (f.getInfo() == tag) { 1252 return f; 1253 } 1254 } 1255 } 1256 } 1257 return null; 1258 } 1259 1260 public View getViewForTag(Object tag) { 1261 int screenCount = getChildCount(); 1262 for (int screen = 0; screen < screenCount; screen++) { 1263 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1264 int count = currentScreen.getChildCount(); 1265 for (int i = 0; i < count; i++) { 1266 View child = currentScreen.getChildAt(i); 1267 if (child.getTag() == tag) { 1268 return child; 1269 } 1270 } 1271 } 1272 return null; 1273 } 1274 1275 /** 1276 * @return True is long presses are still allowed for the current touch 1277 */ 1278 public boolean allowLongPress() { 1279 return mAllowLongPress; 1280 } 1281 1282 /** 1283 * Set true to allow long-press events to be triggered, usually checked by 1284 * {@link Launcher} to accept or block dpad-initiated long-presses. 1285 */ 1286 public void setAllowLongPress(boolean allowLongPress) { 1287 mAllowLongPress = allowLongPress; 1288 } 1289 1290 void removeItems(final ArrayList<ApplicationInfo> apps) { 1291 final int count = getChildCount(); 1292 final PackageManager manager = getContext().getPackageManager(); 1293 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1294 1295 final HashSet<String> packageNames = new HashSet<String>(); 1296 final int appCount = apps.size(); 1297 for (int i = 0; i < appCount; i++) { 1298 packageNames.add(apps.get(i).componentName.getPackageName()); 1299 } 1300 1301 for (int i = 0; i < count; i++) { 1302 final CellLayout layout = (CellLayout) getChildAt(i); 1303 1304 // Avoid ANRs by treating each screen separately 1305 post(new Runnable() { 1306 public void run() { 1307 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1308 childrenToRemove.clear(); 1309 1310 int childCount = layout.getChildCount(); 1311 for (int j = 0; j < childCount; j++) { 1312 final View view = layout.getChildAt(j); 1313 Object tag = view.getTag(); 1314 1315 if (tag instanceof ShortcutInfo) { 1316 final ShortcutInfo info = (ShortcutInfo) tag; 1317 final Intent intent = info.intent; 1318 final ComponentName name = intent.getComponent(); 1319 1320 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1321 for (String packageName: packageNames) { 1322 if (packageName.equals(name.getPackageName())) { 1323 // TODO: This should probably be done on a worker thread 1324 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1325 childrenToRemove.add(view); 1326 } 1327 } 1328 } 1329 } else if (tag instanceof UserFolderInfo) { 1330 final UserFolderInfo info = (UserFolderInfo) tag; 1331 final ArrayList<ShortcutInfo> contents = info.contents; 1332 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1333 final int contentsCount = contents.size(); 1334 boolean removedFromFolder = false; 1335 1336 for (int k = 0; k < contentsCount; k++) { 1337 final ShortcutInfo appInfo = contents.get(k); 1338 final Intent intent = appInfo.intent; 1339 final ComponentName name = intent.getComponent(); 1340 1341 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1342 for (String packageName: packageNames) { 1343 if (packageName.equals(name.getPackageName())) { 1344 toRemove.add(appInfo); 1345 // TODO: This should probably be done on a worker thread 1346 LauncherModel.deleteItemFromDatabase( 1347 mLauncher, appInfo); 1348 removedFromFolder = true; 1349 } 1350 } 1351 } 1352 } 1353 1354 contents.removeAll(toRemove); 1355 if (removedFromFolder) { 1356 final Folder folder = getOpenFolder(); 1357 if (folder != null) folder.notifyDataSetChanged(); 1358 } 1359 } else if (tag instanceof LiveFolderInfo) { 1360 final LiveFolderInfo info = (LiveFolderInfo) tag; 1361 final Uri uri = info.uri; 1362 final ProviderInfo providerInfo = manager.resolveContentProvider( 1363 uri.getAuthority(), 0); 1364 1365 if (providerInfo != null) { 1366 for (String packageName: packageNames) { 1367 if (packageName.equals(providerInfo.packageName)) { 1368 // TODO: This should probably be done on a worker thread 1369 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1370 childrenToRemove.add(view); 1371 } 1372 } 1373 } 1374 } else if (tag instanceof LauncherAppWidgetInfo) { 1375 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1376 final AppWidgetProviderInfo provider = 1377 widgets.getAppWidgetInfo(info.appWidgetId); 1378 if (provider == null) { 1379 for (String packageName: packageNames) { 1380 if (packageName.equals(provider.provider.getPackageName())) { 1381 // TODO: This should probably be done on a worker thread 1382 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1383 childrenToRemove.add(view); 1384 } 1385 } 1386 } 1387 } 1388 } 1389 1390 childCount = childrenToRemove.size(); 1391 for (int j = 0; j < childCount; j++) { 1392 View child = childrenToRemove.get(j); 1393 layout.removeViewInLayout(child); 1394 if (child instanceof DropTarget) { 1395 mDragController.removeDropTarget((DropTarget)child); 1396 } 1397 } 1398 1399 if (childCount > 0) { 1400 layout.requestLayout(); 1401 layout.invalidate(); 1402 } 1403 } 1404 }); 1405 } 1406 } 1407 1408 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1409 final PackageManager pm = mLauncher.getPackageManager(); 1410 1411 final int count = getChildCount(); 1412 for (int i = 0; i < count; i++) { 1413 final CellLayout layout = (CellLayout) getChildAt(i); 1414 int childCount = layout.getChildCount(); 1415 for (int j = 0; j < childCount; j++) { 1416 final View view = layout.getChildAt(j); 1417 Object tag = view.getTag(); 1418 if (tag instanceof ShortcutInfo) { 1419 ShortcutInfo info = (ShortcutInfo)tag; 1420 // We need to check for ACTION_MAIN otherwise getComponent() might 1421 // return null for some shortcuts (for instance, for shortcuts to 1422 // web pages.) 1423 final Intent intent = info.intent; 1424 final ComponentName name = intent.getComponent(); 1425 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1426 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1427 final int appCount = apps.size(); 1428 for (int k=0; k<appCount; k++) { 1429 ApplicationInfo app = apps.get(k); 1430 if (app.componentName.equals(name)) { 1431 info.setIcon(mIconCache.getIcon(info.intent)); 1432 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1433 new FastBitmapDrawable(info.getIcon(mIconCache)), 1434 null, null); 1435 } 1436 } 1437 } 1438 } 1439 } 1440 } 1441 } 1442 1443 void moveToDefaultScreen(boolean animate) { 1444 if (animate) { 1445 snapToScreen(mDefaultScreen); 1446 } else { 1447 setCurrentScreen(mDefaultScreen); 1448 } 1449 getChildAt(mDefaultScreen).requestFocus(); 1450 } 1451 1452 void setIndicators(Drawable previous, Drawable next) { 1453 mPreviousIndicator = previous; 1454 mNextIndicator = next; 1455 previous.setLevel(mCurrentScreen); 1456 next.setLevel(mCurrentScreen); 1457 } 1458 1459 public static class SavedState extends BaseSavedState { 1460 int currentScreen = -1; 1461 1462 SavedState(Parcelable superState) { 1463 super(superState); 1464 } 1465 1466 private SavedState(Parcel in) { 1467 super(in); 1468 currentScreen = in.readInt(); 1469 } 1470 1471 @Override 1472 public void writeToParcel(Parcel out, int flags) { 1473 super.writeToParcel(out, flags); 1474 out.writeInt(currentScreen); 1475 } 1476 1477 public static final Parcelable.Creator<SavedState> CREATOR = 1478 new Parcelable.Creator<SavedState>() { 1479 public SavedState createFromParcel(Parcel in) { 1480 return new SavedState(in); 1481 } 1482 1483 public SavedState[] newArray(int size) { 1484 return new SavedState[size]; 1485 } 1486 }; 1487 } 1488} 1489