Workspace.java revision 7f0f4f3c35fe0b6a94504ec39ac127fa935282ef
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 com.android.launcher.R; 20import com.android.launcher2.CellLayout.CellInfo; 21 22import android.animation.Animatable; 23import android.animation.PropertyAnimator; 24import android.animation.Sequencer; 25import android.animation.Animatable.AnimatableListener; 26import android.app.WallpaperManager; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProviderInfo; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.pm.PackageManager; 33import android.content.pm.ProviderInfo; 34import android.content.res.Resources; 35import android.content.res.TypedArray; 36import android.graphics.Canvas; 37import android.graphics.Matrix; 38import android.graphics.Paint; 39import android.graphics.Rect; 40import android.graphics.drawable.Drawable; 41import android.net.Uri; 42import android.os.IBinder; 43import android.os.Parcel; 44import android.os.Parcelable; 45import android.util.AttributeSet; 46import android.util.Log; 47import android.view.MotionEvent; 48import android.view.VelocityTracker; 49import android.view.View; 50import android.view.ViewConfiguration; 51import android.view.ViewGroup; 52import android.view.ViewParent; 53import android.view.animation.Interpolator; 54import android.widget.Scroller; 55import android.widget.TextView; 56import android.widget.Toast; 57 58import java.util.ArrayList; 59import java.util.HashSet; 60 61/** 62 * The workspace is a wide area with a wallpaper and a finite number of screens. 63 * Each screen contains a number of icons, folders or widgets the user can 64 * interact with. A workspace is meant to be used with a fixed width only. 65 */ 66public class Workspace extends ViewGroup 67 implements DropTarget, DragSource, DragScroller, View.OnTouchListener { 68 @SuppressWarnings({"UnusedDeclaration"}) 69 private static final String TAG = "Launcher.Workspace"; 70 private static final int INVALID_SCREEN = -1; 71 // This is how much the workspace shrinks when we enter all apps or 72 // customization mode 73 private static final float SHRINK_FACTOR = 0.16f; 74 private static final int SHRINK_TO_TOP = 0; 75 private static final int SHRINK_TO_MIDDLE = 1; 76 private static final int SHRINK_TO_BOTTOM = 2; 77 78 /** 79 * The velocity at which a fling gesture will cause us to snap to the next 80 * screen 81 */ 82 private static final int SNAP_VELOCITY = 600; 83 84 private final WallpaperManager mWallpaperManager; 85 86 private int mDefaultScreen; 87 88 private boolean mFirstLayout = true; 89 private boolean mWaitingToShrinkToBottom = false; 90 91 private int mCurrentScreen; 92 private int mNextScreen = INVALID_SCREEN; 93 private Scroller mScroller; 94 private VelocityTracker mVelocityTracker; 95 96 /** 97 * CellInfo for the cell that is currently being dragged 98 */ 99 private CellLayout.CellInfo mDragInfo; 100 101 /** 102 * Target drop area calculated during last acceptDrop call. 103 */ 104 private int[] mTargetCell = null; 105 106 /** 107 * The CellLayout that is currently being dragged over 108 */ 109 private CellLayout mDragTargetLayout = null; 110 111 private float mLastMotionX; 112 private float mLastMotionY; 113 114 private final static int TOUCH_STATE_REST = 0; 115 private final static int TOUCH_STATE_SCROLLING = 1; 116 117 private int mTouchState = TOUCH_STATE_REST; 118 119 private OnLongClickListener mLongClickListener; 120 121 private Launcher mLauncher; 122 private IconCache mIconCache; 123 private DragController mDragController; 124 125 126 private int[] mTempCell = new int[2]; 127 private int[] mTempEstimate = new int[2]; 128 private float[] mTempDragCoordinates = new float[2]; 129 private float[] mTempDragBottomRightCoordinates = new float[2]; 130 131 private boolean mAllowLongPress = true; 132 133 private int mTouchSlop; 134 private int mMaximumVelocity; 135 136 private static final int INVALID_POINTER = -1; 137 private static final int DEFAULT_CELL_COUNT_X = 4; 138 private static final int DEFAULT_CELL_COUNT_Y = 4; 139 140 private int mActivePointerId = INVALID_POINTER; 141 142 private Drawable mPreviousIndicator; 143 private Drawable mNextIndicator; 144 145 private static final float NANOTIME_DIV = 1000000000.0f; 146 private static final float SMOOTHING_SPEED = 0.75f; 147 private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); 148 private float mSmoothingTime; 149 private float mTouchX; 150 151 private WorkspaceOvershootInterpolator mScrollInterpolator; 152 153 private static final float BASELINE_FLING_VELOCITY = 2500.f; 154 private static final float FLING_VELOCITY_INFLUENCE = 0.4f; 155 156 private Paint mDropIndicatorPaint; 157 158 // State variable that indicated whether the screens are small (ie when you're 159 // in all apps or customize mode) 160 private boolean mIsSmall; 161 private AnimatableListener mUnshrinkAnimationListener; 162 163 private static class WorkspaceOvershootInterpolator implements Interpolator { 164 private static final float DEFAULT_TENSION = 1.3f; 165 private float mTension; 166 167 public WorkspaceOvershootInterpolator() { 168 mTension = DEFAULT_TENSION; 169 } 170 171 public void setDistance(int distance) { 172 mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; 173 } 174 175 public void disableSettle() { 176 mTension = 0.f; 177 } 178 179 public float getInterpolation(float t) { 180 // _o(t) = t * t * ((tension + 1) * t + tension) 181 // o(t) = _o(t - 1) + 1 182 t -= 1.0f; 183 return t * t * ((mTension + 1) * t + mTension) + 1.0f; 184 } 185 } 186 187 /** 188 * Used to inflate the Workspace from XML. 189 * 190 * @param context The application's context. 191 * @param attrs The attribtues set containing the Workspace's customization values. 192 */ 193 public Workspace(Context context, AttributeSet attrs) { 194 this(context, attrs, 0); 195 } 196 197 /** 198 * Used to inflate the Workspace from XML. 199 * 200 * @param context The application's context. 201 * @param attrs The attribtues set containing the Workspace's customization values. 202 * @param defStyle Unused. 203 */ 204 public Workspace(Context context, AttributeSet attrs, int defStyle) { 205 super(context, attrs, defStyle); 206 207 mWallpaperManager = WallpaperManager.getInstance(context); 208 209 TypedArray a = context.obtainStyledAttributes(attrs, 210 R.styleable.Workspace, defStyle, 0); 211 int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X); 212 int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y); 213 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); 214 a.recycle(); 215 216 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 217 setHapticFeedbackEnabled(false); 218 initWorkspace(); 219 } 220 221 /** 222 * Initializes various states for this workspace. 223 */ 224 private void initWorkspace() { 225 Context context = getContext(); 226 mScrollInterpolator = new WorkspaceOvershootInterpolator(); 227 mScroller = new Scroller(context, mScrollInterpolator); 228 mCurrentScreen = mDefaultScreen; 229 Launcher.setScreen(mCurrentScreen); 230 LauncherApplication app = (LauncherApplication)context.getApplicationContext(); 231 mIconCache = app.getIconCache(); 232 233 final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 234 mTouchSlop = configuration.getScaledTouchSlop(); 235 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 236 mUnshrinkAnimationListener = new AnimatableListener() { 237 public void onAnimationStart(Animatable animation) {} 238 public void onAnimationEnd(Animatable animation) { 239 mIsSmall = false; 240 } 241 public void onAnimationCancel(Animatable animation) {} 242 public void onAnimationRepeat(Animatable animation) {} 243 }; 244 } 245 246 @Override 247 public void addView(View child, int index, LayoutParams params) { 248 if (!(child instanceof CellLayout)) { 249 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 250 } 251 super.addView(child, index, params); 252 } 253 254 @Override 255 public void addView(View child) { 256 if (!(child instanceof CellLayout)) { 257 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 258 } 259 super.addView(child); 260 } 261 262 @Override 263 public void addView(View child, int index) { 264 if (!(child instanceof CellLayout)) { 265 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 266 } 267 super.addView(child, index); 268 } 269 270 @Override 271 public void addView(View child, int width, int height) { 272 if (!(child instanceof CellLayout)) { 273 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 274 } 275 super.addView(child, width, height); 276 } 277 278 @Override 279 public void addView(View child, LayoutParams params) { 280 if (!(child instanceof CellLayout)) { 281 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 282 } 283 super.addView(child, params); 284 } 285 286 /** 287 * @return The open folder on the current screen, or null if there is none 288 */ 289 Folder getOpenFolder() { 290 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); 291 int count = currentScreen.getChildCount(); 292 for (int i = 0; i < count; i++) { 293 View child = currentScreen.getChildAt(i); 294 if (child instanceof Folder) { 295 Folder folder = (Folder) child; 296 if (folder.getInfo().opened) 297 return folder; 298 } 299 } 300 return null; 301 } 302 303 ArrayList<Folder> getOpenFolders() { 304 final int screenCount = getChildCount(); 305 ArrayList<Folder> folders = new ArrayList<Folder>(screenCount); 306 307 for (int screen = 0; screen < screenCount; screen++) { 308 CellLayout currentScreen = (CellLayout) getChildAt(screen); 309 int count = currentScreen.getChildCount(); 310 for (int i = 0; i < count; i++) { 311 View child = currentScreen.getChildAt(i); 312 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child 313 .getLayoutParams(); 314 if (child instanceof Folder) { 315 Folder folder = (Folder) child; 316 if (folder.getInfo().opened) 317 folders.add(folder); 318 break; 319 } 320 } 321 } 322 323 return folders; 324 } 325 326 boolean isDefaultScreenShowing() { 327 return mCurrentScreen == mDefaultScreen; 328 } 329 330 /** 331 * Returns the index of the currently displayed screen. 332 * 333 * @return The index of the currently displayed screen. 334 */ 335 int getCurrentScreen() { 336 return mCurrentScreen; 337 } 338 339 /** 340 * Sets the current screen. 341 * 342 * @param currentScreen 343 */ 344 void setCurrentScreen(int currentScreen) { 345 setCurrentScreen(currentScreen, true); 346 } 347 348 void setCurrentScreen(int currentScreen, boolean animateScrolling) { 349 setCurrentScreen(currentScreen, animateScrolling, getWidth()); 350 } 351 352 void setCurrentScreen(int currentScreen, boolean animateScrolling, int screenWidth) { 353 if (!mScroller.isFinished()) 354 mScroller.abortAnimation(); 355 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); 356 if (mPreviousIndicator != null) { 357 mPreviousIndicator.setLevel(mCurrentScreen); 358 mNextIndicator.setLevel(mCurrentScreen); 359 } 360 if (animateScrolling) { 361 scrollTo(mCurrentScreen * screenWidth, 0); 362 } else { 363 mScrollX = mCurrentScreen * screenWidth; 364 } 365 updateWallpaperOffset(screenWidth * (getChildCount() - 1)); 366 invalidate(); 367 } 368 369 /** 370 * Adds the specified child in the current screen. The position and dimension of 371 * the child are defined by x, y, spanX and spanY. 372 * 373 * @param child The child to add in one of the workspace's screens. 374 * @param x The X position of the child in the screen's grid. 375 * @param y The Y position of the child in the screen's grid. 376 * @param spanX The number of cells spanned horizontally by the child. 377 * @param spanY The number of cells spanned vertically by the child. 378 */ 379 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) { 380 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false); 381 } 382 383 /** 384 * Adds the specified child in the current screen. The position and dimension of 385 * the child are defined by x, y, spanX and spanY. 386 * 387 * @param child The child to add in one of the workspace's screens. 388 * @param x The X position of the child in the screen's grid. 389 * @param y The Y position of the child in the screen's grid. 390 * @param spanX The number of cells spanned horizontally by the child. 391 * @param spanY The number of cells spanned vertically by the child. 392 * @param insert When true, the child is inserted at the beginning of the children list. 393 */ 394 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) { 395 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert); 396 } 397 398 /** 399 * Adds the specified child in the specified 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 screen The screen in which to add the child. 404 * @param x The X position of the child in the screen's grid. 405 * @param y The Y position of the child in the screen's grid. 406 * @param spanX The number of cells spanned horizontally by the child. 407 * @param spanY The number of cells spanned vertically by the child. 408 */ 409 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) { 410 addInScreen(child, screen, x, y, spanX, spanY, false); 411 } 412 413 void addInFullScreen(View child, int screen) { 414 addInScreen(child, screen, 0, 0, -1, -1); 415 } 416 417 /** 418 * Adds the specified child in the specified screen. The position and dimension of 419 * the child are defined by x, y, spanX and spanY. 420 * 421 * @param child The child to add in one of the workspace's screens. 422 * @param screen The screen in which to add the child. 423 * @param x The X position of the child in the screen's grid. 424 * @param y The Y position of the child in the screen's grid. 425 * @param spanX The number of cells spanned horizontally by the child. 426 * @param spanY The number of cells spanned vertically by the child. 427 * @param insert When true, the child is inserted at the beginning of the children list. 428 */ 429 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) { 430 if (screen < 0 || screen >= getChildCount()) { 431 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount() 432 + " (was " + screen + "); skipping child"); 433 return; 434 } 435 436 final CellLayout group = (CellLayout) getChildAt(screen); 437 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 438 if (lp == null) { 439 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 440 } else { 441 lp.cellX = x; 442 lp.cellY = y; 443 lp.cellHSpan = spanX; 444 lp.cellVSpan = spanY; 445 } 446 447 // Get the canonical child id to uniquely represent this view in this screen 448 int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); 449 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { 450 // TODO: This branch occurs when the workspace is adding views 451 // outside of the defined grid 452 // maybe we should be deleting these items from the LauncherModel? 453 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 454 } 455 456 if (!(child instanceof Folder)) { 457 child.setHapticFeedbackEnabled(false); 458 child.setOnLongClickListener(mLongClickListener); 459 } 460 if (child instanceof DropTarget) { 461 mDragController.addDropTarget((DropTarget) child); 462 } 463 } 464 465 CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) { 466 CellLayout group = (CellLayout) getChildAt(mCurrentScreen); 467 if (group != null) { 468 return group.updateOccupiedCells(occupied, null); 469 } 470 return null; 471 } 472 473 public boolean onTouch(View v, MotionEvent event) { 474 // this is an intercepted event being forwarded from a cell layout 475 if (mIsSmall) { 476 unshrink((CellLayout)v); 477 mLauncher.onWorkspaceUnshrink(); 478 return true; 479 } 480 return false; 481 } 482 483 /** 484 * Registers the specified listener on each screen contained in this workspace. 485 * 486 * @param l The listener used to respond to long clicks. 487 */ 488 @Override 489 public void setOnLongClickListener(OnLongClickListener l) { 490 mLongClickListener = l; 491 final int screenCount = getChildCount(); 492 for (int i = 0; i < screenCount; i++) { 493 getChildAt(i).setOnLongClickListener(l); 494 } 495 } 496 497 private void updateWallpaperOffset() { 498 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft)); 499 } 500 501 private void updateWallpaperOffset(int scrollRange) { 502 final boolean isStaticWallpaper = (mWallpaperManager != null) && 503 (mWallpaperManager.getWallpaperInfo() == null); 504 if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) { 505 IBinder token = getWindowToken(); 506 if (token != null) { 507 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 ); 508 mWallpaperManager.setWallpaperOffsets(getWindowToken(), 509 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 510 } 511 } 512 } 513 514 @Override 515 public void scrollTo(int x, int y) { 516 super.scrollTo(x, y); 517 mTouchX = x; 518 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 519 } 520 521 @Override 522 public void computeScroll() { 523 if (mScroller.computeScrollOffset()) { 524 mTouchX = mScrollX = mScroller.getCurrX(); 525 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 526 mScrollY = mScroller.getCurrY(); 527 updateWallpaperOffset(); 528 postInvalidate(); 529 } else if (mNextScreen != INVALID_SCREEN) { 530 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); 531 if (mPreviousIndicator != null) { 532 mPreviousIndicator.setLevel(mCurrentScreen); 533 mNextIndicator.setLevel(mCurrentScreen); 534 } 535 Launcher.setScreen(mCurrentScreen); 536 mNextScreen = INVALID_SCREEN; 537 clearChildrenCache(); 538 } else if (mTouchState == TOUCH_STATE_SCROLLING) { 539 final float now = System.nanoTime() / NANOTIME_DIV; 540 final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT); 541 final float dx = mTouchX - mScrollX; 542 mScrollX += dx * e; 543 mSmoothingTime = now; 544 545 // Keep generating points as long as we're more than 1px away from the target 546 if (dx > 1.f || dx < -1.f) { 547 updateWallpaperOffset(); 548 postInvalidate(); 549 } 550 } 551 } 552 553 @Override 554 protected void dispatchDraw(Canvas canvas) { 555 boolean restore = false; 556 int restoreCount = 0; 557 558 // ViewGroup.dispatchDraw() supports many features we don't need: 559 // clip to padding, layout animation, animation listener, disappearing 560 // children, etc. The following implementation attempts to fast-track 561 // the drawing dispatch by drawing only what we know needs to be drawn. 562 563 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN; 564 565 // if the screens are all small, we need to draw all the screens since 566 // they're most likely all visible 567 if (mIsSmall) { 568 final int screenCount = getChildCount(); 569 for (int i = 0; i < screenCount; i++) { 570 CellLayout cl = (CellLayout)getChildAt(i); 571 drawChild(canvas, cl, getDrawingTime()); 572 } 573 } else if (fastDraw) { 574 // If we are not scrolling or flinging, draw only the current screen 575 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime()); 576 } else { 577 final long drawingTime = getDrawingTime(); 578 final float scrollPos = (float) mScrollX / getWidth(); 579 final int leftScreen = (int) scrollPos; 580 final int rightScreen = leftScreen + 1; 581 if (leftScreen >= 0) { 582 drawChild(canvas, getChildAt(leftScreen), drawingTime); 583 } 584 if (scrollPos != leftScreen && rightScreen < getChildCount()) { 585 drawChild(canvas, getChildAt(rightScreen), drawingTime); 586 } 587 } 588 589 if (restore) { 590 canvas.restoreToCount(restoreCount); 591 } 592 } 593 594 protected void onAttachedToWindow() { 595 super.onAttachedToWindow(); 596 computeScroll(); 597 mDragController.setWindowToken(getWindowToken()); 598 } 599 600 @Override 601 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 602 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 603 604 final int width = MeasureSpec.getSize(widthMeasureSpec); 605 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 606 if (widthMode != MeasureSpec.EXACTLY) { 607 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 608 } 609 610 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 611 if (heightMode != MeasureSpec.EXACTLY) { 612 throw new IllegalStateException("Workspace can only be used in EXACTLY mode."); 613 } 614 615 // The children are given the same width and height as the workspace 616 final int screenCount = getChildCount(); 617 for (int i = 0; i < screenCount; i++) { 618 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 619 } 620 621 if (mFirstLayout) { 622 setHorizontalScrollBarEnabled(false); 623 setCurrentScreen(mCurrentScreen, false, width); 624 setHorizontalScrollBarEnabled(true); 625 } 626 } 627 628 @Override 629 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 630 if (mFirstLayout) { 631 mFirstLayout = false; 632 } 633 int childLeft = 0; 634 final int screenCount = getChildCount(); 635 for (int i = 0; i < screenCount; i++) { 636 final View child = getChildAt(i); 637 if (child.getVisibility() != View.GONE) { 638 final int childWidth = child.getMeasuredWidth(); 639 child.layout(childLeft, 0, 640 childLeft + childWidth, child.getMeasuredHeight()); 641 childLeft += childWidth; 642 } 643 } 644 645 // if shrinkToBottom() is called on initialization, it has to be deferred 646 // until after the first call to onLayout so that it has the correct width 647 if (mWaitingToShrinkToBottom) { 648 shrinkToBottom(false); 649 mWaitingToShrinkToBottom = false; 650 } 651 652 if (LauncherApplication.isInPlaceRotationEnabled()) { 653 // When the device is rotated, the scroll position of the current screen 654 // needs to be refreshed 655 setCurrentScreen(getCurrentScreen()); 656 } 657 } 658 659 @Override 660 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { 661 int screen = indexOfChild(child); 662 if (screen != mCurrentScreen || !mScroller.isFinished()) { 663 if (!mLauncher.isWorkspaceLocked()) { 664 snapToScreen(screen); 665 } 666 return true; 667 } 668 return false; 669 } 670 671 @Override 672 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 673 if (!mLauncher.isAllAppsVisible()) { 674 final Folder openFolder = getOpenFolder(); 675 if (openFolder != null) { 676 return openFolder.requestFocus(direction, previouslyFocusedRect); 677 } else { 678 int focusableScreen; 679 if (mNextScreen != INVALID_SCREEN) { 680 focusableScreen = mNextScreen; 681 } else { 682 focusableScreen = mCurrentScreen; 683 } 684 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); 685 } 686 } 687 return false; 688 } 689 690 @Override 691 public boolean dispatchUnhandledMove(View focused, int direction) { 692 if (direction == View.FOCUS_LEFT) { 693 if (getCurrentScreen() > 0) { 694 snapToScreen(getCurrentScreen() - 1); 695 return true; 696 } 697 } else if (direction == View.FOCUS_RIGHT) { 698 if (getCurrentScreen() < getChildCount() - 1) { 699 snapToScreen(getCurrentScreen() + 1); 700 return true; 701 } 702 } 703 return super.dispatchUnhandledMove(focused, direction); 704 } 705 706 @Override 707 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 708 if (!mLauncher.isAllAppsVisible()) { 709 final Folder openFolder = getOpenFolder(); 710 if (openFolder == null) { 711 getChildAt(mCurrentScreen).addFocusables(views, direction); 712 if (direction == View.FOCUS_LEFT) { 713 if (mCurrentScreen > 0) { 714 getChildAt(mCurrentScreen - 1).addFocusables(views, direction); 715 } 716 } else if (direction == View.FOCUS_RIGHT) { 717 if (mCurrentScreen < getChildCount() - 1) { 718 getChildAt(mCurrentScreen + 1).addFocusables(views, direction); 719 } 720 } 721 } else { 722 openFolder.addFocusables(views, direction); 723 } 724 } 725 } 726 727 @Override 728 public boolean dispatchTouchEvent(MotionEvent ev) { 729 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 730 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 731 // ie when you click on a mini-screen, it zooms back to that screen) 732 if (mLauncher.isWorkspaceLocked() || 733 (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible())) { 734 return false; 735 } 736 } 737 return super.dispatchTouchEvent(ev); 738 } 739 740 /** 741 * {@inheritDoc} 742 */ 743 @Override 744 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 745 if (disallowIntercept) { 746 // We need to make sure to cancel our long press if 747 // a scrollable widget takes over touch events 748 final View currentScreen = getChildAt(mCurrentScreen); 749 currentScreen.cancelLongPress(); 750 } 751 super.requestDisallowInterceptTouchEvent(disallowIntercept); 752 } 753 754 @Override 755 public boolean onInterceptTouchEvent(MotionEvent ev) { 756 final boolean workspaceLocked = mLauncher.isWorkspaceLocked(); 757 final boolean allAppsVisible = mLauncher.isAllAppsVisible(); 758 759 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 760 // ie when you click on a mini-screen, it zooms back to that screen) 761 if (workspaceLocked || (!LauncherApplication.isScreenXLarge() && allAppsVisible)) { 762 return false; // We don't want the events. Let them fall through to the all apps view. 763 } 764 765 /* 766 * This method JUST determines whether we want to intercept the motion. 767 * If we return true, onTouchEvent will be called and we do the actual 768 * scrolling there. 769 */ 770 771 /* 772 * Shortcut the most recurring case: the user is in the dragging 773 * state and he is moving his finger. We want to intercept this 774 * motion. 775 */ 776 final int action = ev.getAction(); 777 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { 778 return true; 779 } 780 781 if (mVelocityTracker == null) { 782 mVelocityTracker = VelocityTracker.obtain(); 783 } 784 mVelocityTracker.addMovement(ev); 785 786 switch (action & MotionEvent.ACTION_MASK) { 787 case MotionEvent.ACTION_MOVE: { 788 /* 789 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 790 * whether the user has moved far enough from his original down touch. 791 */ 792 793 /* 794 * Locally do absolute value. mLastMotionX is set to the y value 795 * of the down event. 796 */ 797 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 798 final float x = ev.getX(pointerIndex); 799 final float y = ev.getY(pointerIndex); 800 final int xDiff = (int) Math.abs(x - mLastMotionX); 801 final int yDiff = (int) Math.abs(y - mLastMotionY); 802 803 final int touchSlop = mTouchSlop; 804 boolean xMoved = xDiff > touchSlop; 805 boolean yMoved = yDiff > touchSlop; 806 807 if (xMoved || yMoved) { 808 809 if (xMoved) { 810 // Scroll if the user moved far enough along the X axis 811 mTouchState = TOUCH_STATE_SCROLLING; 812 mLastMotionX = x; 813 mTouchX = mScrollX; 814 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 815 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 816 } 817 // Either way, cancel any pending longpress 818 if (mAllowLongPress) { 819 mAllowLongPress = false; 820 // Try canceling the long press. It could also have been scheduled 821 // by a distant descendant, so use the mAllowLongPress flag to block 822 // everything 823 final View currentScreen = getChildAt(mCurrentScreen); 824 currentScreen.cancelLongPress(); 825 } 826 } 827 break; 828 } 829 830 case MotionEvent.ACTION_DOWN: { 831 final float x = ev.getX(); 832 final float y = ev.getY(); 833 // Remember location of down touch 834 mLastMotionX = x; 835 mLastMotionY = y; 836 mActivePointerId = ev.getPointerId(0); 837 mAllowLongPress = true; 838 839 /* 840 * If being flinged and user touches the screen, initiate drag; 841 * otherwise don't. mScroller.isFinished should be false when 842 * being flinged. 843 */ 844 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 845 break; 846 } 847 848 case MotionEvent.ACTION_CANCEL: 849 case MotionEvent.ACTION_UP: 850 851 if (mTouchState != TOUCH_STATE_SCROLLING) { 852 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 853 if (!currentScreen.lastDownOnOccupiedCell()) { 854 getLocationOnScreen(mTempCell); 855 // Send a tap to the wallpaper if the last down was on empty space 856 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 857 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 858 "android.wallpaper.tap", 859 mTempCell[0] + (int) ev.getX(pointerIndex), 860 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); 861 } 862 } 863 864 // Release the drag 865 clearChildrenCache(); 866 mTouchState = TOUCH_STATE_REST; 867 mActivePointerId = INVALID_POINTER; 868 mAllowLongPress = false; 869 870 if (mVelocityTracker != null) { 871 mVelocityTracker.recycle(); 872 mVelocityTracker = null; 873 } 874 875 break; 876 877 case MotionEvent.ACTION_POINTER_UP: 878 onSecondaryPointerUp(ev); 879 break; 880 } 881 882 /* 883 * The only time we want to intercept motion events is if we are in the 884 * drag mode. 885 */ 886 return mTouchState != TOUCH_STATE_REST; 887 } 888 889 private void onSecondaryPointerUp(MotionEvent ev) { 890 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 891 MotionEvent.ACTION_POINTER_INDEX_SHIFT; 892 final int pointerId = ev.getPointerId(pointerIndex); 893 if (pointerId == mActivePointerId) { 894 // This was our active pointer going up. Choose a new 895 // active pointer and adjust accordingly. 896 // TODO: Make this decision more intelligent. 897 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 898 mLastMotionX = ev.getX(newPointerIndex); 899 mLastMotionY = ev.getY(newPointerIndex); 900 mActivePointerId = ev.getPointerId(newPointerIndex); 901 if (mVelocityTracker != null) { 902 mVelocityTracker.clear(); 903 } 904 } 905 } 906 907 /** 908 * If one of our descendant views decides that it could be focused now, only 909 * pass that along if it's on the current screen. 910 * 911 * This happens when live folders requery, and if they're off screen, they 912 * end up calling requestFocus, which pulls it on screen. 913 */ 914 @Override 915 public void focusableViewAvailable(View focused) { 916 View current = getChildAt(mCurrentScreen); 917 View v = focused; 918 while (true) { 919 if (v == current) { 920 super.focusableViewAvailable(focused); 921 return; 922 } 923 if (v == this) { 924 return; 925 } 926 ViewParent parent = v.getParent(); 927 if (parent instanceof View) { 928 v = (View) v.getParent(); 929 } else { 930 return; 931 } 932 } 933 } 934 935 void enableChildrenCache(int fromScreen, int toScreen) { 936 if (fromScreen > toScreen) { 937 final int temp = fromScreen; 938 fromScreen = toScreen; 939 toScreen = temp; 940 } 941 942 final int screenCount = getChildCount(); 943 944 fromScreen = Math.max(fromScreen, 0); 945 toScreen = Math.min(toScreen, screenCount - 1); 946 947 for (int i = fromScreen; i <= toScreen; i++) { 948 final CellLayout layout = (CellLayout) getChildAt(i); 949 layout.setChildrenDrawnWithCacheEnabled(true); 950 layout.setChildrenDrawingCacheEnabled(true); 951 } 952 } 953 954 void clearChildrenCache() { 955 final int screenCount = getChildCount(); 956 for (int i = 0; i < screenCount; i++) { 957 final CellLayout layout = (CellLayout) getChildAt(i); 958 layout.setChildrenDrawnWithCacheEnabled(false); 959 } 960 } 961 962 @Override 963 public boolean onTouchEvent(MotionEvent ev) { 964 965 if (mLauncher.isWorkspaceLocked()) { 966 return false; // We don't want the events. Let them fall through to the all apps view. 967 } 968 if (mLauncher.isAllAppsVisible()) { 969 // Cancel any scrolling that is in progress. 970 if (!mScroller.isFinished()) { 971 mScroller.abortAnimation(); 972 } 973 snapToScreen(mCurrentScreen); 974 return false; // We don't want the events. Let them fall through to the all apps view. 975 } 976 977 if (mVelocityTracker == null) { 978 mVelocityTracker = VelocityTracker.obtain(); 979 } 980 mVelocityTracker.addMovement(ev); 981 982 final int action = ev.getAction(); 983 984 switch (action & MotionEvent.ACTION_MASK) { 985 case MotionEvent.ACTION_DOWN: 986 /* 987 * If being flinged and user touches, stop the fling. isFinished 988 * will be false if being flinged. 989 */ 990 if (!mScroller.isFinished()) { 991 mScroller.abortAnimation(); 992 } 993 994 // Remember where the motion event started 995 mLastMotionX = ev.getX(); 996 mActivePointerId = ev.getPointerId(0); 997 if (mTouchState == TOUCH_STATE_SCROLLING) { 998 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 999 } 1000 break; 1001 case MotionEvent.ACTION_MOVE: 1002 if (mTouchState == TOUCH_STATE_SCROLLING) { 1003 // Scroll to follow the motion event 1004 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 1005 final float x = ev.getX(pointerIndex); 1006 final float deltaX = mLastMotionX - x; 1007 mLastMotionX = x; 1008 1009 if (deltaX < 0) { 1010 if (mTouchX > 0) { 1011 mTouchX += Math.max(-mTouchX, deltaX); 1012 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1013 invalidate(); 1014 } 1015 } else if (deltaX > 0) { 1016 final float availableToScroll = getChildAt(getChildCount() - 1).getRight() - 1017 mTouchX - getWidth(); 1018 if (availableToScroll > 0) { 1019 mTouchX += Math.min(availableToScroll, deltaX); 1020 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1021 invalidate(); 1022 } 1023 } else { 1024 awakenScrollBars(); 1025 } 1026 } 1027 break; 1028 case MotionEvent.ACTION_UP: 1029 if (mTouchState == TOUCH_STATE_SCROLLING) { 1030 final VelocityTracker velocityTracker = mVelocityTracker; 1031 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1032 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); 1033 1034 final int screenWidth = getWidth(); 1035 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; 1036 final float scrolledPos = (float) mScrollX / screenWidth; 1037 1038 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 1039 // Fling hard enough to move left. 1040 // Don't fling across more than one screen at a time. 1041 final int bound = scrolledPos < whichScreen ? 1042 mCurrentScreen - 1 : mCurrentScreen; 1043 snapToScreen(Math.min(whichScreen, bound), velocityX, true); 1044 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { 1045 // Fling hard enough to move right 1046 // Don't fling across more than one screen at a time. 1047 final int bound = scrolledPos > whichScreen ? 1048 mCurrentScreen + 1 : mCurrentScreen; 1049 snapToScreen(Math.max(whichScreen, bound), velocityX, true); 1050 } else { 1051 snapToScreen(whichScreen, 0, true); 1052 } 1053 1054 if (mVelocityTracker != null) { 1055 mVelocityTracker.recycle(); 1056 mVelocityTracker = null; 1057 } 1058 } 1059 mTouchState = TOUCH_STATE_REST; 1060 mActivePointerId = INVALID_POINTER; 1061 break; 1062 case MotionEvent.ACTION_CANCEL: 1063 mTouchState = TOUCH_STATE_REST; 1064 mActivePointerId = INVALID_POINTER; 1065 break; 1066 case MotionEvent.ACTION_POINTER_UP: 1067 onSecondaryPointerUp(ev); 1068 break; 1069 } 1070 1071 return true; 1072 } 1073 1074 public boolean isSmall() { 1075 return mIsSmall; 1076 } 1077 1078 void shrinkToTop(boolean animated) { 1079 shrink(SHRINK_TO_TOP, animated); 1080 } 1081 1082 void shrinkToMiddle() { 1083 shrink(SHRINK_TO_MIDDLE, true); 1084 } 1085 1086 void shrinkToBottom() { 1087 shrinkToBottom(true); 1088 } 1089 1090 void shrinkToBottom(boolean animated) { 1091 if (mFirstLayout) { 1092 // (mFirstLayout == "first layout has not happened yet") 1093 // if we get a call to shrink() as part of our initialization (for example, if 1094 // Launcher is started in All Apps mode) then we need to wait for a layout call 1095 // to get our width so we can layout the mini-screen views correctly 1096 mWaitingToShrinkToBottom = true; 1097 } else { 1098 shrink(SHRINK_TO_BOTTOM, animated); 1099 } 1100 } 1101 1102 // we use this to shrink the workspace for the all apps view and the customize view 1103 private void shrink(int shrinkPosition, boolean animated) { 1104 mIsSmall = true; 1105 final Resources res = getResources(); 1106 final int screenWidth = getWidth(); 1107 final int screenHeight = getHeight(); 1108 final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth); 1109 final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight); 1110 final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing); 1111 1112 final int screenCount = getChildCount(); 1113 float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing; 1114 1115 float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); 1116 if (shrinkPosition == SHRINK_TO_BOTTOM) { 1117 newY = screenHeight - newY - scaledScreenHeight; 1118 } else if (shrinkPosition == SHRINK_TO_MIDDLE) { 1119 newY = screenHeight / 2 - scaledScreenHeight / 2; 1120 } 1121 1122 // We animate all the screens to the centered position in workspace 1123 // At the same time, the screens become greyed/dimmed 1124 1125 // newX is initialized to the left-most position of the centered screens 1126 float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2; 1127 Sequencer s = new Sequencer(); 1128 for (int i = 0; i < screenCount; i++) { 1129 CellLayout cl = (CellLayout) getChildAt(i); 1130 cl.setPivotX(0.0f); 1131 cl.setPivotY(0.0f); 1132 if (animated) { 1133 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 1134 s.playTogether( 1135 new PropertyAnimator(duration, cl, "x", newX), 1136 new PropertyAnimator(duration, cl, "y", newY), 1137 new PropertyAnimator(duration, cl, "scaleX", SHRINK_FACTOR), 1138 new PropertyAnimator(duration, cl, "scaleY", SHRINK_FACTOR), 1139 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 1.0f)); 1140 } else { 1141 cl.setX((int)newX); 1142 cl.setY((int)newY); 1143 cl.setScaleX(SHRINK_FACTOR); 1144 cl.setScaleY(SHRINK_FACTOR); 1145 cl.setDimmedBitmapAlpha(1.0f); 1146 } 1147 // increment newX for the next screen 1148 newX += scaledScreenWidth + scaledSpacing; 1149 cl.setOnInterceptTouchListener(this); 1150 } 1151 setChildrenDrawnWithCacheEnabled(true); 1152 if (animated) s.start(); 1153 } 1154 1155 // We call this when we trigger an unshrink by clicking on the CellLayout cl 1156 private void unshrink(CellLayout clThatWasClicked) { 1157 int newCurrentScreen = mCurrentScreen; 1158 final int screenCount = getChildCount(); 1159 for (int i = 0; i < screenCount; i++) { 1160 if (getChildAt(i) == clThatWasClicked) { 1161 newCurrentScreen = i; 1162 } 1163 } 1164 unshrink(newCurrentScreen); 1165 } 1166 1167 private void unshrink(int newCurrentScreen) { 1168 if (mIsSmall) { 1169 int delta = (newCurrentScreen - mCurrentScreen)*getWidth(); 1170 1171 final int screenCount = getChildCount(); 1172 for (int i = 0; i < screenCount; i++) { 1173 CellLayout cl = (CellLayout) getChildAt(i); 1174 cl.setX(cl.getX() + delta); 1175 } 1176 mScrollX = newCurrentScreen * getWidth(); 1177 1178 unshrink(); 1179 setCurrentScreen(newCurrentScreen); 1180 } 1181 } 1182 1183 void unshrink() { 1184 unshrink(true); 1185 } 1186 1187 void unshrink(boolean animated) { 1188 if (mIsSmall) { 1189 Sequencer s = new Sequencer(); 1190 final int screenCount = getChildCount(); 1191 1192 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 1193 for (int i = 0; i < screenCount; i++) { 1194 final CellLayout cl = (CellLayout)getChildAt(i); 1195 cl.setPivotX(0.0f); 1196 cl.setPivotY(0.0f); 1197 if (animated) { 1198 s.playTogether( 1199 new PropertyAnimator(duration, cl, "translationX", 0.0f), 1200 new PropertyAnimator(duration, cl, "translationY", 0.0f), 1201 new PropertyAnimator(duration, cl, "scaleX", 1.0f), 1202 new PropertyAnimator(duration, cl, "scaleY", 1.0f), 1203 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f)); 1204 } else { 1205 cl.setTranslationX(0.0f); 1206 cl.setTranslationY(0.0f); 1207 cl.setScaleX(1.0f); 1208 cl.setScaleY(1.0f); 1209 cl.setDimmedBitmapAlpha(0.0f); 1210 } 1211 } 1212 s.addListener(mUnshrinkAnimationListener); 1213 s.start(); 1214 } 1215 } 1216 1217 void snapToScreen(int whichScreen) { 1218 snapToScreen(whichScreen, 0, false); 1219 } 1220 1221 private void snapToScreen(int whichScreen, int velocity, boolean settle) { 1222 // if (!mScroller.isFinished()) return; 1223 1224 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 1225 1226 enableChildrenCache(mCurrentScreen, whichScreen); 1227 1228 mNextScreen = whichScreen; 1229 1230 if (mPreviousIndicator != null) { 1231 mPreviousIndicator.setLevel(mNextScreen); 1232 mNextIndicator.setLevel(mNextScreen); 1233 } 1234 1235 View focusedChild = getFocusedChild(); 1236 if (focusedChild != null && whichScreen != mCurrentScreen && 1237 focusedChild == getChildAt(mCurrentScreen)) { 1238 focusedChild.clearFocus(); 1239 } 1240 1241 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); 1242 final int newX = whichScreen * getWidth(); 1243 final int delta = newX - mScrollX; 1244 int duration = (screenDelta + 1) * 100; 1245 1246 if (!mScroller.isFinished()) { 1247 mScroller.abortAnimation(); 1248 } 1249 1250 if (settle) { 1251 mScrollInterpolator.setDistance(screenDelta); 1252 } else { 1253 mScrollInterpolator.disableSettle(); 1254 } 1255 1256 velocity = Math.abs(velocity); 1257 if (velocity > 0) { 1258 duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) 1259 * FLING_VELOCITY_INFLUENCE; 1260 } else { 1261 duration += 100; 1262 } 1263 1264 awakenScrollBars(duration); 1265 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 1266 invalidate(); 1267 } 1268 1269 void startDrag(CellLayout.CellInfo cellInfo) { 1270 View child = cellInfo.cell; 1271 1272 // Make sure the drag was started by a long press as opposed to a long click. 1273 if (!child.isInTouchMode()) { 1274 return; 1275 } 1276 1277 mDragInfo = cellInfo; 1278 mDragInfo.screen = mCurrentScreen; 1279 1280 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); 1281 1282 current.onDragChild(child); 1283 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 1284 invalidate(); 1285 } 1286 1287 @Override 1288 protected Parcelable onSaveInstanceState() { 1289 final SavedState state = new SavedState(super.onSaveInstanceState()); 1290 state.currentScreen = mCurrentScreen; 1291 return state; 1292 } 1293 1294 @Override 1295 protected void onRestoreInstanceState(Parcelable state) { 1296 SavedState savedState = (SavedState) state; 1297 super.onRestoreInstanceState(savedState.getSuperState()); 1298 if (savedState.currentScreen != -1) { 1299 setCurrentScreen(savedState.currentScreen, false); 1300 Launcher.setScreen(mCurrentScreen); 1301 } 1302 } 1303 1304 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { 1305 addApplicationShortcut(info, cellInfo, false); 1306 } 1307 1308 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, 1309 boolean insertAtFirst) { 1310 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); 1311 final int[] result = new int[2]; 1312 1313 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); 1314 onDropExternal(result[0], result[1], info, layout, insertAtFirst); 1315 } 1316 1317 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1318 DragView dragView, Object dragInfo) { 1319 CellLayout cellLayout = getCurrentDropLayout(); 1320 int originX = x - xOffset; 1321 int originY = y - yOffset; 1322 if (mIsSmall) { 1323 // find out which target layout is over 1324 final float[] localXY = mTempDragCoordinates; 1325 localXY[0] = originX; 1326 localXY[1] = originY; 1327 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1328 // we need to subtract left/top here because DragController already adds 1329 // dragRegionLeft/Top to xOffset and yOffset 1330 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1331 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1332 cellLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1333 if (cellLayout == null) { 1334 // cancel the drag if we're not over a mini-screen at time of drop 1335 // TODO: maybe add a nice fade here? 1336 return; 1337 } 1338 // localXY will be transformed into the local screen's coordinate space; save that info 1339 originX = (int)localXY[0]; 1340 originY = (int)localXY[1]; 1341 } 1342 if (source != this) { 1343 onDropExternal(originX, originY, dragInfo, cellLayout); 1344 } else { 1345 // Move internally 1346 if (mDragInfo != null) { 1347 final View cell = mDragInfo.cell; 1348 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1349 if (index != mDragInfo.screen) { 1350 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1351 originalCellLayout.removeView(cell); 1352 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 1353 mDragInfo.spanX, mDragInfo.spanY); 1354 } 1355 1356 mTargetCell = estimateDropCell(originX, originY, 1357 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 1358 mTargetCell); 1359 cellLayout.onDropChild(cell); 1360 1361 // update the item's position after drop 1362 final ItemInfo info = (ItemInfo) cell.getTag(); 1363 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell 1364 .getLayoutParams(); 1365 lp.cellX = mTargetCell[0]; 1366 lp.cellY = mTargetCell[1]; 1367 1368 LauncherModel.moveItemInDatabase(mLauncher, info, 1369 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 1370 lp.cellX, lp.cellY); 1371 } 1372 } 1373 } 1374 1375 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1376 int yOffset, DragView dragView, Object dragInfo) { 1377 } 1378 1379 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 1380 DragView dragView, Object dragInfo) { 1381 1382 // We may need to delegate the drag to a child view. If a 1x1 item 1383 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1384 // then drag events should be handled by that child. 1385 1386 ItemInfo item = (ItemInfo)dragInfo; 1387 CellLayout currentLayout = getCurrentDropLayout(); 1388 1389 int dragPointX, dragPointY; 1390 if (item.spanX == 1 && item.spanY == 1) { 1391 // For a 1x1, calculate the drop cell exactly as in onDragOver 1392 dragPointX = x - xOffset; 1393 dragPointY = y - yOffset; 1394 } else { 1395 // Otherwise, use the exact drag coordinates 1396 dragPointX = x; 1397 dragPointY = y; 1398 } 1399 1400 // If we are dragging over a cell that contains a DropTarget that will 1401 // accept the drop, delegate to that DropTarget. 1402 final int[] cellXY = mTempCell; 1403 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1404 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1405 if (child instanceof DropTarget) { 1406 DropTarget target = (DropTarget)child; 1407 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1408 return target; 1409 } 1410 } 1411 return null; 1412 } 1413 1414 // xy = upper left corner of item being dragged 1415 // bottomRightXy = lower right corner of item being dragged 1416 // This method will see which mini-screen is most overlapped by the item being dragged, and 1417 // return it. It will also transform the parameters xy and bottomRightXy into the local 1418 // coordinate space of the returned screen 1419 private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) { 1420 float x = xy[0]; 1421 float y = xy[1]; 1422 float right = bottomRightXy[0]; 1423 float bottom = bottomRightXy[1]; 1424 1425 float bestX = 0; 1426 float bestY = 0; 1427 float bestRight = 0; 1428 float bestBottom = 0; 1429 1430 Matrix inverseMatrix = new Matrix(); 1431 1432 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 1433 // with the item being dragged. 1434 final int screenCount = getChildCount(); 1435 CellLayout bestMatchingScreen = null; 1436 float bestOverlapSoFar = 0; 1437 for (int i = 0; i < screenCount; i++) { 1438 CellLayout cl = (CellLayout)getChildAt(i); 1439 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1440 float left = cl.getLeft(); 1441 float top = cl.getTop(); 1442 xy[0] = x + mScrollX - left; 1443 xy[1] = y + mScrollY - top; 1444 cl.getMatrix().invert(inverseMatrix); 1445 1446 bottomRightXy[0] = right + mScrollX - left; 1447 bottomRightXy[1] = bottom + mScrollY - top; 1448 1449 inverseMatrix.mapPoints(xy); 1450 inverseMatrix.mapPoints(bottomRightXy); 1451 1452 float dragRegionX = xy[0]; 1453 float dragRegionY = xy[1]; 1454 float dragRegionRight = bottomRightXy[0]; 1455 float dragRegionBottom = bottomRightXy[1]; 1456 1457 // Find the overlapping region 1458 float overlapLeft = Math.max(0f, dragRegionX); 1459 float overlapTop = Math.max(0f, dragRegionY); 1460 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 1461 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 1462 1463 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 1464 overlapTop >= 0 && overlapBottom <= cl.getHeight()) { 1465 // Calculate the size of the overlapping region 1466 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 1467 if (overlap > bestOverlapSoFar) { 1468 bestOverlapSoFar = overlap; 1469 bestMatchingScreen = cl; 1470 bestX = xy[0]; 1471 bestY = xy[1]; 1472 bestRight = bottomRightXy[0]; 1473 bestBottom = bottomRightXy[1]; 1474 } 1475 } 1476 } 1477 if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { 1478 if (mDragTargetLayout != null) { 1479 mDragTargetLayout.onDragComplete(); 1480 } 1481 mDragTargetLayout = bestMatchingScreen; 1482 } 1483 xy[0] = bestX; 1484 xy[1] = bestY; 1485 bottomRightXy[0] = bestRight; 1486 bottomRightXy[1] = bestBottom; 1487 return bestMatchingScreen; 1488 } 1489 1490 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1491 DragView dragView, Object dragInfo) { 1492 1493 final ItemInfo item = (ItemInfo)dragInfo; 1494 CellLayout currentLayout = getCurrentDropLayout(); 1495 1496 if (dragInfo instanceof LauncherAppWidgetInfo) { 1497 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1498 1499 if (widgetInfo.spanX == -1) { 1500 // Calculate the grid spans needed to fit this widget 1501 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 1502 item.spanX = spans[0]; 1503 item.spanY = spans[1]; 1504 } 1505 } 1506 int originX = x - xOffset; 1507 int originY = y - yOffset; 1508 if (mIsSmall) { 1509 // find out which mini screen the dragged item is over 1510 final float[] localXY = mTempDragCoordinates; 1511 localXY[0] = originX; 1512 localXY[1] = originY; 1513 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1514 1515 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1516 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1517 currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1518 if (currentLayout != null) { 1519 currentLayout.setHover(true); 1520 } 1521 1522 originX = (int)localXY[0]; 1523 originY = (int)localXY[1]; 1524 } 1525 1526 if (source != this) { 1527 // This is a hack to fix the point used to determine which cell an icon from the all 1528 // apps screen is over 1529 if (item != null && item.spanX == 1 && currentLayout != null) { 1530 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 1531 1532 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1533 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 1534 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 1535 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 1536 } 1537 } 1538 } 1539 if (currentLayout != mDragTargetLayout) { 1540 if (mDragTargetLayout != null) { 1541 mDragTargetLayout.onDragComplete(); 1542 } 1543 mDragTargetLayout = currentLayout; 1544 } 1545 1546 // only visualize the drop locations for moving icons within the home screen on tablet 1547 // on phone, we also visualize icons dragged in from All Apps 1548 if ((!LauncherApplication.isScreenXLarge() || source == this) 1549 && mDragTargetLayout != null) { 1550 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1551 mDragTargetLayout.visualizeDropLocation( 1552 child, originX, originY, item.spanX, item.spanY); 1553 } 1554 } 1555 1556 public void onDragExit(DragSource source, int x, int y, int xOffset, 1557 int yOffset, DragView dragView, Object dragInfo) { 1558 if (mDragTargetLayout != null) { 1559 mDragTargetLayout.onDragComplete(); 1560 mDragTargetLayout = null; 1561 } 1562 } 1563 1564 private void onDropExternal(int x, int y, Object dragInfo, 1565 CellLayout cellLayout) { 1566 onDropExternal(x, y, dragInfo, cellLayout, false); 1567 } 1568 1569 private void onDropExternal(int x, int y, Object dragInfo, 1570 CellLayout cellLayout, boolean insertAtFirst) { 1571 // Drag from somewhere else 1572 ItemInfo info = (ItemInfo) dragInfo; 1573 1574 View view = null; 1575 1576 switch (info.itemType) { 1577 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1578 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1579 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1580 // Came from all apps -- make a copy 1581 info = new ShortcutInfo((ApplicationInfo) info); 1582 } 1583 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1584 (ShortcutInfo) info); 1585 break; 1586 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1587 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1588 (ViewGroup) getChildAt(mCurrentScreen), 1589 ((UserFolderInfo) info)); 1590 break; 1591 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1592 cellLayout.setTagToCellInfoForPoint(x, y); 1593 int[] position = new int[2]; 1594 position[0] = x; 1595 position[1] = y; 1596 mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, 1597 cellLayout.getTag(), position); 1598 break; 1599 default: 1600 throw new IllegalStateException("Unknown item type: " 1601 + info.itemType); 1602 } 1603 1604 // If the view is null, it has already been added. 1605 if (view == null) { 1606 cellLayout.onDragComplete(); 1607 } else { 1608 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); 1609 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1610 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1611 cellLayout.onDropChild(view); 1612 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1613 1614 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1615 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, 1616 lp.cellX, lp.cellY); 1617 } 1618 } 1619 1620 /** 1621 * Return the current {@link CellLayout}, correctly picking the destination 1622 * screen while a scroll is in progress. 1623 */ 1624 private CellLayout getCurrentDropLayout() { 1625 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1626 return (CellLayout) getChildAt(index); 1627 } 1628 1629 /** 1630 * {@inheritDoc} 1631 */ 1632 public boolean acceptDrop(DragSource source, int x, int y, 1633 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1634 final CellLayout layout = getCurrentDropLayout(); 1635 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1636 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1637 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1638 1639 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1640 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1641 1642 if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { 1643 return true; 1644 } else { 1645 Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1646 return false; 1647 } 1648 } 1649 1650 /** 1651 * {@inheritDoc} 1652 */ 1653 public Rect estimateDropLocation(DragSource source, int x, int y, 1654 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { 1655 final CellLayout layout = getCurrentDropLayout(); 1656 1657 final CellLayout.CellInfo cellInfo = mDragInfo; 1658 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1659 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1660 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1661 1662 final Rect location = recycle != null ? recycle : new Rect(); 1663 1664 // Find drop cell and convert into rectangle 1665 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, 1666 spanY, ignoreView, layout, mTempCell); 1667 1668 if (dropCell == null) { 1669 return null; 1670 } 1671 1672 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); 1673 location.left = mTempEstimate[0]; 1674 location.top = mTempEstimate[1]; 1675 1676 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); 1677 location.right = mTempEstimate[0]; 1678 location.bottom = mTempEstimate[1]; 1679 1680 return location; 1681 } 1682 1683 /** 1684 * Calculate the nearest cell where the given object would be dropped. 1685 */ 1686 private int[] estimateDropCell(int pixelX, int pixelY, 1687 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1688 1689 final int[] cellXY = mTempCell; 1690 layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY); 1691 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1692 1693 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1694 // Find the best target drop location 1695 return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); 1696 } 1697 1698 /** 1699 * Estimate the size that a child with the given dimensions will take in the current screen. 1700 */ 1701 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1702 ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result); 1703 } 1704 1705 void setLauncher(Launcher launcher) { 1706 mLauncher = launcher; 1707 } 1708 1709 public void setDragController(DragController dragController) { 1710 mDragController = dragController; 1711 } 1712 1713 public void onDropCompleted(View target, boolean success) { 1714 if (success) { 1715 if (target != this && mDragInfo != null) { 1716 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1717 cellLayout.removeView(mDragInfo.cell); 1718 if (mDragInfo.cell instanceof DropTarget) { 1719 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1720 } 1721 // final Object tag = mDragInfo.cell.getTag(); 1722 } 1723 } else { 1724 if (mDragInfo != null) { 1725 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1726 cellLayout.onDropAborted(mDragInfo.cell); 1727 } 1728 } 1729 1730 mDragInfo = null; 1731 } 1732 1733 public void scrollLeft() { 1734 if (!mIsSmall) { 1735 if (mScroller.isFinished()) { 1736 if (mCurrentScreen > 0) 1737 snapToScreen(mCurrentScreen - 1); 1738 } else { 1739 if (mNextScreen > 0) 1740 snapToScreen(mNextScreen - 1); 1741 } 1742 } 1743 } 1744 1745 public void scrollRight() { 1746 if (!mIsSmall) { 1747 if (mScroller.isFinished()) { 1748 if (mCurrentScreen < getChildCount() - 1) 1749 snapToScreen(mCurrentScreen + 1); 1750 } else { 1751 if (mNextScreen < getChildCount() - 1) 1752 snapToScreen(mNextScreen + 1); 1753 } 1754 } 1755 } 1756 1757 public int getScreenForView(View v) { 1758 int result = -1; 1759 if (v != null) { 1760 ViewParent vp = v.getParent(); 1761 final int screenCount = getChildCount(); 1762 for (int i = 0; i < screenCount; i++) { 1763 if (vp == getChildAt(i)) { 1764 return i; 1765 } 1766 } 1767 } 1768 return result; 1769 } 1770 1771 public Folder getFolderForTag(Object tag) { 1772 final int screenCount = getChildCount(); 1773 for (int screen = 0; screen < screenCount; screen++) { 1774 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1775 int count = currentScreen.getChildCount(); 1776 for (int i = 0; i < count; i++) { 1777 View child = currentScreen.getChildAt(i); 1778 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1779 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1780 Folder f = (Folder) child; 1781 if (f.getInfo() == tag && f.getInfo().opened) { 1782 return f; 1783 } 1784 } 1785 } 1786 } 1787 return null; 1788 } 1789 1790 public View getViewForTag(Object tag) { 1791 int screenCount = getChildCount(); 1792 for (int screen = 0; screen < screenCount; screen++) { 1793 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1794 int count = currentScreen.getChildCount(); 1795 for (int i = 0; i < count; i++) { 1796 View child = currentScreen.getChildAt(i); 1797 if (child.getTag() == tag) { 1798 return child; 1799 } 1800 } 1801 } 1802 return null; 1803 } 1804 1805 /** 1806 * @return True is long presses are still allowed for the current touch 1807 */ 1808 public boolean allowLongPress() { 1809 return mAllowLongPress; 1810 } 1811 1812 /** 1813 * Set true to allow long-press events to be triggered, usually checked by 1814 * {@link Launcher} to accept or block dpad-initiated long-presses. 1815 */ 1816 public void setAllowLongPress(boolean allowLongPress) { 1817 mAllowLongPress = allowLongPress; 1818 } 1819 1820 void removeItems(final ArrayList<ApplicationInfo> apps) { 1821 final int screenCount = getChildCount(); 1822 final PackageManager manager = getContext().getPackageManager(); 1823 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1824 1825 final HashSet<String> packageNames = new HashSet<String>(); 1826 final int appCount = apps.size(); 1827 for (int i = 0; i < appCount; i++) { 1828 packageNames.add(apps.get(i).componentName.getPackageName()); 1829 } 1830 1831 for (int i = 0; i < screenCount; i++) { 1832 final CellLayout layout = (CellLayout) getChildAt(i); 1833 1834 // Avoid ANRs by treating each screen separately 1835 post(new Runnable() { 1836 public void run() { 1837 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1838 childrenToRemove.clear(); 1839 1840 int childCount = layout.getChildCount(); 1841 for (int j = 0; j < childCount; j++) { 1842 final View view = layout.getChildAt(j); 1843 Object tag = view.getTag(); 1844 1845 if (tag instanceof ShortcutInfo) { 1846 final ShortcutInfo info = (ShortcutInfo) tag; 1847 final Intent intent = info.intent; 1848 final ComponentName name = intent.getComponent(); 1849 1850 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1851 for (String packageName: packageNames) { 1852 if (packageName.equals(name.getPackageName())) { 1853 // TODO: This should probably be done on a worker thread 1854 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1855 childrenToRemove.add(view); 1856 } 1857 } 1858 } 1859 } else if (tag instanceof UserFolderInfo) { 1860 final UserFolderInfo info = (UserFolderInfo) tag; 1861 final ArrayList<ShortcutInfo> contents = info.contents; 1862 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1863 final int contentsCount = contents.size(); 1864 boolean removedFromFolder = false; 1865 1866 for (int k = 0; k < contentsCount; k++) { 1867 final ShortcutInfo appInfo = contents.get(k); 1868 final Intent intent = appInfo.intent; 1869 final ComponentName name = intent.getComponent(); 1870 1871 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1872 for (String packageName: packageNames) { 1873 if (packageName.equals(name.getPackageName())) { 1874 toRemove.add(appInfo); 1875 // TODO: This should probably be done on a worker thread 1876 LauncherModel.deleteItemFromDatabase( 1877 mLauncher, appInfo); 1878 removedFromFolder = true; 1879 } 1880 } 1881 } 1882 } 1883 1884 contents.removeAll(toRemove); 1885 if (removedFromFolder) { 1886 final Folder folder = getOpenFolder(); 1887 if (folder != null) 1888 folder.notifyDataSetChanged(); 1889 } 1890 } else if (tag instanceof LiveFolderInfo) { 1891 final LiveFolderInfo info = (LiveFolderInfo) tag; 1892 final Uri uri = info.uri; 1893 final ProviderInfo providerInfo = manager.resolveContentProvider( 1894 uri.getAuthority(), 0); 1895 1896 if (providerInfo != null) { 1897 for (String packageName: packageNames) { 1898 if (packageName.equals(providerInfo.packageName)) { 1899 // TODO: This should probably be done on a worker thread 1900 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1901 childrenToRemove.add(view); 1902 } 1903 } 1904 } 1905 } else if (tag instanceof LauncherAppWidgetInfo) { 1906 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1907 final AppWidgetProviderInfo provider = 1908 widgets.getAppWidgetInfo(info.appWidgetId); 1909 if (provider != null) { 1910 for (String packageName: packageNames) { 1911 if (packageName.equals(provider.provider.getPackageName())) { 1912 // TODO: This should probably be done on a worker thread 1913 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1914 childrenToRemove.add(view); 1915 } 1916 } 1917 } 1918 } 1919 } 1920 1921 childCount = childrenToRemove.size(); 1922 for (int j = 0; j < childCount; j++) { 1923 View child = childrenToRemove.get(j); 1924 layout.removeViewInLayout(child); 1925 if (child instanceof DropTarget) { 1926 mDragController.removeDropTarget((DropTarget)child); 1927 } 1928 } 1929 1930 if (childCount > 0) { 1931 layout.requestLayout(); 1932 layout.invalidate(); 1933 } 1934 } 1935 }); 1936 } 1937 } 1938 1939 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1940 final PackageManager pm = mLauncher.getPackageManager(); 1941 1942 final int screenCount = getChildCount(); 1943 for (int i = 0; i < screenCount; i++) { 1944 final CellLayout layout = (CellLayout) getChildAt(i); 1945 int childCount = layout.getChildCount(); 1946 for (int j = 0; j < childCount; j++) { 1947 final View view = layout.getChildAt(j); 1948 Object tag = view.getTag(); 1949 if (tag instanceof ShortcutInfo) { 1950 ShortcutInfo info = (ShortcutInfo)tag; 1951 // We need to check for ACTION_MAIN otherwise getComponent() might 1952 // return null for some shortcuts (for instance, for shortcuts to 1953 // web pages.) 1954 final Intent intent = info.intent; 1955 final ComponentName name = intent.getComponent(); 1956 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1957 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1958 final int appCount = apps.size(); 1959 for (int k = 0; k < appCount; k++) { 1960 ApplicationInfo app = apps.get(k); 1961 if (app.componentName.equals(name)) { 1962 info.setIcon(mIconCache.getIcon(info.intent)); 1963 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1964 new FastBitmapDrawable(info.getIcon(mIconCache)), 1965 null, null); 1966 } 1967 } 1968 } 1969 } 1970 } 1971 } 1972 } 1973 1974 void moveToDefaultScreen(boolean animate) { 1975 if (animate) { 1976 if (mIsSmall) { 1977 unshrink(mDefaultScreen); 1978 } else { 1979 snapToScreen(mDefaultScreen); 1980 } 1981 } else { 1982 setCurrentScreen(mDefaultScreen); 1983 } 1984 getChildAt(mDefaultScreen).requestFocus(); 1985 } 1986 1987 void setIndicators(Drawable previous, Drawable next) { 1988 mPreviousIndicator = previous; 1989 mNextIndicator = next; 1990 previous.setLevel(mCurrentScreen); 1991 next.setLevel(mCurrentScreen); 1992 } 1993 1994 public static class SavedState extends BaseSavedState { 1995 int currentScreen = -1; 1996 1997 SavedState(Parcelable superState) { 1998 super(superState); 1999 } 2000 2001 private SavedState(Parcel in) { 2002 super(in); 2003 currentScreen = in.readInt(); 2004 } 2005 2006 @Override 2007 public void writeToParcel(Parcel out, int flags) { 2008 super.writeToParcel(out, flags); 2009 out.writeInt(currentScreen); 2010 } 2011 2012 public static final Parcelable.Creator<SavedState> CREATOR = 2013 new Parcelable.Creator<SavedState>() { 2014 public SavedState createFromParcel(Parcel in) { 2015 return new SavedState(in); 2016 } 2017 2018 public SavedState[] newArray(int size) { 2019 return new SavedState[size]; 2020 } 2021 }; 2022 } 2023} 2024