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