Workspace.java revision df7c3a7853abd4dec460247370fe0b17b50866ac
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; 20 21import android.animation.Animatable; 22import android.animation.Animatable.AnimatableListener; 23import android.animation.PropertyAnimator; 24import android.animation.PropertyValuesHolder; 25import android.animation.Sequencer; 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 for (int i = 0; i < screenCount; i++) { 1128 CellLayout cl = (CellLayout) getChildAt(i); 1129 cl.setPivotX(0.0f); 1130 cl.setPivotY(0.0f); 1131 if (animated) { 1132 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 1133 new PropertyAnimator(duration, cl, 1134 new PropertyValuesHolder("x", newX), 1135 new PropertyValuesHolder("y", newY), 1136 new PropertyValuesHolder("scaleX", SHRINK_FACTOR), 1137 new PropertyValuesHolder("scaleY", SHRINK_FACTOR), 1138 new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start(); 1139 } else { 1140 cl.setX((int)newX); 1141 cl.setY((int)newY); 1142 cl.setScaleX(SHRINK_FACTOR); 1143 cl.setScaleY(SHRINK_FACTOR); 1144 cl.setDimmedBitmapAlpha(1.0f); 1145 } 1146 // increment newX for the next screen 1147 newX += scaledScreenWidth + scaledSpacing; 1148 cl.setOnInterceptTouchListener(this); 1149 } 1150 setChildrenDrawnWithCacheEnabled(true); 1151 } 1152 1153 // We call this when we trigger an unshrink by clicking on the CellLayout cl 1154 private void unshrink(CellLayout clThatWasClicked) { 1155 int newCurrentScreen = mCurrentScreen; 1156 final int screenCount = getChildCount(); 1157 for (int i = 0; i < screenCount; i++) { 1158 if (getChildAt(i) == clThatWasClicked) { 1159 newCurrentScreen = i; 1160 } 1161 } 1162 unshrink(newCurrentScreen); 1163 } 1164 1165 private void unshrink(int newCurrentScreen) { 1166 if (mIsSmall) { 1167 int delta = (newCurrentScreen - mCurrentScreen)*getWidth(); 1168 1169 final int screenCount = getChildCount(); 1170 for (int i = 0; i < screenCount; i++) { 1171 CellLayout cl = (CellLayout) getChildAt(i); 1172 cl.setX(cl.getX() + delta); 1173 } 1174 mScrollX = newCurrentScreen * getWidth(); 1175 1176 unshrink(); 1177 setCurrentScreen(newCurrentScreen); 1178 } 1179 } 1180 1181 void unshrink() { 1182 unshrink(true); 1183 } 1184 1185 void unshrink(boolean animated) { 1186 if (mIsSmall) { 1187 Sequencer s = new Sequencer(); 1188 final int screenCount = getChildCount(); 1189 1190 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 1191 for (int i = 0; i < screenCount; i++) { 1192 final CellLayout cl = (CellLayout)getChildAt(i); 1193 cl.setPivotX(0.0f); 1194 cl.setPivotY(0.0f); 1195 if (animated) { 1196 s.playTogether( 1197 new PropertyAnimator(duration, cl, "translationX", 0.0f), 1198 new PropertyAnimator(duration, cl, "translationY", 0.0f), 1199 new PropertyAnimator(duration, cl, "scaleX", 1.0f), 1200 new PropertyAnimator(duration, cl, "scaleY", 1.0f), 1201 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f)); 1202 } else { 1203 cl.setTranslationX(0.0f); 1204 cl.setTranslationY(0.0f); 1205 cl.setScaleX(1.0f); 1206 cl.setScaleY(1.0f); 1207 cl.setDimmedBitmapAlpha(0.0f); 1208 } 1209 } 1210 s.addListener(mUnshrinkAnimationListener); 1211 s.start(); 1212 } 1213 } 1214 1215 void snapToScreen(int whichScreen) { 1216 snapToScreen(whichScreen, 0, false); 1217 } 1218 1219 private void snapToScreen(int whichScreen, int velocity, boolean settle) { 1220 // if (!mScroller.isFinished()) return; 1221 1222 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 1223 1224 enableChildrenCache(mCurrentScreen, whichScreen); 1225 1226 mNextScreen = whichScreen; 1227 1228 if (mPreviousIndicator != null) { 1229 mPreviousIndicator.setLevel(mNextScreen); 1230 mNextIndicator.setLevel(mNextScreen); 1231 } 1232 1233 View focusedChild = getFocusedChild(); 1234 if (focusedChild != null && whichScreen != mCurrentScreen && 1235 focusedChild == getChildAt(mCurrentScreen)) { 1236 focusedChild.clearFocus(); 1237 } 1238 1239 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); 1240 final int newX = whichScreen * getWidth(); 1241 final int delta = newX - mScrollX; 1242 int duration = (screenDelta + 1) * 100; 1243 1244 if (!mScroller.isFinished()) { 1245 mScroller.abortAnimation(); 1246 } 1247 1248 if (settle) { 1249 mScrollInterpolator.setDistance(screenDelta); 1250 } else { 1251 mScrollInterpolator.disableSettle(); 1252 } 1253 1254 velocity = Math.abs(velocity); 1255 if (velocity > 0) { 1256 duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) 1257 * FLING_VELOCITY_INFLUENCE; 1258 } else { 1259 duration += 100; 1260 } 1261 1262 awakenScrollBars(duration); 1263 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 1264 invalidate(); 1265 } 1266 1267 void startDrag(CellLayout.CellInfo cellInfo) { 1268 View child = cellInfo.cell; 1269 1270 // Make sure the drag was started by a long press as opposed to a long click. 1271 if (!child.isInTouchMode()) { 1272 return; 1273 } 1274 1275 mDragInfo = cellInfo; 1276 mDragInfo.screen = mCurrentScreen; 1277 1278 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); 1279 1280 current.onDragChild(child); 1281 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 1282 invalidate(); 1283 } 1284 1285 @Override 1286 protected Parcelable onSaveInstanceState() { 1287 final SavedState state = new SavedState(super.onSaveInstanceState()); 1288 state.currentScreen = mCurrentScreen; 1289 return state; 1290 } 1291 1292 @Override 1293 protected void onRestoreInstanceState(Parcelable state) { 1294 SavedState savedState = (SavedState) state; 1295 super.onRestoreInstanceState(savedState.getSuperState()); 1296 if (savedState.currentScreen != -1) { 1297 setCurrentScreen(savedState.currentScreen, false); 1298 Launcher.setScreen(mCurrentScreen); 1299 } 1300 } 1301 1302 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { 1303 addApplicationShortcut(info, cellInfo, false); 1304 } 1305 1306 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, 1307 boolean insertAtFirst) { 1308 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); 1309 final int[] result = new int[2]; 1310 1311 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); 1312 onDropExternal(result[0], result[1], info, layout, insertAtFirst); 1313 } 1314 1315 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1316 DragView dragView, Object dragInfo) { 1317 CellLayout cellLayout = getCurrentDropLayout(); 1318 int originX = x - xOffset; 1319 int originY = y - yOffset; 1320 if (mIsSmall) { 1321 // find out which target layout is over 1322 final float[] localXY = mTempDragCoordinates; 1323 localXY[0] = originX; 1324 localXY[1] = originY; 1325 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1326 // we need to subtract left/top here because DragController already adds 1327 // dragRegionLeft/Top to xOffset and yOffset 1328 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1329 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1330 cellLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1331 if (cellLayout == null) { 1332 // cancel the drag if we're not over a mini-screen at time of drop 1333 // TODO: maybe add a nice fade here? 1334 return; 1335 } 1336 // localXY will be transformed into the local screen's coordinate space; save that info 1337 originX = (int)localXY[0]; 1338 originY = (int)localXY[1]; 1339 } 1340 if (source != this) { 1341 onDropExternal(originX, originY, dragInfo, cellLayout); 1342 } else { 1343 // Move internally 1344 if (mDragInfo != null) { 1345 final View cell = mDragInfo.cell; 1346 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1347 if (index != mDragInfo.screen) { 1348 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1349 originalCellLayout.removeView(cell); 1350 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 1351 mDragInfo.spanX, mDragInfo.spanY); 1352 } 1353 1354 mTargetCell = estimateDropCell(originX, originY, 1355 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 1356 mTargetCell); 1357 cellLayout.onDropChild(cell); 1358 1359 // update the item's position after drop 1360 final ItemInfo info = (ItemInfo) cell.getTag(); 1361 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell 1362 .getLayoutParams(); 1363 lp.cellX = mTargetCell[0]; 1364 lp.cellY = mTargetCell[1]; 1365 1366 LauncherModel.moveItemInDatabase(mLauncher, info, 1367 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 1368 lp.cellX, lp.cellY); 1369 } 1370 } 1371 } 1372 1373 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1374 int yOffset, DragView dragView, Object dragInfo) { 1375 } 1376 1377 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 1378 DragView dragView, Object dragInfo) { 1379 1380 // We may need to delegate the drag to a child view. If a 1x1 item 1381 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1382 // then drag events should be handled by that child. 1383 1384 ItemInfo item = (ItemInfo)dragInfo; 1385 CellLayout currentLayout = getCurrentDropLayout(); 1386 1387 int dragPointX, dragPointY; 1388 if (item.spanX == 1 && item.spanY == 1) { 1389 // For a 1x1, calculate the drop cell exactly as in onDragOver 1390 dragPointX = x - xOffset; 1391 dragPointY = y - yOffset; 1392 } else { 1393 // Otherwise, use the exact drag coordinates 1394 dragPointX = x; 1395 dragPointY = y; 1396 } 1397 1398 // If we are dragging over a cell that contains a DropTarget that will 1399 // accept the drop, delegate to that DropTarget. 1400 final int[] cellXY = mTempCell; 1401 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1402 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1403 if (child instanceof DropTarget) { 1404 DropTarget target = (DropTarget)child; 1405 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1406 return target; 1407 } 1408 } 1409 return null; 1410 } 1411 1412 // xy = upper left corner of item being dragged 1413 // bottomRightXy = lower right corner of item being dragged 1414 // This method will see which mini-screen is most overlapped by the item being dragged, and 1415 // return it. It will also transform the parameters xy and bottomRightXy into the local 1416 // coordinate space of the returned screen 1417 private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) { 1418 float x = xy[0]; 1419 float y = xy[1]; 1420 float right = bottomRightXy[0]; 1421 float bottom = bottomRightXy[1]; 1422 1423 float bestX = 0; 1424 float bestY = 0; 1425 float bestRight = 0; 1426 float bestBottom = 0; 1427 1428 Matrix inverseMatrix = new Matrix(); 1429 1430 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 1431 // with the item being dragged. 1432 final int screenCount = getChildCount(); 1433 CellLayout bestMatchingScreen = null; 1434 float bestOverlapSoFar = 0; 1435 for (int i = 0; i < screenCount; i++) { 1436 CellLayout cl = (CellLayout)getChildAt(i); 1437 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1438 float left = cl.getLeft(); 1439 float top = cl.getTop(); 1440 xy[0] = x + mScrollX - left; 1441 xy[1] = y + mScrollY - top; 1442 cl.getMatrix().invert(inverseMatrix); 1443 1444 bottomRightXy[0] = right + mScrollX - left; 1445 bottomRightXy[1] = bottom + mScrollY - top; 1446 1447 inverseMatrix.mapPoints(xy); 1448 inverseMatrix.mapPoints(bottomRightXy); 1449 1450 float dragRegionX = xy[0]; 1451 float dragRegionY = xy[1]; 1452 float dragRegionRight = bottomRightXy[0]; 1453 float dragRegionBottom = bottomRightXy[1]; 1454 1455 // Find the overlapping region 1456 float overlapLeft = Math.max(0f, dragRegionX); 1457 float overlapTop = Math.max(0f, dragRegionY); 1458 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 1459 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 1460 1461 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 1462 overlapTop >= 0 && overlapBottom <= cl.getHeight()) { 1463 // Calculate the size of the overlapping region 1464 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 1465 if (overlap > bestOverlapSoFar) { 1466 bestOverlapSoFar = overlap; 1467 bestMatchingScreen = cl; 1468 bestX = xy[0]; 1469 bestY = xy[1]; 1470 bestRight = bottomRightXy[0]; 1471 bestBottom = bottomRightXy[1]; 1472 } 1473 } 1474 } 1475 if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { 1476 if (mDragTargetLayout != null) { 1477 mDragTargetLayout.onDragComplete(); 1478 } 1479 mDragTargetLayout = bestMatchingScreen; 1480 } 1481 xy[0] = bestX; 1482 xy[1] = bestY; 1483 bottomRightXy[0] = bestRight; 1484 bottomRightXy[1] = bestBottom; 1485 return bestMatchingScreen; 1486 } 1487 1488 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1489 DragView dragView, Object dragInfo) { 1490 1491 final ItemInfo item = (ItemInfo)dragInfo; 1492 CellLayout currentLayout = getCurrentDropLayout(); 1493 1494 if (dragInfo instanceof LauncherAppWidgetInfo) { 1495 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1496 1497 if (widgetInfo.spanX == -1) { 1498 // Calculate the grid spans needed to fit this widget 1499 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 1500 item.spanX = spans[0]; 1501 item.spanY = spans[1]; 1502 } 1503 } 1504 int originX = x - xOffset; 1505 int originY = y - yOffset; 1506 if (mIsSmall) { 1507 // find out which mini screen the dragged item is over 1508 final float[] localXY = mTempDragCoordinates; 1509 localXY[0] = originX; 1510 localXY[1] = originY; 1511 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1512 1513 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1514 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1515 currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1516 if (currentLayout != null) { 1517 currentLayout.setHover(true); 1518 } 1519 1520 originX = (int)localXY[0]; 1521 originY = (int)localXY[1]; 1522 } 1523 1524 if (source != this) { 1525 // This is a hack to fix the point used to determine which cell an icon from the all 1526 // apps screen is over 1527 if (item != null && item.spanX == 1 && currentLayout != null) { 1528 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 1529 1530 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1531 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 1532 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 1533 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 1534 } 1535 } 1536 } 1537 if (currentLayout != mDragTargetLayout) { 1538 if (mDragTargetLayout != null) { 1539 mDragTargetLayout.onDragComplete(); 1540 } 1541 mDragTargetLayout = currentLayout; 1542 } 1543 1544 // only visualize the drop locations for moving icons within the home screen on tablet 1545 // on phone, we also visualize icons dragged in from All Apps 1546 if ((!LauncherApplication.isScreenXLarge() || source == this) 1547 && mDragTargetLayout != null) { 1548 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1549 mDragTargetLayout.visualizeDropLocation( 1550 child, originX, originY, item.spanX, item.spanY); 1551 } 1552 } 1553 1554 public void onDragExit(DragSource source, int x, int y, int xOffset, 1555 int yOffset, DragView dragView, Object dragInfo) { 1556 if (mDragTargetLayout != null) { 1557 mDragTargetLayout.onDragComplete(); 1558 mDragTargetLayout = null; 1559 } 1560 } 1561 1562 private void onDropExternal(int x, int y, Object dragInfo, 1563 CellLayout cellLayout) { 1564 onDropExternal(x, y, dragInfo, cellLayout, false); 1565 } 1566 1567 private void onDropExternal(int x, int y, Object dragInfo, 1568 CellLayout cellLayout, boolean insertAtFirst) { 1569 // Drag from somewhere else 1570 ItemInfo info = (ItemInfo) dragInfo; 1571 1572 View view = null; 1573 1574 switch (info.itemType) { 1575 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1576 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1577 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1578 // Came from all apps -- make a copy 1579 info = new ShortcutInfo((ApplicationInfo) info); 1580 } 1581 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1582 (ShortcutInfo) info); 1583 break; 1584 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1585 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1586 (ViewGroup) getChildAt(mCurrentScreen), 1587 ((UserFolderInfo) info)); 1588 break; 1589 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1590 cellLayout.setTagToCellInfoForPoint(x, y); 1591 int[] position = new int[2]; 1592 position[0] = x; 1593 position[1] = y; 1594 mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, 1595 cellLayout.getTag(), position); 1596 break; 1597 default: 1598 throw new IllegalStateException("Unknown item type: " 1599 + info.itemType); 1600 } 1601 1602 // If the view is null, it has already been added. 1603 if (view == null) { 1604 cellLayout.onDragComplete(); 1605 } else { 1606 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); 1607 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1608 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1609 cellLayout.onDropChild(view); 1610 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1611 1612 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1613 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, 1614 lp.cellX, lp.cellY); 1615 } 1616 } 1617 1618 /** 1619 * Return the current {@link CellLayout}, correctly picking the destination 1620 * screen while a scroll is in progress. 1621 */ 1622 private CellLayout getCurrentDropLayout() { 1623 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1624 return (CellLayout) getChildAt(index); 1625 } 1626 1627 /** 1628 * {@inheritDoc} 1629 */ 1630 public boolean acceptDrop(DragSource source, int x, int y, 1631 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1632 final CellLayout layout = getCurrentDropLayout(); 1633 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1634 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1635 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1636 1637 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1638 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1639 1640 if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { 1641 return true; 1642 } else { 1643 Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1644 return false; 1645 } 1646 } 1647 1648 /** 1649 * {@inheritDoc} 1650 */ 1651 public Rect estimateDropLocation(DragSource source, int x, int y, 1652 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { 1653 final CellLayout layout = getCurrentDropLayout(); 1654 1655 final CellLayout.CellInfo cellInfo = mDragInfo; 1656 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1657 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1658 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1659 1660 final Rect location = recycle != null ? recycle : new Rect(); 1661 1662 // Find drop cell and convert into rectangle 1663 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, 1664 spanY, ignoreView, layout, mTempCell); 1665 1666 if (dropCell == null) { 1667 return null; 1668 } 1669 1670 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); 1671 location.left = mTempEstimate[0]; 1672 location.top = mTempEstimate[1]; 1673 1674 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); 1675 location.right = mTempEstimate[0]; 1676 location.bottom = mTempEstimate[1]; 1677 1678 return location; 1679 } 1680 1681 /** 1682 * Calculate the nearest cell where the given object would be dropped. 1683 */ 1684 private int[] estimateDropCell(int pixelX, int pixelY, 1685 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1686 1687 final int[] cellXY = mTempCell; 1688 layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY); 1689 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1690 1691 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1692 // Find the best target drop location 1693 return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); 1694 } 1695 1696 /** 1697 * Estimate the size that a child with the given dimensions will take in the current screen. 1698 */ 1699 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1700 ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result); 1701 } 1702 1703 void setLauncher(Launcher launcher) { 1704 mLauncher = launcher; 1705 } 1706 1707 public void setDragController(DragController dragController) { 1708 mDragController = dragController; 1709 } 1710 1711 public void onDropCompleted(View target, boolean success) { 1712 if (success) { 1713 if (target != this && mDragInfo != null) { 1714 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1715 cellLayout.removeView(mDragInfo.cell); 1716 if (mDragInfo.cell instanceof DropTarget) { 1717 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1718 } 1719 // final Object tag = mDragInfo.cell.getTag(); 1720 } 1721 } else { 1722 if (mDragInfo != null) { 1723 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1724 cellLayout.onDropAborted(mDragInfo.cell); 1725 } 1726 } 1727 1728 mDragInfo = null; 1729 } 1730 1731 public void scrollLeft() { 1732 if (!mIsSmall) { 1733 if (mScroller.isFinished()) { 1734 if (mCurrentScreen > 0) 1735 snapToScreen(mCurrentScreen - 1); 1736 } else { 1737 if (mNextScreen > 0) 1738 snapToScreen(mNextScreen - 1); 1739 } 1740 } 1741 } 1742 1743 public void scrollRight() { 1744 if (!mIsSmall) { 1745 if (mScroller.isFinished()) { 1746 if (mCurrentScreen < getChildCount() - 1) 1747 snapToScreen(mCurrentScreen + 1); 1748 } else { 1749 if (mNextScreen < getChildCount() - 1) 1750 snapToScreen(mNextScreen + 1); 1751 } 1752 } 1753 } 1754 1755 public int getScreenForView(View v) { 1756 int result = -1; 1757 if (v != null) { 1758 ViewParent vp = v.getParent(); 1759 final int screenCount = getChildCount(); 1760 for (int i = 0; i < screenCount; i++) { 1761 if (vp == getChildAt(i)) { 1762 return i; 1763 } 1764 } 1765 } 1766 return result; 1767 } 1768 1769 public Folder getFolderForTag(Object tag) { 1770 final int screenCount = getChildCount(); 1771 for (int screen = 0; screen < screenCount; screen++) { 1772 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1773 int count = currentScreen.getChildCount(); 1774 for (int i = 0; i < count; i++) { 1775 View child = currentScreen.getChildAt(i); 1776 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1777 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1778 Folder f = (Folder) child; 1779 if (f.getInfo() == tag && f.getInfo().opened) { 1780 return f; 1781 } 1782 } 1783 } 1784 } 1785 return null; 1786 } 1787 1788 public View getViewForTag(Object tag) { 1789 int screenCount = getChildCount(); 1790 for (int screen = 0; screen < screenCount; screen++) { 1791 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1792 int count = currentScreen.getChildCount(); 1793 for (int i = 0; i < count; i++) { 1794 View child = currentScreen.getChildAt(i); 1795 if (child.getTag() == tag) { 1796 return child; 1797 } 1798 } 1799 } 1800 return null; 1801 } 1802 1803 /** 1804 * @return True is long presses are still allowed for the current touch 1805 */ 1806 public boolean allowLongPress() { 1807 return mAllowLongPress; 1808 } 1809 1810 /** 1811 * Set true to allow long-press events to be triggered, usually checked by 1812 * {@link Launcher} to accept or block dpad-initiated long-presses. 1813 */ 1814 public void setAllowLongPress(boolean allowLongPress) { 1815 mAllowLongPress = allowLongPress; 1816 } 1817 1818 void removeItems(final ArrayList<ApplicationInfo> apps) { 1819 final int screenCount = getChildCount(); 1820 final PackageManager manager = getContext().getPackageManager(); 1821 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1822 1823 final HashSet<String> packageNames = new HashSet<String>(); 1824 final int appCount = apps.size(); 1825 for (int i = 0; i < appCount; i++) { 1826 packageNames.add(apps.get(i).componentName.getPackageName()); 1827 } 1828 1829 for (int i = 0; i < screenCount; i++) { 1830 final CellLayout layout = (CellLayout) getChildAt(i); 1831 1832 // Avoid ANRs by treating each screen separately 1833 post(new Runnable() { 1834 public void run() { 1835 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1836 childrenToRemove.clear(); 1837 1838 int childCount = layout.getChildCount(); 1839 for (int j = 0; j < childCount; j++) { 1840 final View view = layout.getChildAt(j); 1841 Object tag = view.getTag(); 1842 1843 if (tag instanceof ShortcutInfo) { 1844 final ShortcutInfo info = (ShortcutInfo) tag; 1845 final Intent intent = info.intent; 1846 final ComponentName name = intent.getComponent(); 1847 1848 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1849 for (String packageName: packageNames) { 1850 if (packageName.equals(name.getPackageName())) { 1851 // TODO: This should probably be done on a worker thread 1852 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1853 childrenToRemove.add(view); 1854 } 1855 } 1856 } 1857 } else if (tag instanceof UserFolderInfo) { 1858 final UserFolderInfo info = (UserFolderInfo) tag; 1859 final ArrayList<ShortcutInfo> contents = info.contents; 1860 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1861 final int contentsCount = contents.size(); 1862 boolean removedFromFolder = false; 1863 1864 for (int k = 0; k < contentsCount; k++) { 1865 final ShortcutInfo appInfo = contents.get(k); 1866 final Intent intent = appInfo.intent; 1867 final ComponentName name = intent.getComponent(); 1868 1869 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1870 for (String packageName: packageNames) { 1871 if (packageName.equals(name.getPackageName())) { 1872 toRemove.add(appInfo); 1873 // TODO: This should probably be done on a worker thread 1874 LauncherModel.deleteItemFromDatabase( 1875 mLauncher, appInfo); 1876 removedFromFolder = true; 1877 } 1878 } 1879 } 1880 } 1881 1882 contents.removeAll(toRemove); 1883 if (removedFromFolder) { 1884 final Folder folder = getOpenFolder(); 1885 if (folder != null) 1886 folder.notifyDataSetChanged(); 1887 } 1888 } else if (tag instanceof LiveFolderInfo) { 1889 final LiveFolderInfo info = (LiveFolderInfo) tag; 1890 final Uri uri = info.uri; 1891 final ProviderInfo providerInfo = manager.resolveContentProvider( 1892 uri.getAuthority(), 0); 1893 1894 if (providerInfo != null) { 1895 for (String packageName: packageNames) { 1896 if (packageName.equals(providerInfo.packageName)) { 1897 // TODO: This should probably be done on a worker thread 1898 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1899 childrenToRemove.add(view); 1900 } 1901 } 1902 } 1903 } else if (tag instanceof LauncherAppWidgetInfo) { 1904 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1905 final AppWidgetProviderInfo provider = 1906 widgets.getAppWidgetInfo(info.appWidgetId); 1907 if (provider != null) { 1908 for (String packageName: packageNames) { 1909 if (packageName.equals(provider.provider.getPackageName())) { 1910 // TODO: This should probably be done on a worker thread 1911 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1912 childrenToRemove.add(view); 1913 } 1914 } 1915 } 1916 } 1917 } 1918 1919 childCount = childrenToRemove.size(); 1920 for (int j = 0; j < childCount; j++) { 1921 View child = childrenToRemove.get(j); 1922 layout.removeViewInLayout(child); 1923 if (child instanceof DropTarget) { 1924 mDragController.removeDropTarget((DropTarget)child); 1925 } 1926 } 1927 1928 if (childCount > 0) { 1929 layout.requestLayout(); 1930 layout.invalidate(); 1931 } 1932 } 1933 }); 1934 } 1935 } 1936 1937 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1938 final PackageManager pm = mLauncher.getPackageManager(); 1939 1940 final int screenCount = getChildCount(); 1941 for (int i = 0; i < screenCount; i++) { 1942 final CellLayout layout = (CellLayout) getChildAt(i); 1943 int childCount = layout.getChildCount(); 1944 for (int j = 0; j < childCount; j++) { 1945 final View view = layout.getChildAt(j); 1946 Object tag = view.getTag(); 1947 if (tag instanceof ShortcutInfo) { 1948 ShortcutInfo info = (ShortcutInfo)tag; 1949 // We need to check for ACTION_MAIN otherwise getComponent() might 1950 // return null for some shortcuts (for instance, for shortcuts to 1951 // web pages.) 1952 final Intent intent = info.intent; 1953 final ComponentName name = intent.getComponent(); 1954 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1955 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1956 final int appCount = apps.size(); 1957 for (int k = 0; k < appCount; k++) { 1958 ApplicationInfo app = apps.get(k); 1959 if (app.componentName.equals(name)) { 1960 info.setIcon(mIconCache.getIcon(info.intent)); 1961 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1962 new FastBitmapDrawable(info.getIcon(mIconCache)), 1963 null, null); 1964 } 1965 } 1966 } 1967 } 1968 } 1969 } 1970 } 1971 1972 void moveToDefaultScreen(boolean animate) { 1973 if (animate) { 1974 if (mIsSmall) { 1975 unshrink(mDefaultScreen); 1976 } else { 1977 snapToScreen(mDefaultScreen); 1978 } 1979 } else { 1980 setCurrentScreen(mDefaultScreen); 1981 } 1982 getChildAt(mDefaultScreen).requestFocus(); 1983 } 1984 1985 void setIndicators(Drawable previous, Drawable next) { 1986 mPreviousIndicator = previous; 1987 mNextIndicator = next; 1988 previous.setLevel(mCurrentScreen); 1989 next.setLevel(mCurrentScreen); 1990 } 1991 1992 public static class SavedState extends BaseSavedState { 1993 int currentScreen = -1; 1994 1995 SavedState(Parcelable superState) { 1996 super(superState); 1997 } 1998 1999 private SavedState(Parcel in) { 2000 super(in); 2001 currentScreen = in.readInt(); 2002 } 2003 2004 @Override 2005 public void writeToParcel(Parcel out, int flags) { 2006 super.writeToParcel(out, flags); 2007 out.writeInt(currentScreen); 2008 } 2009 2010 public static final Parcelable.Creator<SavedState> CREATOR = 2011 new Parcelable.Creator<SavedState>() { 2012 public SavedState createFromParcel(Parcel in) { 2013 return new SavedState(in); 2014 } 2015 2016 public SavedState[] newArray(int size) { 2017 return new SavedState[size]; 2018 } 2019 }; 2020 } 2021} 2022