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