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