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