Workspace.java revision 6b4adbc0558fcebbd44998bd00dcd334ddbee32d
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 snapToScreen(screen); 664 return true; 665 } 666 return false; 667 } 668 669 @Override 670 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 671 if (!mLauncher.isAllAppsVisible()) { 672 final Folder openFolder = getOpenFolder(); 673 if (openFolder != null) { 674 return openFolder.requestFocus(direction, previouslyFocusedRect); 675 } else { 676 int focusableScreen; 677 if (mNextScreen != INVALID_SCREEN) { 678 focusableScreen = mNextScreen; 679 } else { 680 focusableScreen = mCurrentScreen; 681 } 682 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); 683 } 684 } 685 return false; 686 } 687 688 @Override 689 public boolean dispatchUnhandledMove(View focused, int direction) { 690 if (direction == View.FOCUS_LEFT) { 691 if (getCurrentScreen() > 0) { 692 snapToScreen(getCurrentScreen() - 1); 693 return true; 694 } 695 } else if (direction == View.FOCUS_RIGHT) { 696 if (getCurrentScreen() < getChildCount() - 1) { 697 snapToScreen(getCurrentScreen() + 1); 698 return true; 699 } 700 } 701 return super.dispatchUnhandledMove(focused, direction); 702 } 703 704 @Override 705 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 706 if (!mLauncher.isAllAppsVisible()) { 707 final Folder openFolder = getOpenFolder(); 708 if (openFolder == null) { 709 getChildAt(mCurrentScreen).addFocusables(views, direction); 710 if (direction == View.FOCUS_LEFT) { 711 if (mCurrentScreen > 0) { 712 getChildAt(mCurrentScreen - 1).addFocusables(views, direction); 713 } 714 } else if (direction == View.FOCUS_RIGHT) { 715 if (mCurrentScreen < getChildCount() - 1) { 716 getChildAt(mCurrentScreen + 1).addFocusables(views, direction); 717 } 718 } 719 } else { 720 openFolder.addFocusables(views, direction); 721 } 722 } 723 } 724 725 @Override 726 public boolean dispatchTouchEvent(MotionEvent ev) { 727 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 728 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 729 // ie when you click on a mini-screen, it zooms back to that screen) 730 if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) { 731 return false; 732 } 733 } 734 return super.dispatchTouchEvent(ev); 735 } 736 737 /** 738 * {@inheritDoc} 739 */ 740 @Override 741 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 742 if (disallowIntercept) { 743 // We need to make sure to cancel our long press if 744 // a scrollable widget takes over touch events 745 final View currentScreen = getChildAt(mCurrentScreen); 746 currentScreen.cancelLongPress(); 747 } 748 super.requestDisallowInterceptTouchEvent(disallowIntercept); 749 } 750 751 @Override 752 public boolean onInterceptTouchEvent(MotionEvent ev) { 753 final boolean allAppsVisible = mLauncher.isAllAppsVisible(); 754 755 // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps 756 // ie when you click on a mini-screen, it zooms back to that screen) 757 if (!LauncherApplication.isScreenXLarge() && allAppsVisible) { 758 return false; // We don't want the events. Let them fall through to the all apps view. 759 } 760 761 /* 762 * This method JUST determines whether we want to intercept the motion. 763 * If we return true, onTouchEvent will be called and we do the actual 764 * scrolling there. 765 */ 766 767 /* 768 * Shortcut the most recurring case: the user is in the dragging 769 * state and he is moving his finger. We want to intercept this 770 * motion. 771 */ 772 final int action = ev.getAction(); 773 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { 774 return true; 775 } 776 777 if (mVelocityTracker == null) { 778 mVelocityTracker = VelocityTracker.obtain(); 779 } 780 mVelocityTracker.addMovement(ev); 781 782 switch (action & MotionEvent.ACTION_MASK) { 783 case MotionEvent.ACTION_MOVE: { 784 /* 785 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 786 * whether the user has moved far enough from his original down touch. 787 */ 788 789 /* 790 * Locally do absolute value. mLastMotionX is set to the y value 791 * of the down event. 792 */ 793 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 794 final float x = ev.getX(pointerIndex); 795 final float y = ev.getY(pointerIndex); 796 final int xDiff = (int) Math.abs(x - mLastMotionX); 797 final int yDiff = (int) Math.abs(y - mLastMotionY); 798 799 final int touchSlop = mTouchSlop; 800 boolean xMoved = xDiff > touchSlop; 801 boolean yMoved = yDiff > touchSlop; 802 803 if (xMoved || yMoved) { 804 805 if (xMoved) { 806 // Scroll if the user moved far enough along the X axis 807 mTouchState = TOUCH_STATE_SCROLLING; 808 mLastMotionX = x; 809 mTouchX = mScrollX; 810 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 811 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 812 } 813 // Either way, cancel any pending longpress 814 if (mAllowLongPress) { 815 mAllowLongPress = false; 816 // Try canceling the long press. It could also have been scheduled 817 // by a distant descendant, so use the mAllowLongPress flag to block 818 // everything 819 final View currentScreen = getChildAt(mCurrentScreen); 820 currentScreen.cancelLongPress(); 821 } 822 } 823 break; 824 } 825 826 case MotionEvent.ACTION_DOWN: { 827 final float x = ev.getX(); 828 final float y = ev.getY(); 829 // Remember location of down touch 830 mLastMotionX = x; 831 mLastMotionY = y; 832 mActivePointerId = ev.getPointerId(0); 833 mAllowLongPress = true; 834 835 /* 836 * If being flinged and user touches the screen, initiate drag; 837 * otherwise don't. mScroller.isFinished should be false when 838 * being flinged. 839 */ 840 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 841 break; 842 } 843 844 case MotionEvent.ACTION_CANCEL: 845 case MotionEvent.ACTION_UP: 846 847 if (mTouchState != TOUCH_STATE_SCROLLING) { 848 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); 849 if (!currentScreen.lastDownOnOccupiedCell()) { 850 getLocationOnScreen(mTempCell); 851 // Send a tap to the wallpaper if the last down was on empty space 852 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 853 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 854 "android.wallpaper.tap", 855 mTempCell[0] + (int) ev.getX(pointerIndex), 856 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); 857 } 858 } 859 860 // Release the drag 861 clearChildrenCache(); 862 mTouchState = TOUCH_STATE_REST; 863 mActivePointerId = INVALID_POINTER; 864 mAllowLongPress = false; 865 866 if (mVelocityTracker != null) { 867 mVelocityTracker.recycle(); 868 mVelocityTracker = null; 869 } 870 871 break; 872 873 case MotionEvent.ACTION_POINTER_UP: 874 onSecondaryPointerUp(ev); 875 break; 876 } 877 878 /* 879 * The only time we want to intercept motion events is if we are in the 880 * drag mode. 881 */ 882 return mTouchState != TOUCH_STATE_REST; 883 } 884 885 private void onSecondaryPointerUp(MotionEvent ev) { 886 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 887 MotionEvent.ACTION_POINTER_INDEX_SHIFT; 888 final int pointerId = ev.getPointerId(pointerIndex); 889 if (pointerId == mActivePointerId) { 890 // This was our active pointer going up. Choose a new 891 // active pointer and adjust accordingly. 892 // TODO: Make this decision more intelligent. 893 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 894 mLastMotionX = ev.getX(newPointerIndex); 895 mLastMotionY = ev.getY(newPointerIndex); 896 mActivePointerId = ev.getPointerId(newPointerIndex); 897 if (mVelocityTracker != null) { 898 mVelocityTracker.clear(); 899 } 900 } 901 } 902 903 /** 904 * If one of our descendant views decides that it could be focused now, only 905 * pass that along if it's on the current screen. 906 * 907 * This happens when live folders requery, and if they're off screen, they 908 * end up calling requestFocus, which pulls it on screen. 909 */ 910 @Override 911 public void focusableViewAvailable(View focused) { 912 View current = getChildAt(mCurrentScreen); 913 View v = focused; 914 while (true) { 915 if (v == current) { 916 super.focusableViewAvailable(focused); 917 return; 918 } 919 if (v == this) { 920 return; 921 } 922 ViewParent parent = v.getParent(); 923 if (parent instanceof View) { 924 v = (View) v.getParent(); 925 } else { 926 return; 927 } 928 } 929 } 930 931 void enableChildrenCache(int fromScreen, int toScreen) { 932 if (fromScreen > toScreen) { 933 final int temp = fromScreen; 934 fromScreen = toScreen; 935 toScreen = temp; 936 } 937 938 final int screenCount = getChildCount(); 939 940 fromScreen = Math.max(fromScreen, 0); 941 toScreen = Math.min(toScreen, screenCount - 1); 942 943 for (int i = fromScreen; i <= toScreen; i++) { 944 final CellLayout layout = (CellLayout) getChildAt(i); 945 layout.setChildrenDrawnWithCacheEnabled(true); 946 layout.setChildrenDrawingCacheEnabled(true); 947 } 948 } 949 950 void clearChildrenCache() { 951 final int screenCount = getChildCount(); 952 for (int i = 0; i < screenCount; i++) { 953 final CellLayout layout = (CellLayout) getChildAt(i); 954 layout.setChildrenDrawnWithCacheEnabled(false); 955 } 956 } 957 958 @Override 959 public boolean onTouchEvent(MotionEvent ev) { 960 if (mLauncher.isAllAppsVisible()) { 961 // Cancel any scrolling that is in progress. 962 if (!mScroller.isFinished()) { 963 mScroller.abortAnimation(); 964 } 965 snapToScreen(mCurrentScreen); 966 return false; // We don't want the events. Let them fall through to the all apps view. 967 } 968 969 if (mVelocityTracker == null) { 970 mVelocityTracker = VelocityTracker.obtain(); 971 } 972 mVelocityTracker.addMovement(ev); 973 974 final int action = ev.getAction(); 975 976 switch (action & MotionEvent.ACTION_MASK) { 977 case MotionEvent.ACTION_DOWN: 978 /* 979 * If being flinged and user touches, stop the fling. isFinished 980 * will be false if being flinged. 981 */ 982 if (!mScroller.isFinished()) { 983 mScroller.abortAnimation(); 984 } 985 986 // Remember where the motion event started 987 mLastMotionX = ev.getX(); 988 mActivePointerId = ev.getPointerId(0); 989 if (mTouchState == TOUCH_STATE_SCROLLING) { 990 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1); 991 } 992 break; 993 case MotionEvent.ACTION_MOVE: 994 if (mTouchState == TOUCH_STATE_SCROLLING) { 995 // Scroll to follow the motion event 996 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 997 final float x = ev.getX(pointerIndex); 998 final float deltaX = mLastMotionX - x; 999 mLastMotionX = x; 1000 1001 if (deltaX < 0) { 1002 if (mTouchX > 0) { 1003 mTouchX += Math.max(-mTouchX, deltaX); 1004 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1005 invalidate(); 1006 } 1007 } else if (deltaX > 0) { 1008 final float availableToScroll = getChildAt(getChildCount() - 1).getRight() - 1009 mTouchX - getWidth(); 1010 if (availableToScroll > 0) { 1011 mTouchX += Math.min(availableToScroll, deltaX); 1012 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 1013 invalidate(); 1014 } 1015 } else { 1016 awakenScrollBars(); 1017 } 1018 } 1019 break; 1020 case MotionEvent.ACTION_UP: 1021 if (mTouchState == TOUCH_STATE_SCROLLING) { 1022 final VelocityTracker velocityTracker = mVelocityTracker; 1023 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 1024 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); 1025 1026 final int screenWidth = getWidth(); 1027 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; 1028 final float scrolledPos = (float) mScrollX / screenWidth; 1029 1030 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 1031 // Fling hard enough to move left. 1032 // Don't fling across more than one screen at a time. 1033 final int bound = scrolledPos < whichScreen ? 1034 mCurrentScreen - 1 : mCurrentScreen; 1035 snapToScreen(Math.min(whichScreen, bound), velocityX, true); 1036 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { 1037 // Fling hard enough to move right 1038 // Don't fling across more than one screen at a time. 1039 final int bound = scrolledPos > whichScreen ? 1040 mCurrentScreen + 1 : mCurrentScreen; 1041 snapToScreen(Math.max(whichScreen, bound), velocityX, true); 1042 } else { 1043 snapToScreen(whichScreen, 0, true); 1044 } 1045 1046 if (mVelocityTracker != null) { 1047 mVelocityTracker.recycle(); 1048 mVelocityTracker = null; 1049 } 1050 } 1051 mTouchState = TOUCH_STATE_REST; 1052 mActivePointerId = INVALID_POINTER; 1053 break; 1054 case MotionEvent.ACTION_CANCEL: 1055 mTouchState = TOUCH_STATE_REST; 1056 mActivePointerId = INVALID_POINTER; 1057 break; 1058 case MotionEvent.ACTION_POINTER_UP: 1059 onSecondaryPointerUp(ev); 1060 break; 1061 } 1062 1063 return true; 1064 } 1065 1066 public boolean isSmall() { 1067 return mIsSmall; 1068 } 1069 1070 void shrinkToTop(boolean animated) { 1071 shrink(SHRINK_TO_TOP, animated); 1072 } 1073 1074 void shrinkToMiddle() { 1075 shrink(SHRINK_TO_MIDDLE, true); 1076 } 1077 1078 void shrinkToBottom() { 1079 shrinkToBottom(true); 1080 } 1081 1082 void shrinkToBottom(boolean animated) { 1083 if (mFirstLayout) { 1084 // (mFirstLayout == "first layout has not happened yet") 1085 // if we get a call to shrink() as part of our initialization (for example, if 1086 // Launcher is started in All Apps mode) then we need to wait for a layout call 1087 // to get our width so we can layout the mini-screen views correctly 1088 mWaitingToShrinkToBottom = true; 1089 } else { 1090 shrink(SHRINK_TO_BOTTOM, animated); 1091 } 1092 } 1093 1094 // we use this to shrink the workspace for the all apps view and the customize view 1095 private void shrink(int shrinkPosition, boolean animated) { 1096 mIsSmall = true; 1097 final Resources res = getResources(); 1098 final int screenWidth = getWidth(); 1099 final int screenHeight = getHeight(); 1100 final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth); 1101 final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight); 1102 final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing); 1103 1104 final int screenCount = getChildCount(); 1105 float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing; 1106 1107 float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin); 1108 if (shrinkPosition == SHRINK_TO_BOTTOM) { 1109 newY = screenHeight - newY - scaledScreenHeight; 1110 } else if (shrinkPosition == SHRINK_TO_MIDDLE) { 1111 newY = screenHeight / 2 - scaledScreenHeight / 2; 1112 } 1113 1114 // We animate all the screens to the centered position in workspace 1115 // At the same time, the screens become greyed/dimmed 1116 1117 // newX is initialized to the left-most position of the centered screens 1118 float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2; 1119 for (int i = 0; i < screenCount; i++) { 1120 CellLayout cl = (CellLayout) getChildAt(i); 1121 cl.setPivotX(0.0f); 1122 cl.setPivotY(0.0f); 1123 if (animated) { 1124 final int duration = res.getInteger(R.integer.config_workspaceShrinkTime); 1125 new PropertyAnimator(duration, cl, 1126 new PropertyValuesHolder("x", newX), 1127 new PropertyValuesHolder("y", newY), 1128 new PropertyValuesHolder("scaleX", SHRINK_FACTOR), 1129 new PropertyValuesHolder("scaleY", SHRINK_FACTOR), 1130 new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start(); 1131 } else { 1132 cl.setX((int)newX); 1133 cl.setY((int)newY); 1134 cl.setScaleX(SHRINK_FACTOR); 1135 cl.setScaleY(SHRINK_FACTOR); 1136 cl.setDimmedBitmapAlpha(1.0f); 1137 } 1138 // increment newX for the next screen 1139 newX += scaledScreenWidth + scaledSpacing; 1140 cl.setOnInterceptTouchListener(this); 1141 } 1142 setChildrenDrawnWithCacheEnabled(true); 1143 } 1144 1145 // We call this when we trigger an unshrink by clicking on the CellLayout cl 1146 private void unshrink(CellLayout clThatWasClicked) { 1147 int newCurrentScreen = mCurrentScreen; 1148 final int screenCount = getChildCount(); 1149 for (int i = 0; i < screenCount; i++) { 1150 if (getChildAt(i) == clThatWasClicked) { 1151 newCurrentScreen = i; 1152 } 1153 } 1154 unshrink(newCurrentScreen); 1155 } 1156 1157 private void unshrink(int newCurrentScreen) { 1158 if (mIsSmall) { 1159 int delta = (newCurrentScreen - mCurrentScreen)*getWidth(); 1160 1161 final int screenCount = getChildCount(); 1162 for (int i = 0; i < screenCount; i++) { 1163 CellLayout cl = (CellLayout) getChildAt(i); 1164 cl.setX(cl.getX() + delta); 1165 } 1166 mScrollX = newCurrentScreen * getWidth(); 1167 1168 unshrink(); 1169 setCurrentScreen(newCurrentScreen); 1170 } 1171 } 1172 1173 void unshrink() { 1174 unshrink(true); 1175 } 1176 1177 void unshrink(boolean animated) { 1178 if (mIsSmall) { 1179 Sequencer s = new Sequencer(); 1180 final int screenCount = getChildCount(); 1181 1182 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); 1183 for (int i = 0; i < screenCount; i++) { 1184 final CellLayout cl = (CellLayout)getChildAt(i); 1185 cl.setPivotX(0.0f); 1186 cl.setPivotY(0.0f); 1187 if (animated) { 1188 s.playTogether( 1189 new PropertyAnimator(duration, cl, "translationX", 0.0f), 1190 new PropertyAnimator(duration, cl, "translationY", 0.0f), 1191 new PropertyAnimator(duration, cl, "scaleX", 1.0f), 1192 new PropertyAnimator(duration, cl, "scaleY", 1.0f), 1193 new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f)); 1194 } else { 1195 cl.setTranslationX(0.0f); 1196 cl.setTranslationY(0.0f); 1197 cl.setScaleX(1.0f); 1198 cl.setScaleY(1.0f); 1199 cl.setDimmedBitmapAlpha(0.0f); 1200 } 1201 } 1202 s.addListener(mUnshrinkAnimationListener); 1203 s.start(); 1204 } 1205 } 1206 1207 void snapToScreen(int whichScreen) { 1208 snapToScreen(whichScreen, 0, false); 1209 } 1210 1211 private void snapToScreen(int whichScreen, int velocity, boolean settle) { 1212 // if (!mScroller.isFinished()) return; 1213 1214 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 1215 1216 enableChildrenCache(mCurrentScreen, whichScreen); 1217 1218 mNextScreen = whichScreen; 1219 1220 if (mPreviousIndicator != null) { 1221 mPreviousIndicator.setLevel(mNextScreen); 1222 mNextIndicator.setLevel(mNextScreen); 1223 } 1224 1225 View focusedChild = getFocusedChild(); 1226 if (focusedChild != null && whichScreen != mCurrentScreen && 1227 focusedChild == getChildAt(mCurrentScreen)) { 1228 focusedChild.clearFocus(); 1229 } 1230 1231 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); 1232 final int newX = whichScreen * getWidth(); 1233 final int delta = newX - mScrollX; 1234 int duration = (screenDelta + 1) * 100; 1235 1236 if (!mScroller.isFinished()) { 1237 mScroller.abortAnimation(); 1238 } 1239 1240 if (settle) { 1241 mScrollInterpolator.setDistance(screenDelta); 1242 } else { 1243 mScrollInterpolator.disableSettle(); 1244 } 1245 1246 velocity = Math.abs(velocity); 1247 if (velocity > 0) { 1248 duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) 1249 * FLING_VELOCITY_INFLUENCE; 1250 } else { 1251 duration += 100; 1252 } 1253 1254 awakenScrollBars(duration); 1255 mScroller.startScroll(mScrollX, 0, delta, 0, duration); 1256 invalidate(); 1257 } 1258 1259 void startDrag(CellLayout.CellInfo cellInfo) { 1260 View child = cellInfo.cell; 1261 1262 // Make sure the drag was started by a long press as opposed to a long click. 1263 if (!child.isInTouchMode()) { 1264 return; 1265 } 1266 1267 mDragInfo = cellInfo; 1268 mDragInfo.screen = mCurrentScreen; 1269 1270 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); 1271 1272 current.onDragChild(child); 1273 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE); 1274 invalidate(); 1275 } 1276 1277 @Override 1278 protected Parcelable onSaveInstanceState() { 1279 final SavedState state = new SavedState(super.onSaveInstanceState()); 1280 state.currentScreen = mCurrentScreen; 1281 return state; 1282 } 1283 1284 @Override 1285 protected void onRestoreInstanceState(Parcelable state) { 1286 SavedState savedState = (SavedState) state; 1287 super.onRestoreInstanceState(savedState.getSuperState()); 1288 if (savedState.currentScreen != -1) { 1289 setCurrentScreen(savedState.currentScreen, false); 1290 Launcher.setScreen(mCurrentScreen); 1291 } 1292 } 1293 1294 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { 1295 addApplicationShortcut(info, cellInfo, false); 1296 } 1297 1298 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, 1299 boolean insertAtFirst) { 1300 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); 1301 final int[] result = new int[2]; 1302 1303 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result); 1304 onDropExternal(result[0], result[1], info, layout, insertAtFirst); 1305 } 1306 1307 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 1308 DragView dragView, Object dragInfo) { 1309 CellLayout cellLayout = getCurrentDropLayout(); 1310 int originX = x - xOffset; 1311 int originY = y - yOffset; 1312 if (mIsSmall) { 1313 // find out which target layout is over 1314 final float[] localXY = mTempDragCoordinates; 1315 localXY[0] = originX; 1316 localXY[1] = originY; 1317 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1318 // we need to subtract left/top here because DragController already adds 1319 // dragRegionLeft/Top to xOffset and yOffset 1320 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1321 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1322 cellLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1323 if (cellLayout == null) { 1324 // cancel the drag if we're not over a mini-screen at time of drop 1325 // TODO: maybe add a nice fade here? 1326 return; 1327 } 1328 // localXY will be transformed into the local screen's coordinate space; save that info 1329 originX = (int)localXY[0]; 1330 originY = (int)localXY[1]; 1331 } 1332 if (source != this) { 1333 onDropExternal(originX, originY, dragInfo, cellLayout); 1334 } else { 1335 // Move internally 1336 if (mDragInfo != null) { 1337 final View cell = mDragInfo.cell; 1338 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1339 if (index != mDragInfo.screen) { 1340 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1341 originalCellLayout.removeView(cell); 1342 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, 1343 mDragInfo.spanX, mDragInfo.spanY); 1344 } 1345 1346 mTargetCell = estimateDropCell(originX, originY, 1347 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, 1348 mTargetCell); 1349 cellLayout.onDropChild(cell); 1350 1351 // update the item's position after drop 1352 final ItemInfo info = (ItemInfo) cell.getTag(); 1353 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell 1354 .getLayoutParams(); 1355 lp.cellX = mTargetCell[0]; 1356 lp.cellY = mTargetCell[1]; 1357 1358 LauncherModel.moveItemInDatabase(mLauncher, info, 1359 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, 1360 lp.cellX, lp.cellY); 1361 } 1362 } 1363 } 1364 1365 public void onDragEnter(DragSource source, int x, int y, int xOffset, 1366 int yOffset, DragView dragView, Object dragInfo) { 1367 } 1368 1369 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 1370 DragView dragView, Object dragInfo) { 1371 1372 // We may need to delegate the drag to a child view. If a 1x1 item 1373 // would land in a cell occupied by a DragTarget (e.g. a Folder), 1374 // then drag events should be handled by that child. 1375 1376 ItemInfo item = (ItemInfo)dragInfo; 1377 CellLayout currentLayout = getCurrentDropLayout(); 1378 1379 int dragPointX, dragPointY; 1380 if (item.spanX == 1 && item.spanY == 1) { 1381 // For a 1x1, calculate the drop cell exactly as in onDragOver 1382 dragPointX = x - xOffset; 1383 dragPointY = y - yOffset; 1384 } else { 1385 // Otherwise, use the exact drag coordinates 1386 dragPointX = x; 1387 dragPointY = y; 1388 } 1389 1390 // If we are dragging over a cell that contains a DropTarget that will 1391 // accept the drop, delegate to that DropTarget. 1392 final int[] cellXY = mTempCell; 1393 currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); 1394 View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); 1395 if (child instanceof DropTarget) { 1396 DropTarget target = (DropTarget)child; 1397 if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { 1398 return target; 1399 } 1400 } 1401 return null; 1402 } 1403 1404 // xy = upper left corner of item being dragged 1405 // bottomRightXy = lower right corner of item being dragged 1406 // This method will see which mini-screen is most overlapped by the item being dragged, and 1407 // return it. It will also transform the parameters xy and bottomRightXy into the local 1408 // coordinate space of the returned screen 1409 private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) { 1410 float x = xy[0]; 1411 float y = xy[1]; 1412 float right = bottomRightXy[0]; 1413 float bottom = bottomRightXy[1]; 1414 1415 float bestX = 0; 1416 float bestY = 0; 1417 float bestRight = 0; 1418 float bestBottom = 0; 1419 1420 Matrix inverseMatrix = new Matrix(); 1421 1422 // We loop through all the screens (ie CellLayouts) and see which one overlaps the most 1423 // with the item being dragged. 1424 final int screenCount = getChildCount(); 1425 CellLayout bestMatchingScreen = null; 1426 float bestOverlapSoFar = 0; 1427 for (int i = 0; i < screenCount; i++) { 1428 CellLayout cl = (CellLayout)getChildAt(i); 1429 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 1430 float left = cl.getLeft(); 1431 float top = cl.getTop(); 1432 xy[0] = x + mScrollX - left; 1433 xy[1] = y + mScrollY - top; 1434 cl.getMatrix().invert(inverseMatrix); 1435 1436 bottomRightXy[0] = right + mScrollX - left; 1437 bottomRightXy[1] = bottom + mScrollY - top; 1438 1439 inverseMatrix.mapPoints(xy); 1440 inverseMatrix.mapPoints(bottomRightXy); 1441 1442 float dragRegionX = xy[0]; 1443 float dragRegionY = xy[1]; 1444 float dragRegionRight = bottomRightXy[0]; 1445 float dragRegionBottom = bottomRightXy[1]; 1446 1447 // Find the overlapping region 1448 float overlapLeft = Math.max(0f, dragRegionX); 1449 float overlapTop = Math.max(0f, dragRegionY); 1450 float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom); 1451 float overlapRight = Math.min(cl.getWidth(), dragRegionRight); 1452 1453 if (overlapRight >= 0 && overlapLeft <= cl.getWidth() && 1454 overlapTop >= 0 && overlapBottom <= cl.getHeight()) { 1455 // Calculate the size of the overlapping region 1456 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop); 1457 if (overlap > bestOverlapSoFar) { 1458 bestOverlapSoFar = overlap; 1459 bestMatchingScreen = cl; 1460 bestX = xy[0]; 1461 bestY = xy[1]; 1462 bestRight = bottomRightXy[0]; 1463 bestBottom = bottomRightXy[1]; 1464 } 1465 } 1466 } 1467 if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) { 1468 if (mDragTargetLayout != null) { 1469 mDragTargetLayout.onDragComplete(); 1470 } 1471 mDragTargetLayout = bestMatchingScreen; 1472 } 1473 xy[0] = bestX; 1474 xy[1] = bestY; 1475 bottomRightXy[0] = bestRight; 1476 bottomRightXy[1] = bestBottom; 1477 return bestMatchingScreen; 1478 } 1479 1480 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 1481 DragView dragView, Object dragInfo) { 1482 1483 final ItemInfo item = (ItemInfo)dragInfo; 1484 CellLayout currentLayout = getCurrentDropLayout(); 1485 1486 if (dragInfo instanceof LauncherAppWidgetInfo) { 1487 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; 1488 1489 if (widgetInfo.spanX == -1) { 1490 // Calculate the grid spans needed to fit this widget 1491 int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); 1492 item.spanX = spans[0]; 1493 item.spanY = spans[1]; 1494 } 1495 } 1496 int originX = x - xOffset; 1497 int originY = y - yOffset; 1498 if (mIsSmall) { 1499 // find out which mini screen the dragged item is over 1500 final float[] localXY = mTempDragCoordinates; 1501 localXY[0] = originX; 1502 localXY[1] = originY; 1503 final float[] localBottomRightXY = mTempDragBottomRightCoordinates; 1504 1505 localBottomRightXY[0] = originX + dragView.getDragRegionWidth(); 1506 localBottomRightXY[1] = originY + dragView.getDragRegionHeight(); 1507 currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY); 1508 if (currentLayout != null) { 1509 currentLayout.setHover(true); 1510 } 1511 1512 originX = (int)localXY[0]; 1513 originY = (int)localXY[1]; 1514 } 1515 1516 if (source != this) { 1517 // This is a hack to fix the point used to determine which cell an icon from the all 1518 // apps screen is over 1519 if (item != null && item.spanX == 1 && currentLayout != null) { 1520 int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2; 1521 1522 originX += dragRegionLeft - dragView.getDragRegionLeft(); 1523 if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) { 1524 dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(), 1525 currentLayout.getCellWidth(), dragView.getDragRegionHeight()); 1526 } 1527 } 1528 } 1529 if (currentLayout != mDragTargetLayout) { 1530 if (mDragTargetLayout != null) { 1531 mDragTargetLayout.onDragComplete(); 1532 } 1533 mDragTargetLayout = currentLayout; 1534 } 1535 1536 // only visualize the drop locations for moving icons within the home screen on tablet 1537 // on phone, we also visualize icons dragged in from All Apps 1538 if ((!LauncherApplication.isScreenXLarge() || source == this) 1539 && mDragTargetLayout != null) { 1540 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 1541 mDragTargetLayout.visualizeDropLocation( 1542 child, originX, originY, item.spanX, item.spanY); 1543 } 1544 } 1545 1546 public void onDragExit(DragSource source, int x, int y, int xOffset, 1547 int yOffset, DragView dragView, Object dragInfo) { 1548 if (mDragTargetLayout != null) { 1549 mDragTargetLayout.onDragComplete(); 1550 mDragTargetLayout = null; 1551 } 1552 } 1553 1554 private void onDropExternal(int x, int y, Object dragInfo, 1555 CellLayout cellLayout) { 1556 onDropExternal(x, y, dragInfo, cellLayout, false); 1557 } 1558 1559 private void onDropExternal(int x, int y, Object dragInfo, 1560 CellLayout cellLayout, boolean insertAtFirst) { 1561 // Drag from somewhere else 1562 ItemInfo info = (ItemInfo) dragInfo; 1563 1564 View view = null; 1565 1566 switch (info.itemType) { 1567 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1568 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1569 if (info.container == NO_ID && info instanceof ApplicationInfo) { 1570 // Came from all apps -- make a copy 1571 info = new ShortcutInfo((ApplicationInfo) info); 1572 } 1573 view = mLauncher.createShortcut(R.layout.application, cellLayout, 1574 (ShortcutInfo) info); 1575 break; 1576 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: 1577 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, 1578 (ViewGroup) getChildAt(mCurrentScreen), 1579 ((UserFolderInfo) info)); 1580 break; 1581 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1582 cellLayout.setTagToCellInfoForPoint(x, y); 1583 int[] position = new int[2]; 1584 position[0] = x; 1585 position[1] = y; 1586 mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, 1587 cellLayout.getTag(), position); 1588 break; 1589 default: 1590 throw new IllegalStateException("Unknown item type: " 1591 + info.itemType); 1592 } 1593 1594 // If the view is null, it has already been added. 1595 if (view == null) { 1596 cellLayout.onDragComplete(); 1597 } else { 1598 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); 1599 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], 1600 mTargetCell[1], info.spanX, info.spanY, insertAtFirst); 1601 cellLayout.onDropChild(view); 1602 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 1603 1604 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, 1605 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, 1606 lp.cellX, lp.cellY); 1607 } 1608 } 1609 1610 /** 1611 * Return the current {@link CellLayout}, correctly picking the destination 1612 * screen while a scroll is in progress. 1613 */ 1614 private CellLayout getCurrentDropLayout() { 1615 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; 1616 return (CellLayout) getChildAt(index); 1617 } 1618 1619 /** 1620 * {@inheritDoc} 1621 */ 1622 public boolean acceptDrop(DragSource source, int x, int y, 1623 int xOffset, int yOffset, DragView dragView, Object dragInfo) { 1624 final CellLayout layout = getCurrentDropLayout(); 1625 final CellLayout.CellInfo dragCellInfo = mDragInfo; 1626 final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX; 1627 final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY; 1628 1629 final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell; 1630 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1631 1632 if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) { 1633 return true; 1634 } else { 1635 Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); 1636 return false; 1637 } 1638 } 1639 1640 /** 1641 * {@inheritDoc} 1642 */ 1643 public Rect estimateDropLocation(DragSource source, int x, int y, 1644 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { 1645 final CellLayout layout = getCurrentDropLayout(); 1646 1647 final CellLayout.CellInfo cellInfo = mDragInfo; 1648 final int spanX = cellInfo == null ? 1 : cellInfo.spanX; 1649 final int spanY = cellInfo == null ? 1 : cellInfo.spanY; 1650 final View ignoreView = cellInfo == null ? null : cellInfo.cell; 1651 1652 final Rect location = recycle != null ? recycle : new Rect(); 1653 1654 // Find drop cell and convert into rectangle 1655 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, 1656 spanY, ignoreView, layout, mTempCell); 1657 1658 if (dropCell == null) { 1659 return null; 1660 } 1661 1662 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); 1663 location.left = mTempEstimate[0]; 1664 location.top = mTempEstimate[1]; 1665 1666 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); 1667 location.right = mTempEstimate[0]; 1668 location.bottom = mTempEstimate[1]; 1669 1670 return location; 1671 } 1672 1673 /** 1674 * Calculate the nearest cell where the given object would be dropped. 1675 */ 1676 private int[] estimateDropCell(int pixelX, int pixelY, 1677 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { 1678 1679 final int[] cellXY = mTempCell; 1680 layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY); 1681 layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); 1682 1683 final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView); 1684 // Find the best target drop location 1685 return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle); 1686 } 1687 1688 /** 1689 * Estimate the size that a child with the given dimensions will take in the current screen. 1690 */ 1691 void estimateChildSize(int minWidth, int minHeight, int[] result) { 1692 ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result); 1693 } 1694 1695 void setLauncher(Launcher launcher) { 1696 mLauncher = launcher; 1697 } 1698 1699 public void setDragController(DragController dragController) { 1700 mDragController = dragController; 1701 } 1702 1703 public void onDropCompleted(View target, boolean success) { 1704 if (success) { 1705 if (target != this && mDragInfo != null) { 1706 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1707 cellLayout.removeView(mDragInfo.cell); 1708 if (mDragInfo.cell instanceof DropTarget) { 1709 mDragController.removeDropTarget((DropTarget)mDragInfo.cell); 1710 } 1711 // final Object tag = mDragInfo.cell.getTag(); 1712 } 1713 } else { 1714 if (mDragInfo != null) { 1715 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); 1716 cellLayout.onDropAborted(mDragInfo.cell); 1717 } 1718 } 1719 1720 mDragInfo = null; 1721 } 1722 1723 public void scrollLeft() { 1724 if (!mIsSmall) { 1725 if (mScroller.isFinished()) { 1726 if (mCurrentScreen > 0) 1727 snapToScreen(mCurrentScreen - 1); 1728 } else { 1729 if (mNextScreen > 0) 1730 snapToScreen(mNextScreen - 1); 1731 } 1732 } 1733 } 1734 1735 public void scrollRight() { 1736 if (!mIsSmall) { 1737 if (mScroller.isFinished()) { 1738 if (mCurrentScreen < getChildCount() - 1) 1739 snapToScreen(mCurrentScreen + 1); 1740 } else { 1741 if (mNextScreen < getChildCount() - 1) 1742 snapToScreen(mNextScreen + 1); 1743 } 1744 } 1745 } 1746 1747 public int getScreenForView(View v) { 1748 int result = -1; 1749 if (v != null) { 1750 ViewParent vp = v.getParent(); 1751 final int screenCount = getChildCount(); 1752 for (int i = 0; i < screenCount; i++) { 1753 if (vp == getChildAt(i)) { 1754 return i; 1755 } 1756 } 1757 } 1758 return result; 1759 } 1760 1761 public Folder getFolderForTag(Object tag) { 1762 final int screenCount = getChildCount(); 1763 for (int screen = 0; screen < screenCount; screen++) { 1764 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1765 int count = currentScreen.getChildCount(); 1766 for (int i = 0; i < count; i++) { 1767 View child = currentScreen.getChildAt(i); 1768 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 1769 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { 1770 Folder f = (Folder) child; 1771 if (f.getInfo() == tag && f.getInfo().opened) { 1772 return f; 1773 } 1774 } 1775 } 1776 } 1777 return null; 1778 } 1779 1780 public View getViewForTag(Object tag) { 1781 int screenCount = getChildCount(); 1782 for (int screen = 0; screen < screenCount; screen++) { 1783 CellLayout currentScreen = ((CellLayout) getChildAt(screen)); 1784 int count = currentScreen.getChildCount(); 1785 for (int i = 0; i < count; i++) { 1786 View child = currentScreen.getChildAt(i); 1787 if (child.getTag() == tag) { 1788 return child; 1789 } 1790 } 1791 } 1792 return null; 1793 } 1794 1795 /** 1796 * @return True is long presses are still allowed for the current touch 1797 */ 1798 public boolean allowLongPress() { 1799 return mAllowLongPress; 1800 } 1801 1802 /** 1803 * Set true to allow long-press events to be triggered, usually checked by 1804 * {@link Launcher} to accept or block dpad-initiated long-presses. 1805 */ 1806 public void setAllowLongPress(boolean allowLongPress) { 1807 mAllowLongPress = allowLongPress; 1808 } 1809 1810 void removeItems(final ArrayList<ApplicationInfo> apps) { 1811 final int screenCount = getChildCount(); 1812 final PackageManager manager = getContext().getPackageManager(); 1813 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext()); 1814 1815 final HashSet<String> packageNames = new HashSet<String>(); 1816 final int appCount = apps.size(); 1817 for (int i = 0; i < appCount; i++) { 1818 packageNames.add(apps.get(i).componentName.getPackageName()); 1819 } 1820 1821 for (int i = 0; i < screenCount; i++) { 1822 final CellLayout layout = (CellLayout) getChildAt(i); 1823 1824 // Avoid ANRs by treating each screen separately 1825 post(new Runnable() { 1826 public void run() { 1827 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 1828 childrenToRemove.clear(); 1829 1830 int childCount = layout.getChildCount(); 1831 for (int j = 0; j < childCount; j++) { 1832 final View view = layout.getChildAt(j); 1833 Object tag = view.getTag(); 1834 1835 if (tag instanceof ShortcutInfo) { 1836 final ShortcutInfo info = (ShortcutInfo) tag; 1837 final Intent intent = info.intent; 1838 final ComponentName name = intent.getComponent(); 1839 1840 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1841 for (String packageName: packageNames) { 1842 if (packageName.equals(name.getPackageName())) { 1843 // TODO: This should probably be done on a worker thread 1844 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1845 childrenToRemove.add(view); 1846 } 1847 } 1848 } 1849 } else if (tag instanceof UserFolderInfo) { 1850 final UserFolderInfo info = (UserFolderInfo) tag; 1851 final ArrayList<ShortcutInfo> contents = info.contents; 1852 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); 1853 final int contentsCount = contents.size(); 1854 boolean removedFromFolder = false; 1855 1856 for (int k = 0; k < contentsCount; k++) { 1857 final ShortcutInfo appInfo = contents.get(k); 1858 final Intent intent = appInfo.intent; 1859 final ComponentName name = intent.getComponent(); 1860 1861 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1862 for (String packageName: packageNames) { 1863 if (packageName.equals(name.getPackageName())) { 1864 toRemove.add(appInfo); 1865 // TODO: This should probably be done on a worker thread 1866 LauncherModel.deleteItemFromDatabase( 1867 mLauncher, appInfo); 1868 removedFromFolder = true; 1869 } 1870 } 1871 } 1872 } 1873 1874 contents.removeAll(toRemove); 1875 if (removedFromFolder) { 1876 final Folder folder = getOpenFolder(); 1877 if (folder != null) 1878 folder.notifyDataSetChanged(); 1879 } 1880 } else if (tag instanceof LiveFolderInfo) { 1881 final LiveFolderInfo info = (LiveFolderInfo) tag; 1882 final Uri uri = info.uri; 1883 final ProviderInfo providerInfo = manager.resolveContentProvider( 1884 uri.getAuthority(), 0); 1885 1886 if (providerInfo != null) { 1887 for (String packageName: packageNames) { 1888 if (packageName.equals(providerInfo.packageName)) { 1889 // TODO: This should probably be done on a worker thread 1890 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1891 childrenToRemove.add(view); 1892 } 1893 } 1894 } 1895 } else if (tag instanceof LauncherAppWidgetInfo) { 1896 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 1897 final AppWidgetProviderInfo provider = 1898 widgets.getAppWidgetInfo(info.appWidgetId); 1899 if (provider != null) { 1900 for (String packageName: packageNames) { 1901 if (packageName.equals(provider.provider.getPackageName())) { 1902 // TODO: This should probably be done on a worker thread 1903 LauncherModel.deleteItemFromDatabase(mLauncher, info); 1904 childrenToRemove.add(view); 1905 } 1906 } 1907 } 1908 } 1909 } 1910 1911 childCount = childrenToRemove.size(); 1912 for (int j = 0; j < childCount; j++) { 1913 View child = childrenToRemove.get(j); 1914 layout.removeViewInLayout(child); 1915 if (child instanceof DropTarget) { 1916 mDragController.removeDropTarget((DropTarget)child); 1917 } 1918 } 1919 1920 if (childCount > 0) { 1921 layout.requestLayout(); 1922 layout.invalidate(); 1923 } 1924 } 1925 }); 1926 } 1927 } 1928 1929 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 1930 final PackageManager pm = mLauncher.getPackageManager(); 1931 1932 final int screenCount = getChildCount(); 1933 for (int i = 0; i < screenCount; i++) { 1934 final CellLayout layout = (CellLayout) getChildAt(i); 1935 int childCount = layout.getChildCount(); 1936 for (int j = 0; j < childCount; j++) { 1937 final View view = layout.getChildAt(j); 1938 Object tag = view.getTag(); 1939 if (tag instanceof ShortcutInfo) { 1940 ShortcutInfo info = (ShortcutInfo)tag; 1941 // We need to check for ACTION_MAIN otherwise getComponent() might 1942 // return null for some shortcuts (for instance, for shortcuts to 1943 // web pages.) 1944 final Intent intent = info.intent; 1945 final ComponentName name = intent.getComponent(); 1946 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 1947 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 1948 final int appCount = apps.size(); 1949 for (int k = 0; k < appCount; k++) { 1950 ApplicationInfo app = apps.get(k); 1951 if (app.componentName.equals(name)) { 1952 info.setIcon(mIconCache.getIcon(info.intent)); 1953 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null, 1954 new FastBitmapDrawable(info.getIcon(mIconCache)), 1955 null, null); 1956 } 1957 } 1958 } 1959 } 1960 } 1961 } 1962 } 1963 1964 void moveToDefaultScreen(boolean animate) { 1965 if (animate) { 1966 if (mIsSmall) { 1967 unshrink(mDefaultScreen); 1968 } else { 1969 snapToScreen(mDefaultScreen); 1970 } 1971 } else { 1972 setCurrentScreen(mDefaultScreen); 1973 } 1974 getChildAt(mDefaultScreen).requestFocus(); 1975 } 1976 1977 void setIndicators(Drawable previous, Drawable next) { 1978 mPreviousIndicator = previous; 1979 mNextIndicator = next; 1980 previous.setLevel(mCurrentScreen); 1981 next.setLevel(mCurrentScreen); 1982 } 1983 1984 public static class SavedState extends BaseSavedState { 1985 int currentScreen = -1; 1986 1987 SavedState(Parcelable superState) { 1988 super(superState); 1989 } 1990 1991 private SavedState(Parcel in) { 1992 super(in); 1993 currentScreen = in.readInt(); 1994 } 1995 1996 @Override 1997 public void writeToParcel(Parcel out, int flags) { 1998 super.writeToParcel(out, flags); 1999 out.writeInt(currentScreen); 2000 } 2001 2002 public static final Parcelable.Creator<SavedState> CREATOR = 2003 new Parcelable.Creator<SavedState>() { 2004 public SavedState createFromParcel(Parcel in) { 2005 return new SavedState(in); 2006 } 2007 2008 public SavedState[] newArray(int size) { 2009 return new SavedState[size]; 2010 } 2011 }; 2012 } 2013} 2014