Workspace.java revision e47f55c30b9c24f01f2be861247c92f17fbe4a61
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 android.app.WallpaperManager; 20import android.content.Context; 21import android.content.Intent; 22import android.content.ComponentName; 23import android.content.res.TypedArray; 24import android.content.pm.PackageManager; 25import android.graphics.Canvas; 26import android.graphics.RectF; 27import android.graphics.Rect; 28import android.graphics.drawable.Drawable; 29import android.os.Parcelable; 30import android.os.Parcel; 31import android.util.AttributeSet; 32import android.view.MotionEvent; 33import android.view.VelocityTracker; 34import android.view.View; 35import android.view.ViewConfiguration; 36import android.view.ViewGroup; 37import android.view.ViewParent; 38import android.widget.Scroller; 39import android.widget.TextView; 40 41import java.util.ArrayList; 42 43/** 44 * The workspace is a wide area with a wallpaper and a finite number of screens. Each 45 * screen contains a number of icons, folders or widgets the user can interact with. 46 * A workspace is meant to be used with a fixed width only. 47 */ 48public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller { 49 @SuppressWarnings({"UnusedDeclaration"}) 50 private static final String TAG = "Launcher.Workspace"; 51 private static final int INVALID_SCREEN = -1; 52 53 /** 54 * The velocity at which a fling gesture will cause us to snap to the next screen 55 */ 56 private static final int SNAP_VELOCITY = 1000; 57 58 private final WallpaperManager mWallpaperManager; 59 60 private int mDefaultScreen; 61 62 private boolean mFirstLayout = true; 63 64 private int mCurrentScreen; 65 private int mNextScreen = INVALID_SCREEN; 66 private Scroller mScroller; 67 private VelocityTracker mVelocityTracker; 68 69 /** 70 * CellInfo for the cell that is currently being dragged 71 */ 72 private CellLayout.CellInfo mDragInfo; 73 74 /** 75 * Target drop area calculated during last acceptDrop call. 76 */ 77 private int[] mTargetCell = null; 78 79 private float mLastMotionX; 80 private float mLastMotionY; 81 82 private final static int TOUCH_STATE_REST = 0; 83 private final static int TOUCH_STATE_SCROLLING = 1; 84 85 private int mTouchState = TOUCH_STATE_REST; 86 87 private OnLongClickListener mLongClickListener; 88 89 private Launcher mLauncher; 90 private DragController mDragController; 91 92 /** 93 * Cache of vacant cells, used during drag events and invalidated as needed. 94 */ 95 private CellLayout.CellInfo mVacantCache = null; 96 97 private int[] mTempCell = new int[2]; 98 private int[] mTempEstimate = new int[2]; 99 100 private boolean mAllowLongPress = true; 101 102 private int mTouchSlop; 103 private int mMaximumVelocity; 104 105 final Rect mDrawerBounds = new Rect(); 106 final Rect mClipBounds = new Rect(); 107 int mDrawerContentHeight; 108 int mDrawerContentWidth; 109 110 private Drawable mPreviousIndicator; 111 private Drawable mNextIndicator; 112 113 private boolean mFading = true; 114 115 /** 116 * Used to inflate the Workspace from XML. 117 * 118 * @param context The application's context. 119 * @param attrs The attribtues set containing the Workspace's customization values. 120 */ 121 public Workspace(Context context, AttributeSet attrs) { 122 this(context, attrs, 0); 123 } 124 125 /** 126 * Used to inflate the Workspace from XML. 127 * 128 * @param context The application's context. 129 * @param attrs The attribtues set containing the Workspace's customization values. 130 * @param defStyle Unused. 131 */ 132 public Workspace(Context context, AttributeSet attrs, int defStyle) { 133 super(context, attrs, defStyle); 134 135 mWallpaperManager = WallpaperManager.getInstance(context); 136 137 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); 138 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); 139 a.recycle(); 140 141 initWorkspace(); 142 } 143 144 /** 145 * Initializes various states for this workspace. 146 */ 147 private void initWorkspace() { 148 mScroller = new Scroller(getContext()); 149 mCurrentScreen = mDefaultScreen; 150 Launcher.setScreen(mCurrentScreen); 151 152 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 153 mTouchSlop = configuration.getScaledTouchSlop(); 154 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 155 } 156 157 @Override 158 public void addView(View child, int index, LayoutParams params) { 159 if (!(child instanceof CellLayout)) { 160 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 161 } 162 super.addView(child, index, params); 163 } 164 165 @Override 166 public void addView(View child) { 167 if (!(child instanceof CellLayout)) { 168 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 169 } 170 super.addView(child); 171 } 172 173 @Override 174 public void addView(View child, int index) { 175 if (!(child instanceof CellLayout)) { 176 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 177 } 178 super.addView(child, index); 179 } 180 181 @Override 182 public void addView(View child, int width, int height) { 183 if (!(child instanceof CellLayout)) { 184 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 185 } 186 super.addView(child, width, height); 187 } 188 189 @Override 190 public void addView(View child, LayoutParams params) { 191 if (!(child instanceof CellLayout)) { 192 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 193 } 194 super.addView(child, params); 195 } 196 197 /** 198 * @return The open folder on the current screen, or null if there is none 199 */ 200 Folder getOpenFolder() { 201 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); 202 int count = currentScreen.getChildCount(); 203 for (int i = 0; i < count; i++) { 204 View child = currentScreen.getChildAt(i); 205 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 206 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 207 return (Folder) child; 208 } 209 } 210 return null; 211 } 212 213 ArrayList<Folder> getOpenFolders() { 214 final int screens = getChildCount(); 215 ArrayList<Folder> folders = new ArrayList<Folder>(screens); 216 217 for (int screen = 0; screen < screens; screen++) { 218 CellLayout currentScreen = (CellLayout) getChildAt(screen); 219 int count = currentScreen.getChildCount(); 220 for (int i = 0; i < count; i++) { 221 View child = currentScreen.getChildAt(i); 222 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 223 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 224 folders.add((Folder) child); 225 break; 226 } 227 } 228 } 229 230 return folders; 231 } 232 233 boolean isDefaultScreenShowing() { 234 return mCurrentScreen == mDefaultScreen; 235 } 236 237 /** 238 * Returns the index of the currently displayed screen. 239 * 240 * @return The index of the currently displayed screen. 241 */ 242 int getCurrentScreen() { 243 return mCurrentScreen; 244 } 245 246 /** 247 * Returns how many screens there are. 248 */ 249 int getScreenCount() { 250 return getChildCount(); 251 } 252 253 /** 254 * Computes a bounding rectangle for a range of cells 255 * 256 * @param cellX X coordinate of upper left corner expressed as a cell position 257 * @param cellY Y coordinate of upper left corner expressed as a cell position 258 * @param cellHSpan Width in cells 259 * @param cellVSpan Height in cells 260 * @param rect Rectnagle into which to put the results 261 */ 262 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF rect) { 263 ((CellLayout)getChildAt(mCurrentScreen)).cellToRect(cellX, cellY, 264 cellHSpan, cellVSpan, rect); 265 } 266 267 /** 268 * Sets the current screen. 269 * 270 * @param currentScreen 271 */ 272 void setCurrentScreen(int currentScreen) { 273 clearVacantCache(); 274 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); 275 scrollTo(mCurrentScreen * getWidth(), 0); 276 invalidate(); 277 } 278 279 /** 280 * Adds the specified child in the current screen. The position and dimension of 281 * the child are defined by x, y, spanX and spanY. 282 * 283 * @param child The child to add in one of the workspace's screens. 284 * @param x The X position of the child in the screen's grid. 285 * @param y The Y position of the child in the screen's grid. 286 * @param spanX The number of cells spanned horizontally by the child. 287 * @param spanY The number of cells spanned vertically by the child. 288 */ 289 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { 290 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false); 291 } 292 293 /** 294 * Adds the specified child in the current screen. The position and dimension of 295 * the child are defined by x, y, spanX and spanY. 296 * 297 * @param child The child to add in one of the workspace's screens. 298 * @param x The X position of the child in the screen's grid. 299 * @param y The Y position of the child in the screen's grid. 300 * @param spanX The number of cells spanned horizontally by the child. 301 * @param spanY The number of cells spanned vertically by the child. 302 * @param insert When true, the child is inserted at the beginning of the children list. 303 */ 304 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { 305 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert); 306 } 307 308 /** 309 * Adds the specified child in the specified 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 screen The screen in which to add the child. 314 * @param x The X position of the child in the screen's grid. 315 * @param y The Y position of the child in the screen's grid. 316 * @param spanX The number of cells spanned horizontally by the child. 317 * @param spanY The number of cells spanned vertically by the child. 318 */ 319 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 320 addInScreen(child, screen, x, y, spanX, spanY, false); 321 } 322 323 /** 324 * Adds the specified child in the specified screen. The position and dimension of 325 * the child are defined by x, y, spanX and spanY. 326 * 327 * @param child The child to add in one of the workspace's screens. 328 * @param screen The screen in which to add the child. 329 * @param x The X position of the child in the screen's grid. 330 * @param y The Y position of the child in the screen's grid. 331 * @param spanX The number of cells spanned horizontally by the child. 332 * @param spanY The number of cells spanned vertically by the child. 333 * @param insert When true, the child is inserted at the beginning of the children list. 334 */ 335 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 336 if (screen < 0 || screen >= getChildCount()) { 337 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount()); 338 } 339 340 clearVacantCache(); 341 342 final CellLayout group = (CellLayout) getChildAt(screen); 343 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 344 if (lp == null) { 345 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 346 } else { 347 lp.cellX = x; 348 lp.cellY = y; 349 lp.cellHSpan = spanX; 350 lp.cellVSpan = spanY; 351 } 352 group.addView(child, insert ? 0 : -1, lp); 353 if (!(child instanceof Folder)) { 354 child.setOnLongClickListener(mLongClickListener); 355 } 356 if (child instanceof DropTarget) { 357 mDragController.addDropTarget((DropTarget)child); 358 } 359 } 360 361 void addWidget(View view, Widget widget) { 362 addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX, 363 widget.spanY, false); 364 } 365 366 void addWidget(View view, Widget widget, boolean insert) { 367 addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX, 368 widget.spanY, insert); 369 } 370 371 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) { 372 CellLayout group = (CellLayout) getChildAt(mCurrentScreen); 373 if (group != null) { 374 return group.findAllVacantCells(occupied, null); 375 } 376 return null; 377 } 378 379 private void clearVacantCache() { 380 if (mVacantCache != null) { 381 mVacantCache.clearVacantCells(); 382 mVacantCache = null; 383 } 384 } 385 386 /** 387 * Returns the coordinate of a vacant cell for the current screen. 388 */ 389 boolean getVacantCell(int[] vacant, int spanX, int spanY) { 390 CellLayout group = (CellLayout) getChildAt(mCurrentScreen); 391 if (group != null) { 392 return group.getVacantCell(vacant, spanX, spanY); 393 } 394 return false; 395 } 396 397 /** 398 * Adds the specified child in the current screen. The position and dimension of 399 * the child are defined by x, y, spanX and spanY. 400 * 401 * @param child The child to add in one of the workspace's screens. 402 * @param spanX The number of cells spanned horizontally by the child. 403 * @param spanY The number of cells spanned vertically by the child. 404 */ 405 void fitInCurrentScreen(View child, int spanX, int spanY) { 406 fitInScreen(child, mCurrentScreen, spanX, spanY); 407 } 408 409 /** 410 * Adds the specified child in the specified screen. The position and dimension of 411 * the child are defined by x, y, spanX and spanY. 412 * 413 * @param child The child to add in one of the workspace's screens. 414 * @param screen The screen in which to add the child. 415 * @param spanX The number of cells spanned horizontally by the child. 416 * @param spanY The number of cells spanned vertically by the child. 417 */ 418 void fitInScreen(View child, int screen, int spanX, int spanY) { 419 if (screen < 0 || screen >= getChildCount()) { 420 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount()); 421 } 422 423 final CellLayout group = (CellLayout) getChildAt(screen); 424 boolean vacant = group.getVacantCell(mTempCell, spanX, spanY); 425 if (vacant) { 426 group.addView(child, 427 new CellLayout.LayoutParams(mTempCell[0], mTempCell[1], spanX, spanY)); 428 child.setOnLongClickListener(mLongClickListener); 429 if (!(child instanceof Folder)) { 430 child.setOnLongClickListener(mLongClickListener); 431 } 432 if (child instanceof DropTarget) { 433 mDragController.addDropTarget((DropTarget)child); 434 } 435 } 436 } 437 438 /** 439 * Registers the specified listener on each screen contained in this workspace. 440 * 441 * @param l The listener used to respond to long clicks. 442 */ 443 @Override 444 public void setOnLongClickListener(OnLongClickListener l) { 445 mLongClickListener = l; 446 final int count = getChildCount(); 447 for (int i = 0; i < count; i++) { 448 getChildAt(i).setOnLongClickListener(l); 449 } 450 } 451 452 private void updateWallpaperOffset() { 453 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 454 } 455 456 private void updateWallpaperOffset(int scrollRange) { 457 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 458 mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX / (float) scrollRange, 0); 459 } 460 461 @Override 462 public void computeScroll() { 463 if (mScroller.computeScrollOffset()) { 464 mScrollX = mScroller.getCurrX(); 465 mScrollY = mScroller.getCurrY(); 466 updateWallpaperOffset(); 467 postInvalidate(); 468 } else if (mNextScreen != INVALID_SCREEN) { 469 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); 470 mPreviousIndicator.setLevel(mCurrentScreen); 471 mNextIndicator.setLevel(mCurrentScreen); 472 Launcher.setScreen(mCurrentScreen); 473 mNextScreen = INVALID_SCREEN; 474 clearChildrenCache(); 475 } 476 } 477 478 public void startFading(boolean dest) { 479 mFading = dest; 480 invalidate(); 481 } 482 483 @Override 484 protected void dispatchDraw(Canvas canvas) { 485 final boolean allAppsOpaque = mLauncher.isAllAppsOpaque(); 486 if (mFading == allAppsOpaque) { 487 invalidate(); 488 } else { 489 mFading = !allAppsOpaque; 490 } 491 if (allAppsOpaque) { 492 // If the launcher is up, draw black. 493 canvas.drawARGB(0xff, 0, 0, 0); 494 return; 495 } 496 497 boolean restore = false; 498 int restoreCount = 0; 499 500 // For the fade. If view gets setAlpha(), use that instead. 501 float scale = mScale; 502 if (scale < 0.999f) { 503 int sx = mScrollX; 504 505 int alpha = (scale < 0.5f) ? (int)(255 * 2 * scale) : 255; 506 507 restoreCount = canvas.saveLayerAlpha(sx, 0, sx+getWidth(), getHeight(), alpha, 508 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 509 restore = true; 510 511 if (scale < 0.999f) { 512 int w = getWidth(); 513 w += 2 * mCurrentScreen * w; 514 int dx = w/2; 515 int h = getHeight(); 516 int dy = (h/2) - (h/4); 517 canvas.translate(dx, dy); 518 canvas.scale(scale, scale); 519 canvas.translate(-dx, -dy); 520 } 521 } 522 523 // ViewGroup.dispatchDraw() supports many features we don't need: 524 // clip to padding, layout animation, animation listener, disappearing 525 // children, etc. The following implementation attempts to fast-track 526 // the drawing dispatch by drawing only what we know needs to be drawn. 527 528 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN 529 && scale > 0.999f; 530 // If we are not scrolling or flinging, draw only the current screen 531 if (fastDraw) { 532 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); 533 } else { 534 final long drawingTime = getDrawingTime(); 535 // If we are flinging, draw only the current screen and the target screen 536 if (mNextScreen >= 0 && mNextScreen < getChildCount() && 537 Math.abs(mCurrentScreen - mNextScreen) == 1) { 538 drawChild(canvas, getChildAt(mCurrentScreen), drawingTime); 539 drawChild(canvas, getChildAt(mNextScreen), drawingTime); 540 } else { 541 // If we are scrolling, draw all of our children 542 final int count = getChildCount(); 543 for (int i = 0; i < count; i++) { 544 drawChild(canvas, getChildAt(i), drawingTime); 545 } 546 } 547 } 548 549 if (restore) { 550 canvas.restoreToCount(restoreCount); 551 } 552 } 553 554 private float mScale = 1.0f; 555 public void setScale(float scale) { 556 mScale = scale; 557 invalidate(); 558 } 559 560 protected void onAttachedToWindow() { 561 super.onAttachedToWindow(); 562 mDragController.setWindowToken(getWindowToken()); 563 } 564 565 @Override 566 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 567 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 568 569 final int width = MeasureSpec.getSize(widthMeasureSpec); 570 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 571 if (widthMode != MeasureSpec.EXACTLY) { 572 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 573 } 574 575 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 576 if (heightMode != MeasureSpec.EXACTLY) { 577 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 578 } 579 580 // The children are given the same width and height as the workspace 581 final int count = getChildCount(); 582 for (int i = 0; i < count; i++) { 583 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 584 } 585 586 587 if (mFirstLayout) { 588 setHorizontalScrollBarEnabled(false); 589 scrollTo(mCurrentScreen * width, 0); 590 setHorizontalScrollBarEnabled(true); 591 updateWallpaperOffset(width * (getChildCount() - 1)); 592 mFirstLayout = false; 593 } 594 } 595 596 @Override 597 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 598 int childLeft = 0; 599 600 final int count = getChildCount(); 601 for (int i = 0; i < count; i++) { 602 final View child = getChildAt(i); 603 if (child.getVisibility() != View.GONE) { 604 final int childWidth = child.getMeasuredWidth(); 605 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 606 childLeft += childWidth; 607 } 608 } 609 } 610 611 @Override 612 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 613 int screen = indexOfChild(child); 614 if (screen != mCurrentScreen || !mScroller.isFinished()) { 615 if (!mLauncher.isWorkspaceLocked()) { 616 snapToScreen(screen); 617 } 618 return true; 619 } 620 return false; 621 } 622 623 @Override 624 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 625 if (!mLauncher.isAllAppsVisible()) { 626 final Folder openFolder = getOpenFolder(); 627 if (openFolder != null) { 628 return openFolder.requestFocus(direction, previouslyFocusedRect); 629 } else { 630 int focusableScreen; 631 if (mNextScreen != INVALID_SCREEN) { 632 focusableScreen = mNextScreen; 633 } else { 634 focusableScreen = mCurrentScreen; 635 } 636 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); 637 } 638 } 639 return false; 640 } 641 642 @Override 643 public boolean dispatchUnhandledMove(View focused, int direction) { 644 if (direction == View.FOCUS_LEFT) { 645 if (getCurrentScreen() > 0) { 646 snapToScreen(getCurrentScreen() - 1); 647 return true; 648 } 649 } else if (direction == View.FOCUS_RIGHT) { 650 if (getCurrentScreen() < getChildCount() - 1) { 651 snapToScreen(getCurrentScreen() + 1); 652 return true; 653 } 654 } 655 return super.dispatchUnhandledMove(focused, direction); 656 } 657 658 @Override 659 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 660 if (!mLauncher.isAllAppsVisible()) { 661 final Folder openFolder = getOpenFolder(); 662 if (openFolder == null) { 663 getChildAt(mCurrentScreen).addFocusables(views, direction); 664 if (direction == View.FOCUS_LEFT) { 665 if (mCurrentScreen > 0) { 666 getChildAt(mCurrentScreen - 1).addFocusables(views, direction); 667 } 668 } else if (direction == View.FOCUS_RIGHT){ 669 if (mCurrentScreen < getChildCount() - 1) { 670 getChildAt(mCurrentScreen + 1).addFocusables(views, direction); 671 } 672 } 673 } else { 674 openFolder.addFocusables(views, direction); 675 } 676 } 677 } 678 679 @Override 680 public boolean dispatchTouchEvent(MotionEvent ev) { 681 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 682 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) { 683 return false; 684 } 685 } 686 return super.dispatchTouchEvent(ev); 687 } 688 689 @Override 690 public boolean onInterceptTouchEvent(MotionEvent ev) { 691 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) { 692 return false; // We don't want the events. Let them fall through to the all apps view. 693 } 694 695 /* 696 * This method JUST determines whether we want to intercept the motion. 697 * If we return true, onTouchEvent will be called and we do the actual 698 * scrolling there. 699 */ 700 701 /* 702 * Shortcut the most recurring case: the user is in the dragging 703 * state and he is moving his finger. We want to intercept this 704 * motion. 705 */ 706 final int action = ev.getAction(); 707 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { 708 return true; 709 } 710 711 final float x = ev.getX(); 712 final float y = ev.getY(); 713 714 switch (action) { 715 case MotionEvent.ACTION_MOVE: 716 /* 717 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 718 * whether the user has moved far enough from his original down touch. 719 */ 720 721 /* 722 * Locally do absolute value. mLastMotionX is set to the y value 723 * of the down event. 724 */ 725 final int xDiff = (int) Math.abs(x - mLastMotionX); 726 final int yDiff = (int) Math.abs(y - mLastMotionY); 727 728 final int touchSlop = mTouchSlop; 729 boolean xMoved = xDiff > touchSlop; 730 boolean yMoved = yDiff > touchSlop; 731 732 if (xMoved || yMoved) { 733 734 if (xMoved) { 735 // Scroll if the user moved far enough along the X axis 736 mTouchState = TOUCH_STATE_SCROLLING; 737 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 738 } 739 // Either way, cancel any pending longpress 740 if (mAllowLongPress) { 741 mAllowLongPress = false; 742 // Try canceling the long press. It could also have been scheduled 743 // by a distant descendant, so use the mAllowLongPress flag to block 744 // everything 745 final View currentScreen = getChildAt(mCurrentScreen); 746 currentScreen.cancelLongPress(); 747 } 748 } 749 break; 750 751 case MotionEvent.ACTION_DOWN: 752 // Remember location of down touch 753 mLastMotionX = x; 754 mLastMotionY = y; 755 mAllowLongPress = true; 756 757 /* 758 * If being flinged and user touches the screen, initiate drag; 759 * otherwise don't. mScroller.isFinished should be false when 760 * being flinged. 761 */ 762 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 763 break; 764 765 case MotionEvent.ACTION_CANCEL: 766 case MotionEvent.ACTION_UP: 767 768 if (mTouchState != TOUCH_STATE_SCROLLING) { 769 770 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 771 if (!currentScreen.lastDownOnOccupiedCell()) { 772 // Send a tap to the wallpaper if the last down was on empty space 773 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 774 "android.wallpaper.tap", (int) ev.getX(), (int) ev.getY(), 0, null); 775 } 776 } 777 778 // Release the drag 779 clearChildrenCache(); 780 mTouchState = TOUCH_STATE_REST; 781 mAllowLongPress = false; 782 783 break; 784 } 785 786 /* 787 * The only time we want to intercept motion events is if we are in the 788 * drag mode. 789 */ 790 return mTouchState != TOUCH_STATE_REST; 791 } 792 793 /** 794 * If one of our descendant views decides that it could be focused now, only 795 * pass that along if it's on the current screen. 796 * 797 * This happens when live folders requery, and if they're off screen, they 798 * end up calling requestFocus, which pulls it on screen. 799 */ 800 @Override 801 public void focusableViewAvailable(View focused) { 802 View current = getChildAt(mCurrentScreen); 803 View v = focused; 804 while (true) { 805 if (v == current) { 806 super.focusableViewAvailable(focused); 807 return; 808 } 809 if (v == this) { 810 return; 811 } 812 ViewParent parent = v.getParent(); 813 if (parent instanceof View) { 814 v = (View)v.getParent(); 815 } else { 816 return; 817 } 818 } 819 } 820 821 void enableChildrenCache(int fromScreen, int toScreen) { 822 if (fromScreen > toScreen) { 823 fromScreen = toScreen; 824 toScreen = fromScreen; 825 } 826 827 final int count = getChildCount(); 828 829 fromScreen = Math.max(fromScreen, 0); 830 toScreen = Math.min(toScreen, count - 1); 831 832 for (int i = fromScreen; i <= toScreen; i++) { 833 final CellLayout layout = (CellLayout) getChildAt(i); 834 layout.setChildrenDrawnWithCacheEnabled(true); 835 layout.setChildrenDrawingCacheEnabled(true); 836 } 837 } 838 839 void clearChildrenCache() { 840 final int count = getChildCount(); 841 for (int i = 0; i < count; i++) { 842 final CellLayout layout = (CellLayout) getChildAt(i); 843 layout.setChildrenDrawnWithCacheEnabled(false); 844 } 845 } 846 847 @Override 848 public boolean onTouchEvent(MotionEvent ev) { 849 850 if (mLauncher.isWorkspaceLocked()) { 851 return false; // We don't want the events. Let them fall through to the all apps view. 852 } 853 if (mLauncher.isAllAppsVisible()) { 854 // Cancel any scrolling that is in progress. 855 if (!mScroller.isFinished()) { 856 mScroller.abortAnimation(); 857 } 858 snapToScreen(mCurrentScreen); 859 return false; // We don't want the events. Let them fall through to the all apps view. 860 } 861 862 if (mVelocityTracker == null) { 863 mVelocityTracker = VelocityTracker.obtain(); 864 } 865 mVelocityTracker.addMovement(ev); 866 867 final int action = ev.getAction(); 868 final float x = ev.getX(); 869 870 switch (action) { 871 case MotionEvent.ACTION_DOWN: 872 /* 873 * If being flinged and user touches, stop the fling. isFinished 874 * will be false if being flinged. 875 */ 876 if (!mScroller.isFinished()) { 877 mScroller.abortAnimation(); 878 } 879 880 // Remember where the motion event started 881 mLastMotionX = x; 882 break; 883 case MotionEvent.ACTION_MOVE: 884 if (mTouchState == TOUCH_STATE_SCROLLING) { 885 // Scroll to follow the motion event 886 final int deltaX = (int) (mLastMotionX - x); 887 mLastMotionX = x; 888 889 if (deltaX < 0) { 890 if (mScrollX > 0) { 891 scrollBy(Math.max(-mScrollX, deltaX), 0); 892 updateWallpaperOffset(); 893 } 894 } else if (deltaX > 0) { 895 final int availableToScroll = getChildAt(getChildCount() - 1).getRight() - 896 mScrollX - getWidth(); 897 if (availableToScroll > 0) { 898 scrollBy(Math.min(availableToScroll, deltaX), 0); 899 updateWallpaperOffset(); 900 } 901 } else { 902 awakenScrollBars(); 903 } 904 } 905 break; 906 case MotionEvent.ACTION_UP: 907 if (mTouchState == TOUCH_STATE_SCROLLING) { 908 final VelocityTracker velocityTracker = mVelocityTracker; 909 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 910 int velocityX = (int) velocityTracker.getXVelocity(); 911 912 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 913 // Fling hard enough to move left 914 snapToScreen(mCurrentScreen - 1); 915 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { 916 // Fling hard enough to move right 917 snapToScreen(mCurrentScreen + 1); 918 } else { 919 snapToDestination(); 920 } 921 922 if (mVelocityTracker != null) { 923 mVelocityTracker.recycle(); 924 mVelocityTracker = null; 925 } 926 } 927 mTouchState = TOUCH_STATE_REST; 928 break; 929 case MotionEvent.ACTION_CANCEL: 930 mTouchState = TOUCH_STATE_REST; 931 } 932 933 return true; 934 } 935 936 private void snapToDestination() { 937 final int screenWidth = getWidth(); 938 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; 939 940 snapToScreen(whichScreen); 941 } 942 943 void snapToScreen(int whichScreen) { 944 //if (!mScroller.isFinished()) return; 945 946 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 947 948 clearVacantCache(); 949 enableChildrenCache(mCurrentScreen, whichScreen); 950 951 final int screenDelta = Math.abs(whichScreen - mCurrentScreen); 952 953 mNextScreen = whichScreen; 954 955 mPreviousIndicator.setLevel(mNextScreen); 956 mNextIndicator.setLevel(mNextScreen); 957 958 View focusedChild = getFocusedChild(); 959 if (focusedChild != null && screenDelta != 0 && focusedChild == getChildAt(mCurrentScreen)) { 960 focusedChild.clearFocus(); 961 } 962 963 final int newX = whichScreen * getWidth(); 964 final int delta = newX - mScrollX; 965 final int duration = screenDelta * 300; 966 awakenScrollBars(duration); 967 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 968 invalidate(); 969 } 970 971 void startDrag(CellLayout.CellInfo cellInfo) { 972 View child = cellInfo.cell; 973 974 // Make sure the drag was started by a long press as opposed to a long click. 975 // Note that Search takes focus when clicked rather than entering touch mode 976 if (!child.isInTouchMode() && !(child instanceof Search)) { 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(ApplicationInfo info, CellLayout.CellInfo cellInfo) { 1008 addApplicationShortcut(info, cellInfo, false); 1009 } 1010 1011 void addApplicationShortcut(ApplicationInfo 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) { 1076 // Came from all apps -- make a copy 1077 info = new ApplicationInfo((ApplicationInfo) info); 1078 } 1079 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1080 (ApplicationInfo) info); 1081 break; 1082 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1083 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1084 (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info)); 1085 break; 1086 default: 1087 throw new IllegalStateException("Unknown item type: " + info.itemType); 1088 } 1089 1090 cellLayout.addView(view, insertAtFirst ? 0 : -1); 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 (mNextScreen == INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()) { 1212 snapToScreen(mCurrentScreen - 1); 1213 } 1214 } 1215 1216 public void scrollRight() { 1217 clearVacantCache(); 1218 if (mNextScreen == INVALID_SCREEN && mCurrentScreen < getChildCount() -1 && 1219 mScroller.isFinished()) { 1220 snapToScreen(mCurrentScreen + 1); 1221 } 1222 } 1223 1224 public int getScreenForView(View v) { 1225 int result = -1; 1226 if (v != null) { 1227 ViewParent vp = v.getParent(); 1228 int count = getChildCount(); 1229 for (int i = 0; i < count; i++) { 1230 if (vp == getChildAt(i)) { 1231 return i; 1232 } 1233 } 1234 } 1235 return result; 1236 } 1237 1238 /** 1239 * Find a search widget on the given screen 1240 */ 1241 private Search findSearchWidget(CellLayout screen) { 1242 final int count = screen.getChildCount(); 1243 for (int i = 0; i < count; i++) { 1244 View v = screen.getChildAt(i); 1245 if (v instanceof Search) { 1246 return (Search) v; 1247 } 1248 } 1249 return null; 1250 } 1251 1252 /** 1253 * Gets the first search widget on the current screen, if there is one. 1254 * Returns <code>null</code> otherwise. 1255 */ 1256 public Search findSearchWidgetOnCurrentScreen() { 1257 CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 1258 return findSearchWidget(currentScreen); 1259 } 1260 1261 public Folder getFolderForTag(Object tag) { 1262 int screenCount = getChildCount(); 1263 for (int screen = 0; screen < screenCount; screen++) { 1264 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1265 int count = currentScreen.getChildCount(); 1266 for (int i = 0; i < count; i++) { 1267 View child = currentScreen.getChildAt(i); 1268 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1269 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1270 Folder f = (Folder) child; 1271 if (f.getInfo() == tag) { 1272 return f; 1273 } 1274 } 1275 } 1276 } 1277 return null; 1278 } 1279 1280 public View getViewForTag(Object tag) { 1281 int screenCount = getChildCount(); 1282 for (int screen = 0; screen < screenCount; screen++) { 1283 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1284 int count = currentScreen.getChildCount(); 1285 for (int i = 0; i < count; i++) { 1286 View child = currentScreen.getChildAt(i); 1287 if (child.getTag() == tag) { 1288 return child; 1289 } 1290 } 1291 } 1292 return null; 1293 } 1294 1295 /** 1296 * @return True is long presses are still allowed for the current touch 1297 */ 1298 public boolean allowLongPress() { 1299 return mAllowLongPress; 1300 } 1301 1302 /** 1303 * Set true to allow long-press events to be triggered, usually checked by 1304 * {@link Launcher} to accept or block dpad-initiated long-presses. 1305 */ 1306 public void setAllowLongPress(boolean allowLongPress) { 1307 mAllowLongPress = allowLongPress; 1308 } 1309 1310 void removeShortcutsForPackage(String packageName) { 1311 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1312 final int count = getChildCount(); 1313 1314 for (int i = 0; i < count; i++) { 1315 final CellLayout layout = (CellLayout) getChildAt(i); 1316 int childCount = layout.getChildCount(); 1317 1318 childrenToRemove.clear(); 1319 1320 for (int j = 0; j < childCount; j++) { 1321 final View view = layout.getChildAt(j); 1322 Object tag = view.getTag(); 1323 1324 if (tag instanceof ApplicationInfo) { 1325 final ApplicationInfo info = (ApplicationInfo) tag; 1326 // We need to check for ACTION_MAIN otherwise getComponent() might 1327 // return null for some shortcuts (for instance, for shortcuts to 1328 // web pages.) 1329 final Intent intent = info.intent; 1330 final ComponentName name = intent.getComponent(); 1331 1332 if (Intent.ACTION_MAIN.equals(intent.getAction()) && 1333 name != null && packageName.equals(name.getPackageName())) { 1334 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1335 childrenToRemove.add(view); 1336 } 1337 } else if (tag instanceof UserFolderInfo) { 1338 final UserFolderInfo info = (UserFolderInfo) tag; 1339 final ArrayList<ApplicationInfo> contents = info.contents; 1340 final ArrayList<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(1); 1341 final int contentsCount = contents.size(); 1342 boolean removedFromFolder = false; 1343 1344 for (int k = 0; k < contentsCount; k++) { 1345 final ApplicationInfo appInfo = contents.get(k); 1346 final Intent intent = appInfo.intent; 1347 final ComponentName name = intent.getComponent(); 1348 1349 if (Intent.ACTION_MAIN.equals(intent.getAction()) && 1350 name != null && packageName.equals(name.getPackageName())) { 1351 toRemove.add(appInfo); 1352 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo); 1353 removedFromFolder = true; 1354 } 1355 } 1356 1357 contents.removeAll(toRemove); 1358 if (removedFromFolder) { 1359 final Folder folder = getOpenFolder(); 1360 if (folder != null) folder.notifyDataSetChanged(); 1361 } 1362 } 1363 } 1364 1365 childCount = childrenToRemove.size(); 1366 for (int j = 0; j < childCount; j++) { 1367 View child = childrenToRemove.get(j); 1368 layout.removeViewInLayout(child); 1369 if (child instanceof DropTarget) { 1370 mDragController.removeDropTarget((DropTarget)child); 1371 } 1372 } 1373 1374 if (childCount > 0) { 1375 layout.requestLayout(); 1376 layout.invalidate(); 1377 } 1378 } 1379 } 1380 1381 void updateShortcutsForPackage(String packageName) { 1382 final PackageManager pm = mLauncher.getPackageManager(); 1383 1384 final int count = getChildCount(); 1385 for (int i = 0; i < count; i++) { 1386 final CellLayout layout = (CellLayout) getChildAt(i); 1387 int childCount = layout.getChildCount(); 1388 for (int j = 0; j < childCount; j++) { 1389 final View view = layout.getChildAt(j); 1390 Object tag = view.getTag(); 1391 if (tag instanceof ApplicationInfo) { 1392 ApplicationInfo info = (ApplicationInfo) tag; 1393 // We need to check for ACTION_MAIN otherwise getComponent() might 1394 // return null for some shortcuts (for instance, for shortcuts to 1395 // web pages.) 1396 final Intent intent = info.intent; 1397 final ComponentName name = intent.getComponent(); 1398 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1399 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null && 1400 packageName.equals(name.getPackageName())) { 1401 1402 final Drawable icon = AppInfoCache.getIconDrawable(pm, info); 1403 if (icon != null && icon != info.icon) { 1404 info.icon.setCallback(null); 1405 info.icon = Utilities.createIconThumbnail(icon, mContext); 1406 info.filtered = true; 1407 ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null, 1408 info.icon, null, null); 1409 } 1410 } 1411 } 1412 } 1413 } 1414 } 1415 1416 void moveToDefaultScreen() { 1417 snapToScreen(mDefaultScreen); 1418 getChildAt(mDefaultScreen).requestFocus(); 1419 } 1420 1421 void setIndicators(Drawable previous, Drawable next) { 1422 mPreviousIndicator = previous; 1423 mNextIndicator = next; 1424 previous.setLevel(mCurrentScreen); 1425 next.setLevel(mCurrentScreen); 1426 } 1427 1428 public static class SavedState extends BaseSavedState { 1429 int currentScreen = -1; 1430 1431 SavedState(Parcelable superState) { 1432 super(superState); 1433 } 1434 1435 private SavedState(Parcel in) { 1436 super(in); 1437 currentScreen = in.readInt(); 1438 } 1439 1440 @Override 1441 public void writeToParcel(Parcel out, int flags) { 1442 super.writeToParcel(out, flags); 1443 out.writeInt(currentScreen); 1444 } 1445 1446 public static final Parcelable.Creator<SavedState> CREATOR = 1447 new Parcelable.Creator<SavedState>() { 1448 public SavedState createFromParcel(Parcel in) { 1449 return new SavedState(in); 1450 } 1451 1452 public SavedState[] newArray(int size) { 1453 return new SavedState[size]; 1454 } 1455 }; 1456 } 1457 1458 void show() { 1459 setVisibility(VISIBLE); 1460 } 1461 1462 void hide() { 1463 } 1464} 1465