Workspace.java revision ee0ce2b29f9cb06cc143c6f30ffc77f4915b0da9
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.launcher3; 18 19import android.animation.Animator; 20import android.animation.AnimatorSet; 21import android.animation.ObjectAnimator; 22import android.animation.TimeInterpolator; 23import android.animation.ValueAnimator; 24import android.animation.ValueAnimator.AnimatorUpdateListener; 25import android.app.WallpaperManager; 26import android.appwidget.AppWidgetHostView; 27import android.appwidget.AppWidgetProviderInfo; 28import android.content.ComponentName; 29import android.content.Context; 30import android.content.Intent; 31import android.content.SharedPreferences; 32import android.content.res.Resources; 33import android.content.res.TypedArray; 34import android.graphics.Bitmap; 35import android.graphics.Canvas; 36import android.graphics.Matrix; 37import android.graphics.Point; 38import android.graphics.PointF; 39import android.graphics.Rect; 40import android.graphics.Region.Op; 41import android.graphics.drawable.Drawable; 42import android.os.IBinder; 43import android.os.Parcelable; 44import android.util.AttributeSet; 45import android.util.Log; 46import android.util.SparseArray; 47import android.view.Display; 48import android.view.MotionEvent; 49import android.view.View; 50import android.view.ViewGroup; 51import android.view.animation.DecelerateInterpolator; 52import android.widget.ImageView; 53import android.widget.TextView; 54 55import com.android.launcher3.FolderIcon.FolderRingAnimator; 56import com.android.launcher3.LauncherSettings.Favorites; 57 58import java.net.URISyntaxException; 59import java.util.ArrayList; 60import java.util.HashMap; 61import java.util.HashSet; 62import java.util.Iterator; 63import java.util.Set; 64 65/** 66 * The workspace is a wide area with a wallpaper and a finite number of pages. 67 * Each page contains a number of icons, folders or widgets the user can 68 * interact with. A workspace is meant to be used with a fixed width only. 69 */ 70public class Workspace extends SmoothPagedView 71 implements DropTarget, DragSource, DragScroller, View.OnTouchListener, 72 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener { 73 private static final String TAG = "Launcher.Workspace"; 74 75 // Y rotation to apply to the workspace screens 76 private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f; 77 78 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; 79 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; 80 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; 81 82 private static final int BACKGROUND_FADE_OUT_DURATION = 350; 83 private static final int ADJACENT_SCREEN_DROP_DURATION = 300; 84 private static final int FLING_THRESHOLD_VELOCITY = 500; 85 86 // These animators are used to fade the children's outlines 87 private ObjectAnimator mChildrenOutlineFadeInAnimation; 88 private ObjectAnimator mChildrenOutlineFadeOutAnimation; 89 private float mChildrenOutlineAlpha = 0; 90 91 // These properties refer to the background protection gradient used for AllApps and Customize 92 private ValueAnimator mBackgroundFadeInAnimation; 93 private ValueAnimator mBackgroundFadeOutAnimation; 94 private Drawable mBackground; 95 boolean mDrawBackground = true; 96 private float mBackgroundAlpha = 0; 97 98 private float mWallpaperScrollRatio = 1.0f; 99 private int mOriginalPageSpacing; 100 101 private final WallpaperManager mWallpaperManager; 102 private IBinder mWindowToken; 103 private static final float WALLPAPER_SCREENS_SPAN = 2f; 104 105 private int mDefaultPage; 106 107 // The screen id used for the empty screen always present to the right. 108 private final static long EXTRA_EMPTY_SCREEN_ID = -201; 109 private final static long CUSTOM_CONTENT_SCREEN_ID = -301; 110 111 private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>(); 112 private ArrayList<Long> mScreenOrder = new ArrayList<Long>(); 113 114 /** 115 * CellInfo for the cell that is currently being dragged 116 */ 117 private CellLayout.CellInfo mDragInfo; 118 119 /** 120 * Target drop area calculated during last acceptDrop call. 121 */ 122 private int[] mTargetCell = new int[2]; 123 private int mDragOverX = -1; 124 private int mDragOverY = -1; 125 126 static Rect mLandscapeCellLayoutMetrics = null; 127 static Rect mPortraitCellLayoutMetrics = null; 128 129 /** 130 * The CellLayout that is currently being dragged over 131 */ 132 private CellLayout mDragTargetLayout = null; 133 /** 134 * The CellLayout that we will show as glowing 135 */ 136 private CellLayout mDragOverlappingLayout = null; 137 138 /** 139 * The CellLayout which will be dropped to 140 */ 141 private CellLayout mDropToLayout = null; 142 143 private Launcher mLauncher; 144 private IconCache mIconCache; 145 private DragController mDragController; 146 147 // These are temporary variables to prevent having to allocate a new object just to 148 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 149 private int[] mTempCell = new int[2]; 150 private int[] mTempEstimate = new int[2]; 151 private float[] mDragViewVisualCenter = new float[2]; 152 private float[] mTempDragCoordinates = new float[2]; 153 private float[] mTempCellLayoutCenterCoordinates = new float[2]; 154 private float[] mTempDragBottomRightCoordinates = new float[2]; 155 private Matrix mTempInverseMatrix = new Matrix(); 156 157 private SpringLoadedDragController mSpringLoadedDragController; 158 private float mSpringLoadedShrinkFactor; 159 160 private static final int DEFAULT_CELL_COUNT_X = 4; 161 private static final int DEFAULT_CELL_COUNT_Y = 4; 162 163 // State variable that indicates whether the pages are small (ie when you're 164 // in all apps or customize mode) 165 166 enum State { NORMAL, SPRING_LOADED, SMALL }; 167 private State mState = State.NORMAL; 168 private boolean mIsSwitchingState = false; 169 170 boolean mAnimatingViewIntoPlace = false; 171 boolean mIsDragOccuring = false; 172 boolean mChildrenLayersEnabled = true; 173 174 /** Is the user is dragging an item near the edge of a page? */ 175 private boolean mInScrollArea = false; 176 177 private HolographicOutlineHelper mOutlineHelper; 178 private Bitmap mDragOutline = null; 179 private final Rect mTempRect = new Rect(); 180 private final int[] mTempXY = new int[2]; 181 private int[] mTempVisiblePagesRange = new int[2]; 182 private float mOverscrollFade = 0; 183 private boolean mOverscrollTransformsSet; 184 public static final int DRAG_BITMAP_PADDING = 2; 185 private boolean mWorkspaceFadeInAdjacentScreens; 186 187 enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM }; 188 int mWallpaperWidth; 189 int mWallpaperHeight; 190 WallpaperOffsetInterpolator mWallpaperOffset; 191 boolean mUpdateWallpaperOffsetImmediately = false; 192 private Runnable mDelayedResizeRunnable; 193 private Runnable mDelayedSnapToPageRunnable; 194 private Point mDisplaySize = new Point(); 195 private boolean mIsStaticWallpaper; 196 private int mWallpaperTravelWidth; 197 private int mSpringLoadedPageSpacing; 198 private int mCameraDistance; 199 200 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts 201 private static final int FOLDER_CREATION_TIMEOUT = 0; 202 private static final int REORDER_TIMEOUT = 250; 203 private final Alarm mFolderCreationAlarm = new Alarm(); 204 private final Alarm mReorderAlarm = new Alarm(); 205 private FolderRingAnimator mDragFolderRingAnimator = null; 206 private FolderIcon mDragOverFolderIcon = null; 207 private boolean mCreateUserFolderOnDrop = false; 208 private boolean mAddToExistingFolderOnDrop = false; 209 private DropTarget.DragEnforcer mDragEnforcer; 210 private float mMaxDistanceForFolderCreation; 211 212 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget) 213 private float mXDown; 214 private float mYDown; 215 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6; 216 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3; 217 final static float TOUCH_SLOP_DAMPING_FACTOR = 4; 218 219 // Relating to the animation of items being dropped externally 220 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0; 221 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1; 222 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2; 223 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3; 224 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4; 225 226 // Related to dragging, folder creation and reordering 227 private static final int DRAG_MODE_NONE = 0; 228 private static final int DRAG_MODE_CREATE_FOLDER = 1; 229 private static final int DRAG_MODE_ADD_TO_FOLDER = 2; 230 private static final int DRAG_MODE_REORDER = 3; 231 private int mDragMode = DRAG_MODE_NONE; 232 private int mLastReorderX = -1; 233 private int mLastReorderY = -1; 234 235 private SparseArray<Parcelable> mSavedStates; 236 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>(); 237 238 // These variables are used for storing the initial and final values during workspace animations 239 private int mSavedScrollX; 240 private float mSavedRotationY; 241 private float mSavedTranslationX; 242 private float mCurrentScaleX; 243 private float mCurrentScaleY; 244 private float mCurrentRotationY; 245 private float mCurrentTranslationX; 246 private float mCurrentTranslationY; 247 private float[] mOldTranslationXs; 248 private float[] mOldTranslationYs; 249 private float[] mOldScaleXs; 250 private float[] mOldScaleYs; 251 private float[] mOldBackgroundAlphas; 252 private float[] mOldAlphas; 253 private float[] mNewTranslationXs; 254 private float[] mNewTranslationYs; 255 private float[] mNewScaleXs; 256 private float[] mNewScaleYs; 257 private float[] mNewBackgroundAlphas; 258 private float[] mNewAlphas; 259 private float[] mNewRotationYs; 260 private int mLastChildCount = -1; 261 private float mTransitionProgress; 262 263 private final Runnable mBindPages = new Runnable() { 264 @Override 265 public void run() { 266 mLauncher.getModel().bindRemainingSynchronousPages(); 267 } 268 }; 269 270 /** 271 * Used to inflate the Workspace from XML. 272 * 273 * @param context The application's context. 274 * @param attrs The attributes set containing the Workspace's customization values. 275 */ 276 public Workspace(Context context, AttributeSet attrs) { 277 this(context, attrs, 0); 278 } 279 280 /** 281 * Used to inflate the Workspace from XML. 282 * 283 * @param context The application's context. 284 * @param attrs The attributes set containing the Workspace's customization values. 285 * @param defStyle Unused. 286 */ 287 public Workspace(Context context, AttributeSet attrs, int defStyle) { 288 super(context, attrs, defStyle); 289 mContentIsRefreshable = false; 290 mOriginalPageSpacing = mPageSpacing; 291 292 mOutlineHelper = HolographicOutlineHelper.obtain(context); 293 294 mDragEnforcer = new DropTarget.DragEnforcer(context); 295 // With workspace, data is available straight from the get-go 296 setDataIsReady(); 297 298 mLauncher = (Launcher) context; 299 final Resources res = getResources(); 300 mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens); 301 mFadeInAdjacentScreens = false; 302 mWallpaperManager = WallpaperManager.getInstance(context); 303 304 int cellCountX = DEFAULT_CELL_COUNT_X; 305 int cellCountY = DEFAULT_CELL_COUNT_Y; 306 307 TypedArray a = context.obtainStyledAttributes(attrs, 308 R.styleable.Workspace, defStyle, 0); 309 310 if (LauncherAppState.getInstance().isScreenLarge()) { 311 // Determine number of rows/columns dynamically 312 // TODO: This code currently fails on tablets with an aspect ratio < 1.3. 313 // Around that ratio we should make cells the same size in portrait and 314 // landscape 315 TypedArray actionBarSizeTypedArray = 316 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize }); 317 final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f); 318 319 Point minDims = new Point(); 320 Point maxDims = new Point(); 321 mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); 322 323 cellCountX = 1; 324 while (CellLayout.widthInPortrait(res, cellCountX + 1) <= minDims.x) { 325 cellCountX++; 326 } 327 328 cellCountY = 1; 329 while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1) 330 <= minDims.y) { 331 cellCountY++; 332 } 333 } 334 335 mSpringLoadedShrinkFactor = 336 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; 337 mSpringLoadedPageSpacing = 338 res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing); 339 mCameraDistance = res.getInteger(R.integer.config_cameraDistance); 340 341 // if the value is manually specified, use that instead 342 cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX); 343 cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY); 344 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); 345 a.recycle(); 346 347 setOnHierarchyChangeListener(this); 348 349 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY); 350 setHapticFeedbackEnabled(false); 351 352 initWorkspace(); 353 354 // Disable multitouch across the workspace/all apps/customize tray 355 setMotionEventSplittingEnabled(true); 356 357 // Unless otherwise specified this view is important for accessibility. 358 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 359 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 360 } 361 } 362 363 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each 364 // dimension if unsuccessful 365 public int[] estimateItemSize(int hSpan, int vSpan, 366 ItemInfo itemInfo, boolean springLoaded) { 367 int[] size = new int[2]; 368 if (getChildCount() > 0) { 369 CellLayout cl = (CellLayout) mLauncher.getWorkspace().getChildAt(0); 370 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan); 371 size[0] = r.width(); 372 size[1] = r.height(); 373 if (springLoaded) { 374 size[0] *= mSpringLoadedShrinkFactor; 375 size[1] *= mSpringLoadedShrinkFactor; 376 } 377 return size; 378 } else { 379 size[0] = Integer.MAX_VALUE; 380 size[1] = Integer.MAX_VALUE; 381 return size; 382 } 383 } 384 385 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo, 386 int hCell, int vCell, int hSpan, int vSpan) { 387 Rect r = new Rect(); 388 cl.cellToRect(hCell, vCell, hSpan, vSpan, r); 389 return r; 390 } 391 392 public void onDragStart(DragSource source, Object info, int dragAction) { 393 mIsDragOccuring = true; 394 updateChildrenLayersEnabled(false); 395 mLauncher.lockScreenOrientation(); 396 setChildrenBackgroundAlphaMultipliers(1f); 397 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging 398 InstallShortcutReceiver.enableInstallQueue(); 399 UninstallShortcutReceiver.enableUninstallQueue(); 400 } 401 402 public void onDragEnd() { 403 mIsDragOccuring = false; 404 updateChildrenLayersEnabled(false); 405 mLauncher.unlockScreenOrientation(false); 406 407 // Re-enable any Un/InstallShortcutReceiver and now process any queued items 408 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); 409 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext()); 410 } 411 412 /** 413 * Initializes various states for this workspace. 414 */ 415 protected void initWorkspace() { 416 Context context = getContext(); 417 mCurrentPage = mDefaultPage; 418 Launcher.setScreen(mCurrentPage); 419 LauncherAppState app = LauncherAppState.getInstance(); 420 mIconCache = app.getIconCache(); 421 setWillNotDraw(false); 422 setClipChildren(false); 423 setClipToPadding(false); 424 setChildrenDrawnWithCacheEnabled(true); 425 426 final Resources res = getResources(); 427 try { 428 mBackground = res.getDrawable(R.drawable.apps_customize_bg); 429 } catch (Resources.NotFoundException e) { 430 // In this case, we will skip drawing background protection 431 } 432 433 mWallpaperOffset = new WallpaperOffsetInterpolator(); 434 Display display = mLauncher.getWindowManager().getDefaultDisplay(); 435 display.getSize(mDisplaySize); 436 mWallpaperTravelWidth = (int) (mDisplaySize.x * 437 wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y)); 438 439 mMaxDistanceForFolderCreation = (0.55f * res.getDimensionPixelSize(R.dimen.app_icon_size)); 440 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); 441 } 442 443 @Override 444 protected int getScrollMode() { 445 return SmoothPagedView.X_LARGE_MODE; 446 } 447 448 @Override 449 public void onChildViewAdded(View parent, View child) { 450 if (!(child instanceof CellLayout)) { 451 throw new IllegalArgumentException("A Workspace can only have CellLayout children."); 452 } 453 CellLayout cl = ((CellLayout) child); 454 cl.setOnInterceptTouchListener(this); 455 cl.setClickable(true); 456 cl.setContentDescription(getContext().getString( 457 R.string.workspace_description_format, getChildCount())); 458 459 super.onChildViewAdded(parent, child); 460 } 461 462 protected boolean shouldDrawChild(View child) { 463 final CellLayout cl = (CellLayout) child; 464 return super.shouldDrawChild(child) && 465 (cl.getShortcutsAndWidgets().getAlpha() > 0 || 466 cl.getBackgroundAlpha() > 0); 467 } 468 469 /** 470 * @return The open folder on the current screen, or null if there is none 471 */ 472 Folder getOpenFolder() { 473 DragLayer dragLayer = mLauncher.getDragLayer(); 474 int count = dragLayer.getChildCount(); 475 for (int i = 0; i < count; i++) { 476 View child = dragLayer.getChildAt(i); 477 if (child instanceof Folder) { 478 Folder folder = (Folder) child; 479 if (folder.getInfo().opened) 480 return folder; 481 } 482 } 483 return null; 484 } 485 486 boolean isTouchActive() { 487 return mTouchState != TOUCH_STATE_REST; 488 } 489 490 public long insertNewWorkspaceScreen(long screenId) { 491 return insertNewWorkspaceScreen(screenId, true); 492 } 493 494 public long insertNewWorkspaceScreenOnBind(long screenId) { 495 return insertNewWorkspaceScreen(screenId, false); 496 } 497 498 public long insertNewWorkspaceScreen(long screenId, boolean updateDb) { 499 CellLayout newScreen = (CellLayout) 500 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null); 501 502 mWorkspaceScreens.put(screenId, newScreen); 503 mScreenOrder.add(screenId); 504 addView(newScreen, getChildCount()); 505 if (updateDb) { 506 // On bind we don't need to update the screens in the database. 507 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder); 508 } 509 return screenId; 510 } 511 512 public void addCustomContentToLeft(View customContent) { 513 CellLayout customScreen = (CellLayout) 514 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null); 515 516 int spanX = customScreen.getCountX(); 517 int spanY = customScreen.getCountY(); 518 519 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY); 520 lp.canReorder = false; 521 522 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true); 523 524 Rect p = new Rect(); 525 AppWidgetHostView.getDefaultPaddingForWidget(mLauncher, mLauncher.getComponentName(), p); 526 customContent.setPadding(p.left, p.top, p.right, p.bottom); 527 528 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen); 529 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID); 530 addView(customScreen, 0); 531 532 533 // Ensure that the current page and default page are maintained. 534 mDefaultPage++; 535 setCurrentPage(getCurrentPage() + 1); 536 } 537 538 public long commitExtraEmptyScreen() { 539 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); 540 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); 541 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); 542 543 long newId = LauncherAppState.getInstance().getLauncherProvider().generateNewScreenId(); 544 mWorkspaceScreens.put(newId, cl); 545 mScreenOrder.add(newId); 546 547 addExtraEmptyScreen(); 548 return newId; 549 } 550 551 public void addExtraEmptyScreen() { 552 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID, false); 553 } 554 555 public CellLayout getScreenWithId(long screenId) { 556 CellLayout layout = mWorkspaceScreens.get(screenId); 557 return layout; 558 } 559 560 public long getIdForScreen(CellLayout layout) { 561 Iterator<Long> iter = mWorkspaceScreens.keySet().iterator(); 562 while (iter.hasNext()) { 563 long id = iter.next(); 564 if (mWorkspaceScreens.get(id) == layout) { 565 return id; 566 } 567 } 568 return -1; 569 } 570 571 public int getPageIndexForScreenId(long screenId) { 572 return indexOfChild(mWorkspaceScreens.get(screenId)); 573 } 574 575 public long getScreenIdForPageIndex(int index) { 576 return mScreenOrder.get(index); 577 } 578 579 ArrayList<Long> getScreenOrder() { 580 return mScreenOrder; 581 } 582 583 public void stripEmptyScreens() { 584 ArrayList<Long> removeScreens = new ArrayList<Long>(); 585 for (Long id: mWorkspaceScreens.keySet()) { 586 CellLayout cl = mWorkspaceScreens.get(id); 587 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) { 588 removeScreens.add(id); 589 } 590 } 591 592 int pageShift = 0; 593 for (Long id: removeScreens) { 594 CellLayout cl = mWorkspaceScreens.get(id); 595 mWorkspaceScreens.remove(id); 596 mScreenOrder.remove(id); 597 if (indexOfChild(cl) < mCurrentPage) { 598 pageShift++; 599 } 600 removeView(cl); 601 } 602 setCurrentPage(mCurrentPage - pageShift); 603 } 604 605 // See implementation for parameter definition. 606 void addInScreen(View child, long container, long screenId, 607 int x, int y, int spanX, int spanY) { 608 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false); 609 } 610 611 // At bind time, we use the rank (screenId) to compute x and y for hotseat items. 612 // See implementation for parameter definition. 613 void addInScreenFromBind(View child, long container, long screenId, int x, int y, 614 int spanX, int spanY) { 615 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true); 616 } 617 618 // See implementation for parameter definition. 619 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY, 620 boolean insert) { 621 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false); 622 } 623 624 /** 625 * Adds the specified child in the specified screen. The position and dimension of 626 * the child are defined by x, y, spanX and spanY. 627 * 628 * @param child The child to add in one of the workspace's screens. 629 * @param screenId The screen in which to add the child. 630 * @param x The X position of the child in the screen's grid. 631 * @param y The Y position of the child in the screen's grid. 632 * @param spanX The number of cells spanned horizontally by the child. 633 * @param spanY The number of cells spanned vertically by the child. 634 * @param insert When true, the child is inserted at the beginning of the children list. 635 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute 636 * the x and y position in which to place hotseat items. Otherwise 637 * we use the x and y position to compute the rank. 638 */ 639 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY, 640 boolean insert, boolean computeXYFromRank) { 641 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 642 if (getScreenWithId(screenId) == null) { 643 Log.e(TAG, "Skipping child, screenId " + screenId + " not found"); 644 return; 645 } 646 } 647 648 // If an item is added to the extra empty screen, we convert it to a real 649 if (screenId == EXTRA_EMPTY_SCREEN_ID) { 650 screenId = commitExtraEmptyScreen(); 651 } 652 653 final CellLayout layout; 654 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 655 layout = mLauncher.getHotseat().getLayout(); 656 child.setOnKeyListener(null); 657 658 // Hide folder title in the hotseat 659 if (child instanceof FolderIcon) { 660 ((FolderIcon) child).setTextVisible(false); 661 } 662 663 if (computeXYFromRank) { 664 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId); 665 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId); 666 } else { 667 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y); 668 } 669 } else { 670 // Show folder title if not in the hotseat 671 if (child instanceof FolderIcon) { 672 ((FolderIcon) child).setTextVisible(true); 673 } 674 layout = getScreenWithId(screenId); 675 child.setOnKeyListener(new IconKeyEventListener()); 676 } 677 678 LayoutParams genericLp = child.getLayoutParams(); 679 CellLayout.LayoutParams lp; 680 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) { 681 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 682 } else { 683 lp = (CellLayout.LayoutParams) genericLp; 684 lp.cellX = x; 685 lp.cellY = y; 686 lp.cellHSpan = spanX; 687 lp.cellVSpan = spanY; 688 } 689 690 if (spanX < 0 && spanY < 0) { 691 lp.isLockedToGrid = false; 692 } 693 694 // Get the canonical child id to uniquely represent this view in this screen 695 int childId = LauncherModel.getCellLayoutChildId(container, screenId, x, y, spanX, spanY); 696 boolean markCellsAsOccupied = !(child instanceof Folder); 697 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) { 698 // TODO: This branch occurs when the workspace is adding views 699 // outside of the defined grid 700 // maybe we should be deleting these items from the LauncherModel? 701 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); 702 } 703 704 if (!(child instanceof Folder)) { 705 child.setHapticFeedbackEnabled(false); 706 child.setOnLongClickListener(mLongClickListener); 707 } 708 if (child instanceof DropTarget) { 709 mDragController.addDropTarget((DropTarget) child); 710 } 711 } 712 713 /** 714 * Check if the point (x, y) hits a given page. 715 */ 716 private boolean hitsPage(int index, float x, float y) { 717 final View page = getChildAt(index); 718 if (page != null) { 719 float[] localXY = { x, y }; 720 mapPointFromSelfToChild(page, localXY); 721 return (localXY[0] >= 0 && localXY[0] < page.getWidth() 722 && localXY[1] >= 0 && localXY[1] < page.getHeight()); 723 } 724 return false; 725 } 726 727 @Override 728 protected boolean hitsPreviousPage(float x, float y) { 729 // mNextPage is set to INVALID_PAGE whenever we are stationary. 730 // Calculating "next page" this way ensures that you scroll to whatever page you tap on 731 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage; 732 733 // Only allow tap to next page on large devices, where there's significant margin outside 734 // the active workspace 735 return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current - 1, x, y); 736 } 737 738 @Override 739 protected boolean hitsNextPage(float x, float y) { 740 // mNextPage is set to INVALID_PAGE whenever we are stationary. 741 // Calculating "next page" this way ensures that you scroll to whatever page you tap on 742 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage; 743 744 // Only allow tap to next page on large devices, where there's significant margin outside 745 // the active workspace 746 return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current + 1, x, y); 747 } 748 749 /** 750 * Called directly from a CellLayout (not by the framework), after we've been added as a 751 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout 752 * that it should intercept touch events, which is not something that is normally supported. 753 */ 754 @Override 755 public boolean onTouch(View v, MotionEvent event) { 756 return (isSmall() || !isFinishedSwitchingState()); 757 } 758 759 public boolean isSwitchingState() { 760 return mIsSwitchingState; 761 } 762 763 /** This differs from isSwitchingState in that we take into account how far the transition 764 * has completed. */ 765 public boolean isFinishedSwitchingState() { 766 return !mIsSwitchingState || (mTransitionProgress > 0.5f); 767 } 768 769 protected void onWindowVisibilityChanged (int visibility) { 770 mLauncher.onWindowVisibilityChanged(visibility); 771 } 772 773 @Override 774 public boolean dispatchUnhandledMove(View focused, int direction) { 775 if (isSmall() || !isFinishedSwitchingState()) { 776 // when the home screens are shrunken, shouldn't allow side-scrolling 777 return false; 778 } 779 return super.dispatchUnhandledMove(focused, direction); 780 } 781 782 @Override 783 public boolean onInterceptTouchEvent(MotionEvent ev) { 784 switch (ev.getAction() & MotionEvent.ACTION_MASK) { 785 case MotionEvent.ACTION_DOWN: 786 mXDown = ev.getX(); 787 mYDown = ev.getY(); 788 break; 789 case MotionEvent.ACTION_POINTER_UP: 790 case MotionEvent.ACTION_UP: 791 if (mTouchState == TOUCH_STATE_REST) { 792 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage); 793 if (!currentPage.lastDownOnOccupiedCell()) { 794 onWallpaperTap(ev); 795 } 796 } 797 } 798 799 if (mLauncher != null && mLauncher.onTouch(this, ev)) { 800 return true; 801 } 802 803 return super.onInterceptTouchEvent(ev); 804 } 805 806 protected void reinflateWidgetsIfNecessary() { 807 final int clCount = getChildCount(); 808 for (int i = 0; i < clCount; i++) { 809 CellLayout cl = (CellLayout) getChildAt(i); 810 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets(); 811 final int itemCount = swc.getChildCount(); 812 for (int j = 0; j < itemCount; j++) { 813 View v = swc.getChildAt(j); 814 815 if (v.getTag() instanceof LauncherAppWidgetInfo) { 816 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag(); 817 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView; 818 if (lahv != null && lahv.orientationChangedSincedInflation()) { 819 mLauncher.removeAppWidget(info); 820 // Remove the current widget which is inflated with the wrong orientation 821 cl.removeView(lahv); 822 mLauncher.bindAppWidget(info); 823 } 824 } 825 } 826 } 827 } 828 829 @Override 830 protected void determineScrollingStart(MotionEvent ev) { 831 if (isSmall()) return; 832 if (!isFinishedSwitchingState()) return; 833 834 float deltaX = Math.abs(ev.getX() - mXDown); 835 float deltaY = Math.abs(ev.getY() - mYDown); 836 837 if (Float.compare(deltaX, 0f) == 0) return; 838 839 float slope = deltaY / deltaX; 840 float theta = (float) Math.atan(slope); 841 842 if (deltaX > mTouchSlop || deltaY > mTouchSlop) { 843 cancelCurrentPageLongPress(); 844 } 845 846 if (theta > MAX_SWIPE_ANGLE) { 847 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace 848 return; 849 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) { 850 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to 851 // increase the touch slop to make it harder to begin scrolling the workspace. This 852 // results in vertically scrolling widgets to more easily. The higher the angle, the 853 // more we increase touch slop. 854 theta -= START_DAMPING_TOUCH_SLOP_ANGLE; 855 float extraRatio = (float) 856 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE))); 857 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio); 858 } else { 859 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special 860 super.determineScrollingStart(ev); 861 } 862 } 863 864 protected void onPageBeginMoving() { 865 super.onPageBeginMoving(); 866 867 if (isHardwareAccelerated()) { 868 updateChildrenLayersEnabled(false); 869 } else { 870 if (mNextPage != INVALID_PAGE) { 871 // we're snapping to a particular screen 872 enableChildrenCache(mCurrentPage, mNextPage); 873 } else { 874 // this is when user is actively dragging a particular screen, they might 875 // swipe it either left or right (but we won't advance by more than one screen) 876 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); 877 } 878 } 879 880 // Only show page outlines as we pan if we are on large screen 881 if (LauncherAppState.getInstance().isScreenLarge()) { 882 showOutlines(); 883 mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null; 884 } 885 886 // If we are not fading in adjacent screens, we still need to restore the alpha in case the 887 // user scrolls while we are transitioning (should not affect dispatchDraw optimizations) 888 if (!mWorkspaceFadeInAdjacentScreens) { 889 for (int i = 0; i < getChildCount(); ++i) { 890 ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f); 891 } 892 } 893 894 // Show the scroll indicator as you pan the page 895 showScrollingIndicator(false); 896 } 897 898 protected void onPageEndMoving() { 899 super.onPageEndMoving(); 900 901 if (isHardwareAccelerated()) { 902 updateChildrenLayersEnabled(false); 903 } else { 904 clearChildrenCache(); 905 } 906 907 908 if (mDragController.isDragging()) { 909 if (isSmall()) { 910 // If we are in springloaded mode, then force an event to check if the current touch 911 // is under a new page (to scroll to) 912 mDragController.forceTouchMove(); 913 } 914 } else { 915 // If we are not mid-dragging, hide the page outlines if we are on a large screen 916 if (LauncherAppState.getInstance().isScreenLarge()) { 917 hideOutlines(); 918 } 919 920 // Hide the scroll indicator as you pan the page 921 if (!mDragController.isDragging()) { 922 hideScrollingIndicator(false); 923 } 924 } 925 926 if (mDelayedResizeRunnable != null) { 927 mDelayedResizeRunnable.run(); 928 mDelayedResizeRunnable = null; 929 } 930 931 if (mDelayedSnapToPageRunnable != null) { 932 mDelayedSnapToPageRunnable.run(); 933 mDelayedSnapToPageRunnable = null; 934 } 935 } 936 937 @Override 938 protected void notifyPageSwitchListener() { 939 super.notifyPageSwitchListener(); 940 Launcher.setScreen(mCurrentPage); 941 }; 942 943 // As a ratio of screen height, the total distance we want the parallax effect to span 944 // horizontally 945 private float wallpaperTravelToScreenWidthRatio(int width, int height) { 946 float aspectRatio = width / (float) height; 947 948 // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width 949 // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width 950 // We will use these two data points to extrapolate how much the wallpaper parallax effect 951 // to span (ie travel) at any aspect ratio: 952 953 final float ASPECT_RATIO_LANDSCAPE = 16/10f; 954 final float ASPECT_RATIO_PORTRAIT = 10/16f; 955 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; 956 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; 957 958 // To find out the desired width at different aspect ratios, we use the following two 959 // formulas, where the coefficient on x is the aspect ratio (width/height): 960 // (16/10)x + y = 1.5 961 // (10/16)x + y = 1.2 962 // We solve for x and y and end up with a final formula: 963 final float x = 964 (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / 965 (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); 966 final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; 967 return x * aspectRatio + y; 968 } 969 970 // The range of scroll values for Workspace 971 private int getScrollRange() { 972 return getChildOffset(getChildCount() - 1) - getChildOffset(0); 973 } 974 975 protected void setWallpaperDimension() { 976 Point minDims = new Point(); 977 Point maxDims = new Point(); 978 mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); 979 980 final int maxDim = Math.max(maxDims.x, maxDims.y); 981 final int minDim = Math.min(minDims.x, minDims.y); 982 983 // We need to ensure that there is enough extra space in the wallpaper for the intended 984 // parallax effects 985 if (LauncherAppState.getInstance().isScreenLarge()) { 986 mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 987 mWallpaperHeight = maxDim; 988 } else { 989 mWallpaperWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 990 mWallpaperHeight = maxDim; 991 } 992 new Thread("setWallpaperDimension") { 993 public void run() { 994 mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight); 995 } 996 }.start(); 997 } 998 999 private float wallpaperOffsetForCurrentScroll() { 1000 // Set wallpaper offset steps (1 / (number of screens - 1)) 1001 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f); 1002 1003 // For the purposes of computing the scrollRange and overScrollOffset, we assume 1004 // that mLayoutScale is 1. This means that when we're in spring-loaded mode, 1005 // there's no discrepancy between the wallpaper offset for a given page. 1006 float layoutScale = mLayoutScale; 1007 mLayoutScale = 1f; 1008 int scrollRange = getScrollRange(); 1009 1010 // Again, we adjust the wallpaper offset to be consistent between values of mLayoutScale 1011 float adjustedScrollX = Math.max(0, Math.min(getScrollX(), mMaxScrollX)); 1012 adjustedScrollX *= mWallpaperScrollRatio; 1013 mLayoutScale = layoutScale; 1014 1015 float scrollProgress = 1016 adjustedScrollX / (float) scrollRange; 1017 1018 if (LauncherAppState.getInstance().isScreenLarge() && mIsStaticWallpaper) { 1019 // The wallpaper travel width is how far, from left to right, the wallpaper will move 1020 // at this orientation. On tablets in portrait mode we don't move all the way to the 1021 // edges of the wallpaper, or otherwise the parallax effect would be too strong. 1022 int wallpaperTravelWidth = Math.min(mWallpaperTravelWidth, mWallpaperWidth); 1023 1024 float offsetInDips = wallpaperTravelWidth * scrollProgress + 1025 (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it 1026 float offset = offsetInDips / (float) mWallpaperWidth; 1027 return offset; 1028 } else { 1029 return scrollProgress; 1030 } 1031 } 1032 1033 private void syncWallpaperOffsetWithScroll() { 1034 final boolean enableWallpaperEffects = isHardwareAccelerated(); 1035 if (enableWallpaperEffects) { 1036 // TODO: figure out what to do about parallax, for now disable it 1037 //mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll()); 1038 } 1039 } 1040 1041 public void updateWallpaperOffsetImmediately() { 1042 mUpdateWallpaperOffsetImmediately = true; 1043 } 1044 1045 private void updateWallpaperOffsets() { 1046 boolean updateNow = false; 1047 boolean keepUpdating = true; 1048 if (mUpdateWallpaperOffsetImmediately) { 1049 updateNow = true; 1050 keepUpdating = false; 1051 mWallpaperOffset.jumpToFinal(); 1052 mUpdateWallpaperOffsetImmediately = false; 1053 } else { 1054 updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset(); 1055 } 1056 if (updateNow) { 1057 if (mWindowToken != null) { 1058 mWallpaperManager.setWallpaperOffsets(mWindowToken, 1059 mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY()); 1060 } 1061 } 1062 if (keepUpdating) { 1063 invalidate(); 1064 } 1065 } 1066 1067 @Override 1068 protected void updateCurrentPageScroll() { 1069 super.updateCurrentPageScroll(); 1070 computeWallpaperScrollRatio(mCurrentPage); 1071 } 1072 1073 @Override 1074 protected void snapToPage(int whichPage) { 1075 super.snapToPage(whichPage); 1076 computeWallpaperScrollRatio(whichPage); 1077 } 1078 1079 @Override 1080 protected void snapToPage(int whichPage, int duration) { 1081 super.snapToPage(whichPage, duration); 1082 computeWallpaperScrollRatio(whichPage); 1083 } 1084 1085 protected void snapToPage(int whichPage, Runnable r) { 1086 if (mDelayedSnapToPageRunnable != null) { 1087 mDelayedSnapToPageRunnable.run(); 1088 } 1089 mDelayedSnapToPageRunnable = r; 1090 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION); 1091 } 1092 1093 protected void snapToScreenId(long screenId, Runnable r) { 1094 snapToPage(getPageIndexForScreenId(screenId), r); 1095 } 1096 1097 private void computeWallpaperScrollRatio(int page) { 1098 // Here, we determine what the desired scroll would be with and without a layout scale, 1099 // and compute a ratio between the two. This allows us to adjust the wallpaper offset 1100 // as though there is no layout scale. 1101 float layoutScale = mLayoutScale; 1102 int scaled = getChildOffset(page) - getRelativeChildOffset(page); 1103 mLayoutScale = 1.0f; 1104 float unscaled = getChildOffset(page) - getRelativeChildOffset(page); 1105 mLayoutScale = layoutScale; 1106 if (scaled > 0) { 1107 mWallpaperScrollRatio = (1.0f * unscaled) / scaled; 1108 } else { 1109 mWallpaperScrollRatio = 1f; 1110 } 1111 } 1112 1113 class WallpaperOffsetInterpolator { 1114 float mFinalHorizontalWallpaperOffset = 0.0f; 1115 float mFinalVerticalWallpaperOffset = 0.5f; 1116 float mHorizontalWallpaperOffset = 0.0f; 1117 float mVerticalWallpaperOffset = 0.5f; 1118 long mLastWallpaperOffsetUpdateTime; 1119 boolean mIsMovingFast; 1120 boolean mOverrideHorizontalCatchupConstant; 1121 float mHorizontalCatchupConstant = 0.35f; 1122 float mVerticalCatchupConstant = 0.35f; 1123 1124 public WallpaperOffsetInterpolator() { 1125 } 1126 1127 public void setOverrideHorizontalCatchupConstant(boolean override) { 1128 mOverrideHorizontalCatchupConstant = override; 1129 } 1130 1131 public void setHorizontalCatchupConstant(float f) { 1132 mHorizontalCatchupConstant = f; 1133 } 1134 1135 public void setVerticalCatchupConstant(float f) { 1136 mVerticalCatchupConstant = f; 1137 } 1138 1139 public boolean computeScrollOffset() { 1140 if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 && 1141 Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) { 1142 mIsMovingFast = false; 1143 return false; 1144 } 1145 boolean isLandscape = mDisplaySize.x > mDisplaySize.y; 1146 1147 long currentTime = System.currentTimeMillis(); 1148 long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime; 1149 timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate); 1150 timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate); 1151 1152 float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset); 1153 if (!mIsMovingFast && xdiff > 0.07) { 1154 mIsMovingFast = true; 1155 } 1156 1157 float fractionToCatchUpIn1MsHorizontal; 1158 if (mOverrideHorizontalCatchupConstant) { 1159 fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant; 1160 } else if (mIsMovingFast) { 1161 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f; 1162 } else { 1163 // slow 1164 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f; 1165 } 1166 float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant; 1167 1168 fractionToCatchUpIn1MsHorizontal /= 33f; 1169 fractionToCatchUpIn1MsVertical /= 33f; 1170 1171 final float UPDATE_THRESHOLD = 0.00001f; 1172 float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset; 1173 float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset; 1174 boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD && 1175 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD; 1176 1177 // Don't have any lag between workspace and wallpaper on non-large devices 1178 if (!LauncherAppState.getInstance().isScreenLarge() || jumpToFinalValue) { 1179 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset; 1180 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset; 1181 } else { 1182 float percentToCatchUpVertical = 1183 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical); 1184 float percentToCatchUpHorizontal = 1185 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal); 1186 mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta; 1187 mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta; 1188 } 1189 1190 mLastWallpaperOffsetUpdateTime = System.currentTimeMillis(); 1191 return true; 1192 } 1193 1194 public float getCurrX() { 1195 return mHorizontalWallpaperOffset; 1196 } 1197 1198 public float getFinalX() { 1199 return mFinalHorizontalWallpaperOffset; 1200 } 1201 1202 public float getCurrY() { 1203 return mVerticalWallpaperOffset; 1204 } 1205 1206 public float getFinalY() { 1207 return mFinalVerticalWallpaperOffset; 1208 } 1209 1210 public void setFinalX(float x) { 1211 mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f)); 1212 } 1213 1214 public void setFinalY(float y) { 1215 mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f)); 1216 } 1217 1218 public void jumpToFinal() { 1219 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset; 1220 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset; 1221 } 1222 } 1223 1224 @Override 1225 public void computeScroll() { 1226 super.computeScroll(); 1227 syncWallpaperOffsetWithScroll(); 1228 } 1229 1230 void showOutlines() { 1231 if (!isSmall() && !mIsSwitchingState) { 1232 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 1233 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 1234 mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f); 1235 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION); 1236 mChildrenOutlineFadeInAnimation.start(); 1237 } 1238 } 1239 1240 void hideOutlines() { 1241 if (!isSmall() && !mIsSwitchingState) { 1242 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 1243 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 1244 mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f); 1245 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION); 1246 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY); 1247 mChildrenOutlineFadeOutAnimation.start(); 1248 } 1249 } 1250 1251 public void showOutlinesTemporarily() { 1252 if (!mIsPageMoving && !isTouchActive()) { 1253 snapToPage(mCurrentPage); 1254 } 1255 } 1256 1257 public void setChildrenOutlineAlpha(float alpha) { 1258 mChildrenOutlineAlpha = alpha; 1259 for (int i = 0; i < getChildCount(); i++) { 1260 CellLayout cl = (CellLayout) getChildAt(i); 1261 cl.setBackgroundAlpha(alpha); 1262 } 1263 } 1264 1265 public float getChildrenOutlineAlpha() { 1266 return mChildrenOutlineAlpha; 1267 } 1268 1269 void disableBackground() { 1270 mDrawBackground = false; 1271 } 1272 void enableBackground() { 1273 mDrawBackground = true; 1274 } 1275 1276 private void animateBackgroundGradient(float finalAlpha, boolean animated) { 1277 if (mBackground == null) return; 1278 if (mBackgroundFadeInAnimation != null) { 1279 mBackgroundFadeInAnimation.cancel(); 1280 mBackgroundFadeInAnimation = null; 1281 } 1282 if (mBackgroundFadeOutAnimation != null) { 1283 mBackgroundFadeOutAnimation.cancel(); 1284 mBackgroundFadeOutAnimation = null; 1285 } 1286 float startAlpha = getBackgroundAlpha(); 1287 if (finalAlpha != startAlpha) { 1288 if (animated) { 1289 mBackgroundFadeOutAnimation = 1290 LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha); 1291 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() { 1292 public void onAnimationUpdate(ValueAnimator animation) { 1293 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue()); 1294 } 1295 }); 1296 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); 1297 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); 1298 mBackgroundFadeOutAnimation.start(); 1299 } else { 1300 setBackgroundAlpha(finalAlpha); 1301 } 1302 } 1303 } 1304 1305 public void setBackgroundAlpha(float alpha) { 1306 if (alpha != mBackgroundAlpha) { 1307 mBackgroundAlpha = alpha; 1308 invalidate(); 1309 } 1310 } 1311 1312 public float getBackgroundAlpha() { 1313 return mBackgroundAlpha; 1314 } 1315 1316 float backgroundAlphaInterpolator(float r) { 1317 float pivotA = 0.1f; 1318 float pivotB = 0.4f; 1319 if (r < pivotA) { 1320 return 0; 1321 } else if (r > pivotB) { 1322 return 1.0f; 1323 } else { 1324 return (r - pivotA)/(pivotB - pivotA); 1325 } 1326 } 1327 1328 private void updatePageAlphaValues(int screenCenter) { 1329 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; 1330 if (mWorkspaceFadeInAdjacentScreens && 1331 mState == State.NORMAL && 1332 !mIsSwitchingState && 1333 !isInOverscroll) { 1334 for (int i = 0; i < getChildCount(); i++) { 1335 CellLayout child = (CellLayout) getChildAt(i); 1336 if (child != null) { 1337 float scrollProgress = getScrollProgress(screenCenter, child, i); 1338 float alpha = 1 - Math.abs(scrollProgress); 1339 child.getShortcutsAndWidgets().setAlpha(alpha); 1340 if (!mIsDragOccuring) { 1341 child.setBackgroundAlphaMultiplier( 1342 backgroundAlphaInterpolator(Math.abs(scrollProgress))); 1343 } else { 1344 child.setBackgroundAlphaMultiplier(1f); 1345 } 1346 } 1347 } 1348 } 1349 } 1350 1351 private void setChildrenBackgroundAlphaMultipliers(float a) { 1352 for (int i = 0; i < getChildCount(); i++) { 1353 CellLayout child = (CellLayout) getChildAt(i); 1354 child.setBackgroundAlphaMultiplier(a); 1355 } 1356 } 1357 1358 @Override 1359 protected void screenScrolled(int screenCenter) { 1360 final boolean isRtl = isLayoutRtl(); 1361 super.screenScrolled(screenCenter); 1362 1363 updatePageAlphaValues(screenCenter); 1364 enableHwLayersOnVisiblePages(); 1365 1366 if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) { 1367 int index = 0; 1368 float pivotX = 0f; 1369 final float leftBiasedPivot = 0.25f; 1370 final float rightBiasedPivot = 0.75f; 1371 final int lowerIndex = 0; 1372 final int upperIndex = getChildCount() - 1; 1373 if (isRtl) { 1374 index = mOverScrollX < 0 ? upperIndex : lowerIndex; 1375 pivotX = (index == 0 ? leftBiasedPivot : rightBiasedPivot); 1376 } else { 1377 index = mOverScrollX < 0 ? lowerIndex : upperIndex; 1378 pivotX = (index == 0 ? rightBiasedPivot : leftBiasedPivot); 1379 } 1380 1381 CellLayout cl = (CellLayout) getChildAt(index); 1382 float scrollProgress = getScrollProgress(screenCenter, cl, index); 1383 final boolean isLeftPage = (isRtl ? index > 0 : index == 0); 1384 cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage); 1385 float rotation = -WORKSPACE_OVERSCROLL_ROTATION * scrollProgress; 1386 cl.setRotationY(rotation); 1387 setFadeForOverScroll(Math.abs(scrollProgress)); 1388 if (!mOverscrollTransformsSet) { 1389 mOverscrollTransformsSet = true; 1390 cl.setCameraDistance(mDensity * mCameraDistance); 1391 cl.setPivotX(cl.getMeasuredWidth() * pivotX); 1392 cl.setPivotY(cl.getMeasuredHeight() * 0.5f); 1393 cl.setOverscrollTransformsDirty(true); 1394 } 1395 } else { 1396 if (mOverscrollFade != 0) { 1397 setFadeForOverScroll(0); 1398 } 1399 if (mOverscrollTransformsSet) { 1400 mOverscrollTransformsSet = false; 1401 ((CellLayout) getChildAt(0)).resetOverscrollTransforms(); 1402 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms(); 1403 } 1404 } 1405 } 1406 1407 @Override 1408 protected void overScroll(float amount) { 1409 acceleratedOverScroll(amount); 1410 } 1411 1412 protected void onAttachedToWindow() { 1413 super.onAttachedToWindow(); 1414 mWindowToken = getWindowToken(); 1415 computeScroll(); 1416 mDragController.setWindowToken(mWindowToken); 1417 } 1418 1419 protected void onDetachedFromWindow() { 1420 mWindowToken = null; 1421 } 1422 1423 @Override 1424 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 1425 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { 1426 mUpdateWallpaperOffsetImmediately = true; 1427 } 1428 super.onLayout(changed, left, top, right, bottom); 1429 } 1430 1431 @Override 1432 protected void onDraw(Canvas canvas) { 1433 updateWallpaperOffsets(); 1434 1435 // Draw the background gradient if necessary 1436 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) { 1437 int alpha = (int) (mBackgroundAlpha * 255); 1438 mBackground.setAlpha(alpha); 1439 mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(), 1440 getMeasuredHeight()); 1441 mBackground.draw(canvas); 1442 } 1443 1444 super.onDraw(canvas); 1445 1446 // Call back to LauncherModel to finish binding after the first draw 1447 post(mBindPages); 1448 } 1449 1450 boolean isDrawingBackgroundGradient() { 1451 return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground); 1452 } 1453 1454 @Override 1455 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 1456 if (!mLauncher.isAllAppsVisible()) { 1457 final Folder openFolder = getOpenFolder(); 1458 if (openFolder != null) { 1459 return openFolder.requestFocus(direction, previouslyFocusedRect); 1460 } else { 1461 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 1462 } 1463 } 1464 return false; 1465 } 1466 1467 @Override 1468 public int getDescendantFocusability() { 1469 if (isSmall()) { 1470 return ViewGroup.FOCUS_BLOCK_DESCENDANTS; 1471 } 1472 return super.getDescendantFocusability(); 1473 } 1474 1475 @Override 1476 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { 1477 if (!mLauncher.isAllAppsVisible()) { 1478 final Folder openFolder = getOpenFolder(); 1479 if (openFolder != null) { 1480 openFolder.addFocusables(views, direction); 1481 } else { 1482 super.addFocusables(views, direction, focusableMode); 1483 } 1484 } 1485 } 1486 1487 public boolean isSmall() { 1488 return mState == State.SMALL || mState == State.SPRING_LOADED; 1489 } 1490 1491 void enableChildrenCache(int fromPage, int toPage) { 1492 if (fromPage > toPage) { 1493 final int temp = fromPage; 1494 fromPage = toPage; 1495 toPage = temp; 1496 } 1497 1498 final int screenCount = getChildCount(); 1499 1500 fromPage = Math.max(fromPage, 0); 1501 toPage = Math.min(toPage, screenCount - 1); 1502 1503 for (int i = fromPage; i <= toPage; i++) { 1504 final CellLayout layout = (CellLayout) getChildAt(i); 1505 layout.setChildrenDrawnWithCacheEnabled(true); 1506 layout.setChildrenDrawingCacheEnabled(true); 1507 } 1508 } 1509 1510 void clearChildrenCache() { 1511 final int screenCount = getChildCount(); 1512 for (int i = 0; i < screenCount; i++) { 1513 final CellLayout layout = (CellLayout) getChildAt(i); 1514 layout.setChildrenDrawnWithCacheEnabled(false); 1515 // In software mode, we don't want the items to continue to be drawn into bitmaps 1516 if (!isHardwareAccelerated()) { 1517 layout.setChildrenDrawingCacheEnabled(false); 1518 } 1519 } 1520 } 1521 1522 1523 private void updateChildrenLayersEnabled(boolean force) { 1524 boolean small = mState == State.SMALL || mIsSwitchingState; 1525 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving(); 1526 1527 if (enableChildrenLayers != mChildrenLayersEnabled) { 1528 mChildrenLayersEnabled = enableChildrenLayers; 1529 if (mChildrenLayersEnabled) { 1530 enableHwLayersOnVisiblePages(); 1531 } else { 1532 for (int i = 0; i < getPageCount(); i++) { 1533 final CellLayout cl = (CellLayout) getChildAt(i); 1534 cl.disableHardwareLayers(); 1535 } 1536 } 1537 } 1538 } 1539 1540 private void enableHwLayersOnVisiblePages() { 1541 if (mChildrenLayersEnabled) { 1542 final int screenCount = getChildCount(); 1543 getVisiblePages(mTempVisiblePagesRange); 1544 int leftScreen = mTempVisiblePagesRange[0]; 1545 int rightScreen = mTempVisiblePagesRange[1]; 1546 if (leftScreen == rightScreen) { 1547 // make sure we're caching at least two pages always 1548 if (rightScreen < screenCount - 1) { 1549 rightScreen++; 1550 } else if (leftScreen > 0) { 1551 leftScreen--; 1552 } 1553 } 1554 for (int i = 0; i < screenCount; i++) { 1555 final CellLayout layout = (CellLayout) getPageAt(i); 1556 if (!(leftScreen <= i && i <= rightScreen && shouldDrawChild(layout))) { 1557 layout.disableHardwareLayers(); 1558 } 1559 } 1560 for (int i = 0; i < screenCount; i++) { 1561 final CellLayout layout = (CellLayout) getPageAt(i); 1562 if (leftScreen <= i && i <= rightScreen && shouldDrawChild(layout)) { 1563 layout.enableHardwareLayers(); 1564 } 1565 } 1566 } 1567 } 1568 1569 public void buildPageHardwareLayers() { 1570 // force layers to be enabled just for the call to buildLayer 1571 updateChildrenLayersEnabled(true); 1572 if (getWindowToken() != null) { 1573 final int childCount = getChildCount(); 1574 for (int i = 0; i < childCount; i++) { 1575 CellLayout cl = (CellLayout) getChildAt(i); 1576 cl.buildHardwareLayer(); 1577 } 1578 } 1579 updateChildrenLayersEnabled(false); 1580 } 1581 1582 protected void onWallpaperTap(MotionEvent ev) { 1583 final int[] position = mTempCell; 1584 getLocationOnScreen(position); 1585 1586 int pointerIndex = ev.getActionIndex(); 1587 position[0] += (int) ev.getX(pointerIndex); 1588 position[1] += (int) ev.getY(pointerIndex); 1589 1590 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 1591 ev.getAction() == MotionEvent.ACTION_UP 1592 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP, 1593 position[0], position[1], 0, null); 1594 } 1595 1596 /* 1597 * This interpolator emulates the rate at which the perceived scale of an object changes 1598 * as its distance from a camera increases. When this interpolator is applied to a scale 1599 * animation on a view, it evokes the sense that the object is shrinking due to moving away 1600 * from the camera. 1601 */ 1602 static class ZInterpolator implements TimeInterpolator { 1603 private float focalLength; 1604 1605 public ZInterpolator(float foc) { 1606 focalLength = foc; 1607 } 1608 1609 public float getInterpolation(float input) { 1610 return (1.0f - focalLength / (focalLength + input)) / 1611 (1.0f - focalLength / (focalLength + 1.0f)); 1612 } 1613 } 1614 1615 /* 1616 * The exact reverse of ZInterpolator. 1617 */ 1618 static class InverseZInterpolator implements TimeInterpolator { 1619 private ZInterpolator zInterpolator; 1620 public InverseZInterpolator(float foc) { 1621 zInterpolator = new ZInterpolator(foc); 1622 } 1623 public float getInterpolation(float input) { 1624 return 1 - zInterpolator.getInterpolation(1 - input); 1625 } 1626 } 1627 1628 /* 1629 * ZInterpolator compounded with an ease-out. 1630 */ 1631 static class ZoomOutInterpolator implements TimeInterpolator { 1632 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f); 1633 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f); 1634 1635 public float getInterpolation(float input) { 1636 return decelerate.getInterpolation(zInterpolator.getInterpolation(input)); 1637 } 1638 } 1639 1640 /* 1641 * InvereZInterpolator compounded with an ease-out. 1642 */ 1643 static class ZoomInInterpolator implements TimeInterpolator { 1644 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f); 1645 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f); 1646 1647 public float getInterpolation(float input) { 1648 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input)); 1649 } 1650 } 1651 1652 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator(); 1653 1654 /* 1655 * 1656 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we 1657 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace 1658 * 1659 * These methods mark the appropriate pages as accepting drops (which alters their visual 1660 * appearance). 1661 * 1662 */ 1663 public void onDragStartedWithItem(View v) { 1664 final Canvas canvas = new Canvas(); 1665 1666 // The outline is used to visualize where the item will land if dropped 1667 mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING); 1668 } 1669 1670 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) { 1671 final Canvas canvas = new Canvas(); 1672 1673 int[] size = estimateItemSize(info.spanX, info.spanY, info, false); 1674 1675 // The outline is used to visualize where the item will land if dropped 1676 mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0], 1677 size[1], clipAlpha); 1678 } 1679 1680 public void exitWidgetResizeMode() { 1681 DragLayer dragLayer = mLauncher.getDragLayer(); 1682 dragLayer.clearAllResizeFrames(); 1683 } 1684 1685 private void initAnimationArrays() { 1686 final int childCount = getChildCount(); 1687 if (mLastChildCount == childCount) return; 1688 mOldTranslationXs = new float[childCount]; 1689 mOldTranslationYs = new float[childCount]; 1690 mOldScaleXs = new float[childCount]; 1691 mOldScaleYs = new float[childCount]; 1692 mOldBackgroundAlphas = new float[childCount]; 1693 mOldAlphas = new float[childCount]; 1694 mNewTranslationXs = new float[childCount]; 1695 mNewTranslationYs = new float[childCount]; 1696 mNewScaleXs = new float[childCount]; 1697 mNewScaleYs = new float[childCount]; 1698 mNewBackgroundAlphas = new float[childCount]; 1699 mNewAlphas = new float[childCount]; 1700 mNewRotationYs = new float[childCount]; 1701 } 1702 1703 Animator getChangeStateAnimation(final State state, boolean animated) { 1704 return getChangeStateAnimation(state, animated, 0); 1705 } 1706 1707 Animator getChangeStateAnimation(final State state, boolean animated, int delay) { 1708 if (mState == state) { 1709 return null; 1710 } 1711 1712 // Initialize animation arrays for the first time if necessary 1713 initAnimationArrays(); 1714 1715 AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null; 1716 1717 // Stop any scrolling, move to the current page right away 1718 setCurrentPage(getNextPage()); 1719 1720 final State oldState = mState; 1721 final boolean oldStateIsNormal = (oldState == State.NORMAL); 1722 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED); 1723 final boolean oldStateIsSmall = (oldState == State.SMALL); 1724 mState = state; 1725 final boolean stateIsNormal = (state == State.NORMAL); 1726 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED); 1727 final boolean stateIsSmall = (state == State.SMALL); 1728 float finalScaleFactor = 1.0f; 1729 float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f; 1730 float translationX = 0; 1731 float translationY = 0; 1732 boolean zoomIn = true; 1733 1734 if (state != State.NORMAL) { 1735 finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0); 1736 setPageSpacing(mSpringLoadedPageSpacing); 1737 if (oldStateIsNormal && stateIsSmall) { 1738 zoomIn = false; 1739 setLayoutScale(finalScaleFactor); 1740 updateChildrenLayersEnabled(false); 1741 } else { 1742 finalBackgroundAlpha = 1.0f; 1743 setLayoutScale(finalScaleFactor); 1744 } 1745 } else { 1746 setPageSpacing(mOriginalPageSpacing); 1747 setLayoutScale(1.0f); 1748 } 1749 1750 final int duration = zoomIn ? 1751 getResources().getInteger(R.integer.config_workspaceUnshrinkTime) : 1752 getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime); 1753 for (int i = 0; i < getChildCount(); i++) { 1754 final CellLayout cl = (CellLayout) getChildAt(i); 1755 float finalAlpha = (!mWorkspaceFadeInAdjacentScreens || stateIsSpringLoaded || 1756 (i == mCurrentPage)) ? 1f : 0f; 1757 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); 1758 float initialAlpha = currentAlpha; 1759 1760 // Determine the pages alpha during the state transition 1761 if ((oldStateIsSmall && stateIsNormal) || 1762 (oldStateIsNormal && stateIsSmall)) { 1763 // To/from workspace - only show the current page unless the transition is not 1764 // animated and the animation end callback below doesn't run; 1765 // or, if we're in spring-loaded mode 1766 if (i == mCurrentPage || !animated || oldStateIsSpringLoaded) { 1767 finalAlpha = 1f; 1768 } else { 1769 initialAlpha = 0f; 1770 finalAlpha = 0f; 1771 } 1772 } 1773 1774 mOldAlphas[i] = initialAlpha; 1775 mNewAlphas[i] = finalAlpha; 1776 if (animated) { 1777 mOldTranslationXs[i] = cl.getTranslationX(); 1778 mOldTranslationYs[i] = cl.getTranslationY(); 1779 mOldScaleXs[i] = cl.getScaleX(); 1780 mOldScaleYs[i] = cl.getScaleY(); 1781 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); 1782 1783 mNewTranslationXs[i] = translationX; 1784 mNewTranslationYs[i] = translationY; 1785 mNewScaleXs[i] = finalScaleFactor; 1786 mNewScaleYs[i] = finalScaleFactor; 1787 mNewBackgroundAlphas[i] = finalBackgroundAlpha; 1788 } else { 1789 cl.setTranslationX(translationX); 1790 cl.setTranslationY(translationY); 1791 cl.setScaleX(finalScaleFactor); 1792 cl.setScaleY(finalScaleFactor); 1793 cl.setBackgroundAlpha(finalBackgroundAlpha); 1794 cl.setShortcutAndWidgetAlpha(finalAlpha); 1795 } 1796 } 1797 1798 if (animated) { 1799 for (int index = 0; index < getChildCount(); index++) { 1800 final int i = index; 1801 final CellLayout cl = (CellLayout) getChildAt(i); 1802 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha(); 1803 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { 1804 cl.setTranslationX(mNewTranslationXs[i]); 1805 cl.setTranslationY(mNewTranslationYs[i]); 1806 cl.setScaleX(mNewScaleXs[i]); 1807 cl.setScaleY(mNewScaleYs[i]); 1808 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); 1809 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]); 1810 cl.setRotationY(mNewRotationYs[i]); 1811 } else { 1812 LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl); 1813 a.translationX(mNewTranslationXs[i]) 1814 .translationY(mNewTranslationYs[i]) 1815 .scaleX(mNewScaleXs[i]) 1816 .scaleY(mNewScaleYs[i]) 1817 .setDuration(duration) 1818 .setInterpolator(mZoomInInterpolator); 1819 anim.play(a); 1820 1821 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) { 1822 LauncherViewPropertyAnimator alphaAnim = 1823 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); 1824 alphaAnim.alpha(mNewAlphas[i]) 1825 .setDuration(duration) 1826 .setInterpolator(mZoomInInterpolator); 1827 anim.play(alphaAnim); 1828 } 1829 if (mOldBackgroundAlphas[i] != 0 || 1830 mNewBackgroundAlphas[i] != 0) { 1831 ValueAnimator bgAnim = 1832 LauncherAnimUtils.ofFloat(cl, 0f, 1f).setDuration(duration); 1833 bgAnim.setInterpolator(mZoomInInterpolator); 1834 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { 1835 public void onAnimationUpdate(float a, float b) { 1836 cl.setBackgroundAlpha( 1837 a * mOldBackgroundAlphas[i] + 1838 b * mNewBackgroundAlphas[i]); 1839 } 1840 }); 1841 anim.play(bgAnim); 1842 } 1843 } 1844 } 1845 anim.setStartDelay(delay); 1846 } 1847 1848 if (stateIsSpringLoaded) { 1849 // Right now we're covered by Apps Customize 1850 // Show the background gradient immediately, so the gradient will 1851 // be showing once AppsCustomize disappears 1852 animateBackgroundGradient(getResources().getInteger( 1853 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false); 1854 } else { 1855 // Fade the background gradient away 1856 animateBackgroundGradient(0f, true); 1857 } 1858 return anim; 1859 } 1860 1861 @Override 1862 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { 1863 mIsSwitchingState = true; 1864 updateChildrenLayersEnabled(false); 1865 cancelScrollingIndicatorAnimations(); 1866 } 1867 1868 @Override 1869 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { 1870 } 1871 1872 @Override 1873 public void onLauncherTransitionStep(Launcher l, float t) { 1874 mTransitionProgress = t; 1875 } 1876 1877 @Override 1878 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { 1879 mIsSwitchingState = false; 1880 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false); 1881 updateChildrenLayersEnabled(false); 1882 // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure 1883 // ensure that only the current page is visible during (and subsequently, after) the 1884 // transition animation. If fade adjacent pages is disabled, then re-enable the page 1885 // visibility after the transition animation. 1886 if (!mWorkspaceFadeInAdjacentScreens) { 1887 for (int i = 0; i < getChildCount(); i++) { 1888 final CellLayout cl = (CellLayout) getChildAt(i); 1889 cl.setShortcutAndWidgetAlpha(1f); 1890 } 1891 } 1892 } 1893 1894 @Override 1895 public View getContent() { 1896 return this; 1897 } 1898 1899 /** 1900 * Draw the View v into the given Canvas. 1901 * 1902 * @param v the view to draw 1903 * @param destCanvas the canvas to draw on 1904 * @param padding the horizontal and vertical padding to use when drawing 1905 */ 1906 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) { 1907 final Rect clipRect = mTempRect; 1908 v.getDrawingRect(clipRect); 1909 1910 boolean textVisible = false; 1911 1912 destCanvas.save(); 1913 if (v instanceof TextView && pruneToDrawable) { 1914 Drawable d = ((TextView) v).getCompoundDrawables()[1]; 1915 clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding); 1916 destCanvas.translate(padding / 2, padding / 2); 1917 d.draw(destCanvas); 1918 } else { 1919 if (v instanceof FolderIcon) { 1920 // For FolderIcons the text can bleed into the icon area, and so we need to 1921 // hide the text completely (which can't be achieved by clipping). 1922 if (((FolderIcon) v).getTextVisible()) { 1923 ((FolderIcon) v).setTextVisible(false); 1924 textVisible = true; 1925 } 1926 } else if (v instanceof BubbleTextView) { 1927 final BubbleTextView tv = (BubbleTextView) v; 1928 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + 1929 tv.getLayout().getLineTop(0); 1930 } else if (v instanceof TextView) { 1931 final TextView tv = (TextView) v; 1932 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() + 1933 tv.getLayout().getLineTop(0); 1934 } 1935 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2); 1936 destCanvas.clipRect(clipRect, Op.REPLACE); 1937 v.draw(destCanvas); 1938 1939 // Restore text visibility of FolderIcon if necessary 1940 if (textVisible) { 1941 ((FolderIcon) v).setTextVisible(true); 1942 } 1943 } 1944 destCanvas.restore(); 1945 } 1946 1947 /** 1948 * Returns a new bitmap to show when the given View is being dragged around. 1949 * Responsibility for the bitmap is transferred to the caller. 1950 */ 1951 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) { 1952 Bitmap b; 1953 1954 if (v instanceof TextView) { 1955 Drawable d = ((TextView) v).getCompoundDrawables()[1]; 1956 b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding, 1957 d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888); 1958 } else { 1959 b = Bitmap.createBitmap( 1960 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); 1961 } 1962 1963 canvas.setBitmap(b); 1964 drawDragView(v, canvas, padding, true); 1965 canvas.setBitmap(null); 1966 1967 return b; 1968 } 1969 1970 /** 1971 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 1972 * Responsibility for the bitmap is transferred to the caller. 1973 */ 1974 private Bitmap createDragOutline(View v, Canvas canvas, int padding) { 1975 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light); 1976 final Bitmap b = Bitmap.createBitmap( 1977 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); 1978 1979 canvas.setBitmap(b); 1980 drawDragView(v, canvas, padding, true); 1981 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor); 1982 canvas.setBitmap(null); 1983 return b; 1984 } 1985 1986 /** 1987 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 1988 * Responsibility for the bitmap is transferred to the caller. 1989 */ 1990 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h, 1991 boolean clipAlpha) { 1992 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light); 1993 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 1994 canvas.setBitmap(b); 1995 1996 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight()); 1997 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(), 1998 (h - padding) / (float) orig.getHeight()); 1999 int scaledWidth = (int) (scaleFactor * orig.getWidth()); 2000 int scaledHeight = (int) (scaleFactor * orig.getHeight()); 2001 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight); 2002 2003 // center the image 2004 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2); 2005 2006 canvas.drawBitmap(orig, src, dst, null); 2007 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor, 2008 clipAlpha); 2009 canvas.setBitmap(null); 2010 2011 return b; 2012 } 2013 2014 void startDrag(CellLayout.CellInfo cellInfo) { 2015 View child = cellInfo.cell; 2016 2017 // Make sure the drag was started by a long press as opposed to a long click. 2018 if (!child.isInTouchMode()) { 2019 return; 2020 } 2021 2022 mDragInfo = cellInfo; 2023 child.setVisibility(INVISIBLE); 2024 CellLayout layout = (CellLayout) child.getParent().getParent(); 2025 layout.prepareChildForDrag(child); 2026 2027 child.clearFocus(); 2028 child.setPressed(false); 2029 2030 final Canvas canvas = new Canvas(); 2031 2032 // The outline is used to visualize where the item will land if dropped 2033 mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING); 2034 beginDragShared(child, this); 2035 } 2036 2037 public void beginDragShared(View child, DragSource source) { 2038 Resources r = getResources(); 2039 2040 // The drag bitmap follows the touch point around on the screen 2041 final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING); 2042 2043 final int bmpWidth = b.getWidth(); 2044 final int bmpHeight = b.getHeight(); 2045 2046 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY); 2047 int dragLayerX = 2048 Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2); 2049 int dragLayerY = 2050 Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 2051 - DRAG_BITMAP_PADDING / 2); 2052 2053 Point dragVisualizeOffset = null; 2054 Rect dragRect = null; 2055 if (child instanceof BubbleTextView || child instanceof PagedViewIcon) { 2056 int iconSize = r.getDimensionPixelSize(R.dimen.app_icon_size); 2057 int iconPaddingTop = r.getDimensionPixelSize(R.dimen.app_icon_padding_top); 2058 int top = child.getPaddingTop(); 2059 int left = (bmpWidth - iconSize) / 2; 2060 int right = left + iconSize; 2061 int bottom = top + iconSize; 2062 dragLayerY += top; 2063 // Note: The drag region is used to calculate drag layer offsets, but the 2064 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. 2065 dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, 2066 iconPaddingTop - DRAG_BITMAP_PADDING / 2); 2067 dragRect = new Rect(left, top, right, bottom); 2068 } else if (child instanceof FolderIcon) { 2069 int previewSize = r.getDimensionPixelSize(R.dimen.folder_preview_size); 2070 dragRect = new Rect(0, 0, child.getWidth(), previewSize); 2071 } 2072 2073 // Clear the pressed state if necessary 2074 if (child instanceof BubbleTextView) { 2075 BubbleTextView icon = (BubbleTextView) child; 2076 icon.clearPressedOrFocusedBackground(); 2077 } 2078 2079 mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), 2080 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale); 2081 b.recycle(); 2082 2083 // Show the scrolling indicator when you pick up an item 2084 showScrollingIndicator(false); 2085 } 2086 2087 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId, 2088 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) { 2089 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info); 2090 2091 final int[] cellXY = new int[2]; 2092 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY); 2093 addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst); 2094 2095 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0], 2096 cellXY[1]); 2097 } 2098 2099 public boolean transitionStateShouldAllowDrop() { 2100 return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL); 2101 } 2102 2103 /** 2104 * {@inheritDoc} 2105 */ 2106 public boolean acceptDrop(DragObject d) { 2107 // If it's an external drop (e.g. from All Apps), check if it should be accepted 2108 CellLayout dropTargetLayout = mDropToLayout; 2109 if (d.dragSource != this) { 2110 // Don't accept the drop if we're not over a screen at time of drop 2111 if (dropTargetLayout == null) { 2112 return false; 2113 } 2114 if (!transitionStateShouldAllowDrop()) return false; 2115 2116 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, 2117 d.dragView, mDragViewVisualCenter); 2118 2119 // We want the point to be mapped to the dragTarget. 2120 if (mLauncher.isHotseatLayout(dropTargetLayout)) { 2121 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); 2122 } else { 2123 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); 2124 } 2125 2126 int spanX = 1; 2127 int spanY = 1; 2128 if (mDragInfo != null) { 2129 final CellLayout.CellInfo dragCellInfo = mDragInfo; 2130 spanX = dragCellInfo.spanX; 2131 spanY = dragCellInfo.spanY; 2132 } else { 2133 final ItemInfo dragInfo = (ItemInfo) d.dragInfo; 2134 spanX = dragInfo.spanX; 2135 spanY = dragInfo.spanY; 2136 } 2137 2138 int minSpanX = spanX; 2139 int minSpanY = spanY; 2140 if (d.dragInfo instanceof PendingAddWidgetInfo) { 2141 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX; 2142 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY; 2143 } 2144 2145 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], 2146 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout, 2147 mTargetCell); 2148 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], 2149 mDragViewVisualCenter[1], mTargetCell); 2150 if (willCreateUserFolder((ItemInfo) d.dragInfo, dropTargetLayout, 2151 mTargetCell, distance, true)) { 2152 return true; 2153 } 2154 if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, dropTargetLayout, 2155 mTargetCell, distance)) { 2156 return true; 2157 } 2158 2159 int[] resultSpan = new int[2]; 2160 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0], 2161 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, 2162 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP); 2163 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0; 2164 2165 // Don't accept the drop if there's no room for the item 2166 if (!foundCell) { 2167 // Don't show the message if we are dropping on the AllApps button and the hotseat 2168 // is full 2169 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout); 2170 if (mTargetCell != null && isHotseat) { 2171 Hotseat hotseat = mLauncher.getHotseat(); 2172 if (hotseat.isAllAppsButtonRank( 2173 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) { 2174 return false; 2175 } 2176 } 2177 2178 mLauncher.showOutOfSpaceMessage(isHotseat); 2179 return false; 2180 } 2181 } 2182 return true; 2183 } 2184 2185 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float 2186 distance, boolean considerTimeout) { 2187 if (distance > mMaxDistanceForFolderCreation) return false; 2188 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); 2189 2190 if (dropOverView != null) { 2191 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); 2192 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { 2193 return false; 2194 } 2195 } 2196 2197 boolean hasntMoved = false; 2198 if (mDragInfo != null) { 2199 hasntMoved = dropOverView == mDragInfo.cell; 2200 } 2201 2202 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) { 2203 return false; 2204 } 2205 2206 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo); 2207 boolean willBecomeShortcut = 2208 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 2209 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT); 2210 2211 return (aboveShortcut && willBecomeShortcut); 2212 } 2213 2214 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell, 2215 float distance) { 2216 if (distance > mMaxDistanceForFolderCreation) return false; 2217 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); 2218 2219 if (dropOverView != null) { 2220 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); 2221 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { 2222 return false; 2223 } 2224 } 2225 2226 if (dropOverView instanceof FolderIcon) { 2227 FolderIcon fi = (FolderIcon) dropOverView; 2228 if (fi.acceptDrop(dragInfo)) { 2229 return true; 2230 } 2231 } 2232 return false; 2233 } 2234 2235 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target, 2236 int[] targetCell, float distance, boolean external, DragView dragView, 2237 Runnable postAnimationRunnable) { 2238 if (distance > mMaxDistanceForFolderCreation) return false; 2239 View v = target.getChildAt(targetCell[0], targetCell[1]); 2240 2241 boolean hasntMoved = false; 2242 if (mDragInfo != null) { 2243 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell); 2244 hasntMoved = (mDragInfo.cellX == targetCell[0] && 2245 mDragInfo.cellY == targetCell[1]) && (cellParent == target); 2246 } 2247 2248 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false; 2249 mCreateUserFolderOnDrop = false; 2250 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target); 2251 2252 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo); 2253 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo); 2254 2255 if (aboveShortcut && willBecomeShortcut) { 2256 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag(); 2257 ShortcutInfo destInfo = (ShortcutInfo) v.getTag(); 2258 // if the drag started here, we need to remove it from the workspace 2259 if (!external) { 2260 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); 2261 } 2262 2263 Rect folderLocation = new Rect(); 2264 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation); 2265 target.removeView(v); 2266 2267 FolderIcon fi = 2268 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]); 2269 destInfo.cellX = -1; 2270 destInfo.cellY = -1; 2271 sourceInfo.cellX = -1; 2272 sourceInfo.cellY = -1; 2273 2274 // If the dragView is null, we can't animate 2275 boolean animate = dragView != null; 2276 if (animate) { 2277 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale, 2278 postAnimationRunnable); 2279 } else { 2280 fi.addItem(destInfo); 2281 fi.addItem(sourceInfo); 2282 } 2283 return true; 2284 } 2285 return false; 2286 } 2287 2288 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell, 2289 float distance, DragObject d, boolean external) { 2290 if (distance > mMaxDistanceForFolderCreation) return false; 2291 2292 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); 2293 if (!mAddToExistingFolderOnDrop) return false; 2294 mAddToExistingFolderOnDrop = false; 2295 2296 if (dropOverView instanceof FolderIcon) { 2297 FolderIcon fi = (FolderIcon) dropOverView; 2298 if (fi.acceptDrop(d.dragInfo)) { 2299 fi.onDrop(d); 2300 2301 // if the drag started here, we need to remove it from the workspace 2302 if (!external) { 2303 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); 2304 } 2305 return true; 2306 } 2307 } 2308 return false; 2309 } 2310 2311 public void onDrop(final DragObject d) { 2312 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, 2313 mDragViewVisualCenter); 2314 2315 CellLayout dropTargetLayout = mDropToLayout; 2316 2317 // We want the point to be mapped to the dragTarget. 2318 if (dropTargetLayout != null) { 2319 if (mLauncher.isHotseatLayout(dropTargetLayout)) { 2320 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); 2321 } else { 2322 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); 2323 } 2324 } 2325 2326 int snapScreen = -1; 2327 boolean resizeOnDrop = false; 2328 if (d.dragSource != this) { 2329 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], 2330 (int) mDragViewVisualCenter[1] }; 2331 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d); 2332 } else if (mDragInfo != null) { 2333 final View cell = mDragInfo.cell; 2334 2335 Runnable resizeRunnable = null; 2336 if (dropTargetLayout != null) { 2337 // Move internally 2338 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout); 2339 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout); 2340 long container = hasMovedIntoHotseat ? 2341 LauncherSettings.Favorites.CONTAINER_HOTSEAT : 2342 LauncherSettings.Favorites.CONTAINER_DESKTOP; 2343 long screenId = (mTargetCell[0] < 0) ? 2344 mDragInfo.screenId : getIdForScreen(dropTargetLayout); 2345 int spanX = mDragInfo != null ? mDragInfo.spanX : 1; 2346 int spanY = mDragInfo != null ? mDragInfo.spanY : 1; 2347 // First we find the cell nearest to point at which the item is 2348 // dropped, without any consideration to whether there is an item there. 2349 2350 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int) 2351 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell); 2352 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], 2353 mDragViewVisualCenter[1], mTargetCell); 2354 2355 // If the item being dropped is a shortcut and the nearest drop 2356 // cell also contains a shortcut, then create a folder with the two shortcuts. 2357 if (!mInScrollArea && createUserFolderIfNecessary(cell, container, 2358 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) { 2359 return; 2360 } 2361 2362 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, 2363 distance, d, false)) { 2364 return; 2365 } 2366 2367 // Aside from the special case where we're dropping a shortcut onto a shortcut, 2368 // we need to find the nearest cell location that is vacant 2369 ItemInfo item = (ItemInfo) d.dragInfo; 2370 int minSpanX = item.spanX; 2371 int minSpanY = item.spanY; 2372 if (item.minSpanX > 0 && item.minSpanY > 0) { 2373 minSpanX = item.minSpanX; 2374 minSpanY = item.minSpanY; 2375 } 2376 2377 int[] resultSpan = new int[2]; 2378 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0], 2379 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell, 2380 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP); 2381 2382 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0; 2383 2384 // if the widget resizes on drop 2385 if (foundCell && (cell instanceof AppWidgetHostView) && 2386 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) { 2387 resizeOnDrop = true; 2388 item.spanX = resultSpan[0]; 2389 item.spanY = resultSpan[1]; 2390 AppWidgetHostView awhv = (AppWidgetHostView) cell; 2391 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0], 2392 resultSpan[1]); 2393 } 2394 2395 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) { 2396 snapScreen = getPageIndexForScreenId(screenId); 2397 snapToPage(snapScreen); 2398 } 2399 2400 if (foundCell) { 2401 final ItemInfo info = (ItemInfo) cell.getTag(); 2402 if (hasMovedLayouts) { 2403 // Reparent the view 2404 getParentCellLayoutForView(cell).removeView(cell); 2405 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1], 2406 info.spanX, info.spanY); 2407 } 2408 2409 // update the item's position after drop 2410 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 2411 lp.cellX = lp.tmpCellX = mTargetCell[0]; 2412 lp.cellY = lp.tmpCellY = mTargetCell[1]; 2413 lp.cellHSpan = item.spanX; 2414 lp.cellVSpan = item.spanY; 2415 lp.isLockedToGrid = true; 2416 cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screenId, 2417 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); 2418 2419 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT && 2420 cell instanceof LauncherAppWidgetHostView) { 2421 final CellLayout cellLayout = dropTargetLayout; 2422 // We post this call so that the widget has a chance to be placed 2423 // in its final location 2424 2425 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; 2426 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo(); 2427 if (pinfo != null && 2428 pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { 2429 final Runnable addResizeFrame = new Runnable() { 2430 public void run() { 2431 DragLayer dragLayer = mLauncher.getDragLayer(); 2432 dragLayer.addResizeFrame(info, hostView, cellLayout); 2433 } 2434 }; 2435 resizeRunnable = (new Runnable() { 2436 public void run() { 2437 if (!isPageMoving()) { 2438 addResizeFrame.run(); 2439 } else { 2440 mDelayedResizeRunnable = addResizeFrame; 2441 } 2442 } 2443 }); 2444 } 2445 } 2446 2447 LauncherModel.moveItemInDatabase(mLauncher, info, container, screenId, lp.cellX, 2448 lp.cellY); 2449 } else { 2450 // If we can't find a drop location, we return the item to its original position 2451 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); 2452 mTargetCell[0] = lp.cellX; 2453 mTargetCell[1] = lp.cellY; 2454 CellLayout layout = (CellLayout) cell.getParent().getParent(); 2455 layout.markCellsAsOccupiedForView(cell); 2456 } 2457 } 2458 2459 final CellLayout parent = (CellLayout) cell.getParent().getParent(); 2460 final Runnable finalResizeRunnable = resizeRunnable; 2461 // Prepare it to be animated into its new position 2462 // This must be called after the view has been re-parented 2463 final Runnable onCompleteRunnable = new Runnable() { 2464 @Override 2465 public void run() { 2466 mAnimatingViewIntoPlace = false; 2467 updateChildrenLayersEnabled(false); 2468 if (finalResizeRunnable != null) { 2469 finalResizeRunnable.run(); 2470 } 2471 } 2472 }; 2473 mAnimatingViewIntoPlace = true; 2474 if (d.dragView.hasDrawn()) { 2475 final ItemInfo info = (ItemInfo) cell.getTag(); 2476 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) { 2477 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE : 2478 ANIMATE_INTO_POSITION_AND_DISAPPEAR; 2479 animateWidgetDrop(info, parent, d.dragView, 2480 onCompleteRunnable, animationType, cell, false); 2481 } else { 2482 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION; 2483 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration, 2484 onCompleteRunnable, this); 2485 } 2486 } else { 2487 d.deferDragViewCleanupPostAnimation = false; 2488 cell.setVisibility(VISIBLE); 2489 } 2490 parent.onDropChild(cell); 2491 } 2492 } 2493 2494 public void setFinalScrollForPageChange(int pageIndex) { 2495 CellLayout cl = (CellLayout) getChildAt(pageIndex); 2496 if (cl != null) { 2497 mSavedScrollX = getScrollX(); 2498 mSavedTranslationX = cl.getTranslationX(); 2499 mSavedRotationY = cl.getRotationY(); 2500 final int newX = getChildOffset(pageIndex) - getRelativeChildOffset(pageIndex); 2501 setScrollX(newX); 2502 cl.setTranslationX(0f); 2503 cl.setRotationY(0f); 2504 } 2505 } 2506 2507 public void resetFinalScrollForPageChange(int pageIndex) { 2508 if (pageIndex >= 0) { 2509 CellLayout cl = (CellLayout) getChildAt(pageIndex); 2510 setScrollX(mSavedScrollX); 2511 cl.setTranslationX(mSavedTranslationX); 2512 cl.setRotationY(mSavedRotationY); 2513 } 2514 } 2515 2516 public void getViewLocationRelativeToSelf(View v, int[] location) { 2517 getLocationInWindow(location); 2518 int x = location[0]; 2519 int y = location[1]; 2520 2521 v.getLocationInWindow(location); 2522 int vX = location[0]; 2523 int vY = location[1]; 2524 2525 location[0] = vX - x; 2526 location[1] = vY - y; 2527 } 2528 2529 public void onDragEnter(DragObject d) { 2530 mDragEnforcer.onDragEnter(); 2531 mCreateUserFolderOnDrop = false; 2532 mAddToExistingFolderOnDrop = false; 2533 2534 mDropToLayout = null; 2535 CellLayout layout = getCurrentDropLayout(); 2536 setCurrentDropLayout(layout); 2537 setCurrentDragOverlappingLayout(layout); 2538 2539 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we 2540 // don't need to show the outlines 2541 if (LauncherAppState.getInstance().isScreenLarge()) { 2542 showOutlines(); 2543 } 2544 } 2545 2546 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) { 2547 Resources res = launcher.getResources(); 2548 Display display = launcher.getWindowManager().getDefaultDisplay(); 2549 Point smallestSize = new Point(); 2550 Point largestSize = new Point(); 2551 display.getCurrentSizeRange(smallestSize, largestSize); 2552 if (orientation == CellLayout.LANDSCAPE) { 2553 if (mLandscapeCellLayoutMetrics == null) { 2554 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land); 2555 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land); 2556 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land); 2557 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land); 2558 int width = largestSize.x - paddingLeft - paddingRight; 2559 int height = smallestSize.y - paddingTop - paddingBottom; 2560 mLandscapeCellLayoutMetrics = new Rect(); 2561 CellLayout.getMetrics(mLandscapeCellLayoutMetrics, res, 2562 width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(), 2563 orientation); 2564 } 2565 return mLandscapeCellLayoutMetrics; 2566 } else if (orientation == CellLayout.PORTRAIT) { 2567 if (mPortraitCellLayoutMetrics == null) { 2568 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land); 2569 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land); 2570 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land); 2571 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land); 2572 int width = smallestSize.x - paddingLeft - paddingRight; 2573 int height = largestSize.y - paddingTop - paddingBottom; 2574 mPortraitCellLayoutMetrics = new Rect(); 2575 CellLayout.getMetrics(mPortraitCellLayoutMetrics, res, 2576 width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(), 2577 orientation); 2578 } 2579 return mPortraitCellLayoutMetrics; 2580 } 2581 return null; 2582 } 2583 2584 public void onDragExit(DragObject d) { 2585 mDragEnforcer.onDragExit(); 2586 2587 // Here we store the final page that will be dropped to, if the workspace in fact 2588 // receives the drop 2589 if (mInScrollArea) { 2590 if (isPageMoving()) { 2591 // If the user drops while the page is scrolling, we should use that page as the 2592 // destination instead of the page that is being hovered over. 2593 mDropToLayout = (CellLayout) getPageAt(getNextPage()); 2594 } else { 2595 mDropToLayout = mDragOverlappingLayout; 2596 } 2597 } else { 2598 mDropToLayout = mDragTargetLayout; 2599 } 2600 2601 if (mDragMode == DRAG_MODE_CREATE_FOLDER) { 2602 mCreateUserFolderOnDrop = true; 2603 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) { 2604 mAddToExistingFolderOnDrop = true; 2605 } 2606 2607 // Reset the scroll area and previous drag target 2608 onResetScrollArea(); 2609 setCurrentDropLayout(null); 2610 setCurrentDragOverlappingLayout(null); 2611 2612 mSpringLoadedDragController.cancel(); 2613 2614 if (!mIsPageMoving) { 2615 hideOutlines(); 2616 } 2617 } 2618 2619 void setCurrentDropLayout(CellLayout layout) { 2620 if (mDragTargetLayout != null) { 2621 mDragTargetLayout.revertTempState(); 2622 mDragTargetLayout.onDragExit(); 2623 } 2624 mDragTargetLayout = layout; 2625 if (mDragTargetLayout != null) { 2626 mDragTargetLayout.onDragEnter(); 2627 } 2628 cleanupReorder(true); 2629 cleanupFolderCreation(); 2630 setCurrentDropOverCell(-1, -1); 2631 } 2632 2633 void setCurrentDragOverlappingLayout(CellLayout layout) { 2634 if (mDragOverlappingLayout != null) { 2635 mDragOverlappingLayout.setIsDragOverlapping(false); 2636 } 2637 mDragOverlappingLayout = layout; 2638 if (mDragOverlappingLayout != null) { 2639 mDragOverlappingLayout.setIsDragOverlapping(true); 2640 } 2641 invalidate(); 2642 } 2643 2644 void setCurrentDropOverCell(int x, int y) { 2645 if (x != mDragOverX || y != mDragOverY) { 2646 mDragOverX = x; 2647 mDragOverY = y; 2648 setDragMode(DRAG_MODE_NONE); 2649 } 2650 } 2651 2652 void setDragMode(int dragMode) { 2653 if (dragMode != mDragMode) { 2654 if (dragMode == DRAG_MODE_NONE) { 2655 cleanupAddToFolder(); 2656 // We don't want to cancel the re-order alarm every time the target cell changes 2657 // as this feels to slow / unresponsive. 2658 cleanupReorder(false); 2659 cleanupFolderCreation(); 2660 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) { 2661 cleanupReorder(true); 2662 cleanupFolderCreation(); 2663 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) { 2664 cleanupAddToFolder(); 2665 cleanupReorder(true); 2666 } else if (dragMode == DRAG_MODE_REORDER) { 2667 cleanupAddToFolder(); 2668 cleanupFolderCreation(); 2669 } 2670 mDragMode = dragMode; 2671 } 2672 } 2673 2674 private void cleanupFolderCreation() { 2675 if (mDragFolderRingAnimator != null) { 2676 mDragFolderRingAnimator.animateToNaturalState(); 2677 } 2678 mFolderCreationAlarm.cancelAlarm(); 2679 } 2680 2681 private void cleanupAddToFolder() { 2682 if (mDragOverFolderIcon != null) { 2683 mDragOverFolderIcon.onDragExit(null); 2684 mDragOverFolderIcon = null; 2685 } 2686 } 2687 2688 private void cleanupReorder(boolean cancelAlarm) { 2689 // Any pending reorders are canceled 2690 if (cancelAlarm) { 2691 mReorderAlarm.cancelAlarm(); 2692 } 2693 mLastReorderX = -1; 2694 mLastReorderY = -1; 2695 } 2696 2697 public DropTarget getDropTargetDelegate(DragObject d) { 2698 return null; 2699 } 2700 2701 /* 2702 * 2703 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 2704 * coordinate space. The argument xy is modified with the return result. 2705 * 2706 */ 2707 void mapPointFromSelfToChild(View v, float[] xy) { 2708 mapPointFromSelfToChild(v, xy, null); 2709 } 2710 2711 /* 2712 * 2713 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's 2714 * coordinate space. The argument xy is modified with the return result. 2715 * 2716 * if cachedInverseMatrix is not null, this method will just use that matrix instead of 2717 * computing it itself; we use this to avoid redundant matrix inversions in 2718 * findMatchingPageForDragOver 2719 * 2720 */ 2721 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) { 2722 if (cachedInverseMatrix == null) { 2723 v.getMatrix().invert(mTempInverseMatrix); 2724 cachedInverseMatrix = mTempInverseMatrix; 2725 } 2726 int scrollX = getScrollX(); 2727 if (mNextPage != INVALID_PAGE) { 2728 scrollX = mScroller.getFinalX(); 2729 } 2730 xy[0] = xy[0] + scrollX - v.getLeft(); 2731 xy[1] = xy[1] + getScrollY() - v.getTop(); 2732 cachedInverseMatrix.mapPoints(xy); 2733 } 2734 2735 2736 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) { 2737 hotseat.getLayout().getMatrix().invert(mTempInverseMatrix); 2738 xy[0] = xy[0] - hotseat.getLeft() - hotseat.getLayout().getLeft(); 2739 xy[1] = xy[1] - hotseat.getTop() - hotseat.getLayout().getTop(); 2740 mTempInverseMatrix.mapPoints(xy); 2741 } 2742 2743 /* 2744 * 2745 * Convert the 2D coordinate xy from this CellLayout's coordinate space to 2746 * the parent View's coordinate space. The argument xy is modified with the return result. 2747 * 2748 */ 2749 void mapPointFromChildToSelf(View v, float[] xy) { 2750 v.getMatrix().mapPoints(xy); 2751 int scrollX = getScrollX(); 2752 if (mNextPage != INVALID_PAGE) { 2753 scrollX = mScroller.getFinalX(); 2754 } 2755 xy[0] -= (scrollX - v.getLeft()); 2756 xy[1] -= (getScrollY() - v.getTop()); 2757 } 2758 2759 static private float squaredDistance(float[] point1, float[] point2) { 2760 float distanceX = point1[0] - point2[0]; 2761 float distanceY = point2[1] - point2[1]; 2762 return distanceX * distanceX + distanceY * distanceY; 2763 } 2764 2765 /* 2766 * 2767 * Returns true if the passed CellLayout cl overlaps with dragView 2768 * 2769 */ 2770 boolean overlaps(CellLayout cl, DragView dragView, 2771 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) { 2772 // Transform the coordinates of the item being dragged to the CellLayout's coordinates 2773 final float[] draggedItemTopLeft = mTempDragCoordinates; 2774 draggedItemTopLeft[0] = dragViewX; 2775 draggedItemTopLeft[1] = dragViewY; 2776 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates; 2777 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth(); 2778 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight(); 2779 2780 // Transform the dragged item's top left coordinates 2781 // to the CellLayout's local coordinates 2782 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix); 2783 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]); 2784 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]); 2785 2786 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) { 2787 // Transform the dragged item's bottom right coordinates 2788 // to the CellLayout's local coordinates 2789 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix); 2790 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]); 2791 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]); 2792 2793 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) { 2794 float overlap = (overlapRegionRight - overlapRegionLeft) * 2795 (overlapRegionBottom - overlapRegionTop); 2796 if (overlap > 0) { 2797 return true; 2798 } 2799 } 2800 } 2801 return false; 2802 } 2803 2804 /* 2805 * 2806 * This method returns the CellLayout that is currently being dragged to. In order to drag 2807 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second 2808 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one 2809 * 2810 * Return null if no CellLayout is currently being dragged over 2811 * 2812 */ 2813 private CellLayout findMatchingPageForDragOver( 2814 DragView dragView, float originX, float originY, boolean exact) { 2815 // We loop through all the screens (ie CellLayouts) and see which ones overlap 2816 // with the item being dragged and then choose the one that's closest to the touch point 2817 final int screenCount = getChildCount(); 2818 CellLayout bestMatchingScreen = null; 2819 float smallestDistSoFar = Float.MAX_VALUE; 2820 2821 for (int i = 0; i < screenCount; i++) { 2822 CellLayout cl = (CellLayout) getChildAt(i); 2823 2824 final float[] touchXy = {originX, originY}; 2825 // Transform the touch coordinates to the CellLayout's local coordinates 2826 // If the touch point is within the bounds of the cell layout, we can return immediately 2827 cl.getMatrix().invert(mTempInverseMatrix); 2828 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); 2829 2830 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && 2831 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { 2832 return cl; 2833 } 2834 2835 if (!exact) { 2836 // Get the center of the cell layout in screen coordinates 2837 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates; 2838 cellLayoutCenter[0] = cl.getWidth()/2; 2839 cellLayoutCenter[1] = cl.getHeight()/2; 2840 mapPointFromChildToSelf(cl, cellLayoutCenter); 2841 2842 touchXy[0] = originX; 2843 touchXy[1] = originY; 2844 2845 // Calculate the distance between the center of the CellLayout 2846 // and the touch point 2847 float dist = squaredDistance(touchXy, cellLayoutCenter); 2848 2849 if (dist < smallestDistSoFar) { 2850 smallestDistSoFar = dist; 2851 bestMatchingScreen = cl; 2852 } 2853 } 2854 } 2855 return bestMatchingScreen; 2856 } 2857 2858 // This is used to compute the visual center of the dragView. This point is then 2859 // used to visualize drop locations and determine where to drop an item. The idea is that 2860 // the visual center represents the user's interpretation of where the item is, and hence 2861 // is the appropriate point to use when determining drop location. 2862 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset, 2863 DragView dragView, float[] recycle) { 2864 float res[]; 2865 if (recycle == null) { 2866 res = new float[2]; 2867 } else { 2868 res = recycle; 2869 } 2870 2871 // First off, the drag view has been shifted in a way that is not represented in the 2872 // x and y values or the x/yOffsets. Here we account for that shift. 2873 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX); 2874 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); 2875 2876 // These represent the visual top and left of drag view if a dragRect was provided. 2877 // If a dragRect was not provided, then they correspond to the actual view left and 2878 // top, as the dragRect is in that case taken to be the entire dragView. 2879 // R.dimen.dragViewOffsetY. 2880 int left = x - xOffset; 2881 int top = y - yOffset; 2882 2883 // In order to find the visual center, we shift by half the dragRect 2884 res[0] = left + dragView.getDragRegion().width() / 2; 2885 res[1] = top + dragView.getDragRegion().height() / 2; 2886 2887 return res; 2888 } 2889 2890 private boolean isDragWidget(DragObject d) { 2891 return (d.dragInfo instanceof LauncherAppWidgetInfo || 2892 d.dragInfo instanceof PendingAddWidgetInfo); 2893 } 2894 private boolean isExternalDragWidget(DragObject d) { 2895 return d.dragSource != this && isDragWidget(d); 2896 } 2897 2898 public void onDragOver(DragObject d) { 2899 // Skip drag over events while we are dragging over side pages 2900 if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return; 2901 2902 Rect r = new Rect(); 2903 CellLayout layout = null; 2904 ItemInfo item = (ItemInfo) d.dragInfo; 2905 2906 // Ensure that we have proper spans for the item that we are dropping 2907 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found"); 2908 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, 2909 d.dragView, mDragViewVisualCenter); 2910 2911 final View child = (mDragInfo == null) ? null : mDragInfo.cell; 2912 // Identify whether we have dragged over a side page 2913 if (isSmall()) { 2914 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) { 2915 mLauncher.getHotseat().getHitRect(r); 2916 if (r.contains(d.x, d.y)) { 2917 layout = mLauncher.getHotseat().getLayout(); 2918 } 2919 } 2920 if (layout == null) { 2921 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false); 2922 } 2923 if (layout != mDragTargetLayout) { 2924 2925 setCurrentDropLayout(layout); 2926 setCurrentDragOverlappingLayout(layout); 2927 2928 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED); 2929 if (isInSpringLoadedMode) { 2930 if (mLauncher.isHotseatLayout(layout)) { 2931 mSpringLoadedDragController.cancel(); 2932 } else { 2933 mSpringLoadedDragController.setAlarm(mDragTargetLayout); 2934 } 2935 } 2936 } 2937 } else { 2938 // Test to see if we are over the hotseat otherwise just use the current page 2939 if (mLauncher.getHotseat() != null && !isDragWidget(d)) { 2940 mLauncher.getHotseat().getHitRect(r); 2941 if (r.contains(d.x, d.y)) { 2942 layout = mLauncher.getHotseat().getLayout(); 2943 } 2944 } 2945 if (layout == null) { 2946 layout = getCurrentDropLayout(); 2947 } 2948 if (layout != mDragTargetLayout) { 2949 setCurrentDropLayout(layout); 2950 setCurrentDragOverlappingLayout(layout); 2951 } 2952 } 2953 2954 // Handle the drag over 2955 if (mDragTargetLayout != null) { 2956 // We want the point to be mapped to the dragTarget. 2957 if (mLauncher.isHotseatLayout(mDragTargetLayout)) { 2958 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); 2959 } else { 2960 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null); 2961 } 2962 2963 ItemInfo info = (ItemInfo) d.dragInfo; 2964 2965 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], 2966 (int) mDragViewVisualCenter[1], item.spanX, item.spanY, 2967 mDragTargetLayout, mTargetCell); 2968 2969 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]); 2970 2971 float targetCellDistance = mDragTargetLayout.getDistanceFromCell( 2972 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); 2973 2974 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], 2975 mTargetCell[1]); 2976 2977 manageFolderFeedback(info, mDragTargetLayout, mTargetCell, 2978 targetCellDistance, dragOverView); 2979 2980 int minSpanX = item.spanX; 2981 int minSpanY = item.spanY; 2982 if (item.minSpanX > 0 && item.minSpanY > 0) { 2983 minSpanX = item.minSpanX; 2984 minSpanY = item.minSpanY; 2985 } 2986 2987 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int) 2988 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, 2989 item.spanY, child, mTargetCell); 2990 2991 if (!nearestDropOccupied) { 2992 mDragTargetLayout.visualizeDropLocation(child, mDragOutline, 2993 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], 2994 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, 2995 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion()); 2996 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) 2997 && !mReorderAlarm.alarmPending() && (mLastReorderX != mTargetCell[0] || 2998 mLastReorderY != mTargetCell[1])) { 2999 3000 // Otherwise, if we aren't adding to or creating a folder and there's no pending 3001 // reorder, then we schedule a reorder 3002 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter, 3003 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child); 3004 mReorderAlarm.setOnAlarmListener(listener); 3005 mReorderAlarm.setAlarm(REORDER_TIMEOUT); 3006 } 3007 3008 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER || 3009 !nearestDropOccupied) { 3010 if (mDragTargetLayout != null) { 3011 mDragTargetLayout.revertTempState(); 3012 } 3013 } 3014 } 3015 } 3016 3017 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout, 3018 int[] targetCell, float distance, View dragOverView) { 3019 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance, 3020 false); 3021 3022 if (mDragMode == DRAG_MODE_NONE && userFolderPending && 3023 !mFolderCreationAlarm.alarmPending()) { 3024 mFolderCreationAlarm.setOnAlarmListener(new 3025 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1])); 3026 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT); 3027 return; 3028 } 3029 3030 boolean willAddToFolder = 3031 willAddToExistingUserFolder(info, targetLayout, targetCell, distance); 3032 3033 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) { 3034 mDragOverFolderIcon = ((FolderIcon) dragOverView); 3035 mDragOverFolderIcon.onDragEnter(info); 3036 if (targetLayout != null) { 3037 targetLayout.clearDragOutlines(); 3038 } 3039 setDragMode(DRAG_MODE_ADD_TO_FOLDER); 3040 return; 3041 } 3042 3043 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) { 3044 setDragMode(DRAG_MODE_NONE); 3045 } 3046 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) { 3047 setDragMode(DRAG_MODE_NONE); 3048 } 3049 3050 return; 3051 } 3052 3053 class FolderCreationAlarmListener implements OnAlarmListener { 3054 CellLayout layout; 3055 int cellX; 3056 int cellY; 3057 3058 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) { 3059 this.layout = layout; 3060 this.cellX = cellX; 3061 this.cellY = cellY; 3062 } 3063 3064 public void onAlarm(Alarm alarm) { 3065 if (mDragFolderRingAnimator == null) { 3066 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null); 3067 } 3068 mDragFolderRingAnimator.setCell(cellX, cellY); 3069 mDragFolderRingAnimator.setCellLayout(layout); 3070 mDragFolderRingAnimator.animateToAcceptState(); 3071 layout.showFolderAccept(mDragFolderRingAnimator); 3072 layout.clearDragOutlines(); 3073 setDragMode(DRAG_MODE_CREATE_FOLDER); 3074 } 3075 } 3076 3077 class ReorderAlarmListener implements OnAlarmListener { 3078 float[] dragViewCenter; 3079 int minSpanX, minSpanY, spanX, spanY; 3080 DragView dragView; 3081 View child; 3082 3083 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX, 3084 int spanY, DragView dragView, View child) { 3085 this.dragViewCenter = dragViewCenter; 3086 this.minSpanX = minSpanX; 3087 this.minSpanY = minSpanY; 3088 this.spanX = spanX; 3089 this.spanY = spanY; 3090 this.child = child; 3091 this.dragView = dragView; 3092 } 3093 3094 public void onAlarm(Alarm alarm) { 3095 int[] resultSpan = new int[2]; 3096 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], 3097 (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell); 3098 mLastReorderX = mTargetCell[0]; 3099 mLastReorderY = mTargetCell[1]; 3100 3101 mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0], 3102 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, 3103 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER); 3104 3105 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) { 3106 mDragTargetLayout.revertTempState(); 3107 } else { 3108 setDragMode(DRAG_MODE_REORDER); 3109 } 3110 3111 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY; 3112 mDragTargetLayout.visualizeDropLocation(child, mDragOutline, 3113 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], 3114 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, 3115 dragView.getDragVisualizeOffset(), dragView.getDragRegion()); 3116 } 3117 } 3118 3119 @Override 3120 public void getHitRect(Rect outRect) { 3121 // We want the workspace to have the whole area of the display (it will find the correct 3122 // cell layout to drop to in the existing drag/drop logic. 3123 outRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); 3124 } 3125 3126 /** 3127 * Add the item specified by dragInfo to the given layout. 3128 * @return true if successful 3129 */ 3130 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) { 3131 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) { 3132 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false); 3133 return true; 3134 } 3135 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout)); 3136 return false; 3137 } 3138 3139 private void onDropExternal(int[] touchXY, Object dragInfo, 3140 CellLayout cellLayout, boolean insertAtFirst) { 3141 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null); 3142 } 3143 3144 /** 3145 * Drop an item that didn't originate on one of the workspace screens. 3146 * It may have come from Launcher (e.g. from all apps or customize), or it may have 3147 * come from another app altogether. 3148 * 3149 * NOTE: This can also be called when we are outside of a drag event, when we want 3150 * to add an item to one of the workspace screens. 3151 */ 3152 private void onDropExternal(final int[] touchXY, final Object dragInfo, 3153 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) { 3154 final Runnable exitSpringLoadedRunnable = new Runnable() { 3155 @Override 3156 public void run() { 3157 mLauncher.exitSpringLoadedDragModeDelayed(true, false, null); 3158 } 3159 }; 3160 3161 ItemInfo info = (ItemInfo) dragInfo; 3162 int spanX = info.spanX; 3163 int spanY = info.spanY; 3164 if (mDragInfo != null) { 3165 spanX = mDragInfo.spanX; 3166 spanY = mDragInfo.spanY; 3167 } 3168 3169 final long container = mLauncher.isHotseatLayout(cellLayout) ? 3170 LauncherSettings.Favorites.CONTAINER_HOTSEAT : 3171 LauncherSettings.Favorites.CONTAINER_DESKTOP; 3172 final long screenId = getIdForScreen(cellLayout); 3173 if (!mLauncher.isHotseatLayout(cellLayout) 3174 && screenId != getScreenIdForPageIndex(mCurrentPage) 3175 && mState != State.SPRING_LOADED) { 3176 snapToScreenId(screenId, null); 3177 } 3178 3179 if (info instanceof PendingAddItemInfo) { 3180 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo; 3181 3182 boolean findNearestVacantCell = true; 3183 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 3184 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY, 3185 cellLayout, mTargetCell); 3186 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], 3187 mDragViewVisualCenter[1], mTargetCell); 3188 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell, 3189 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo, 3190 cellLayout, mTargetCell, distance)) { 3191 findNearestVacantCell = false; 3192 } 3193 } 3194 3195 final ItemInfo item = (ItemInfo) d.dragInfo; 3196 boolean updateWidgetSize = false; 3197 if (findNearestVacantCell) { 3198 int minSpanX = item.spanX; 3199 int minSpanY = item.spanY; 3200 if (item.minSpanX > 0 && item.minSpanY > 0) { 3201 minSpanX = item.minSpanX; 3202 minSpanY = item.minSpanY; 3203 } 3204 int[] resultSpan = new int[2]; 3205 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0], 3206 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY, 3207 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL); 3208 3209 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) { 3210 updateWidgetSize = true; 3211 } 3212 item.spanX = resultSpan[0]; 3213 item.spanY = resultSpan[1]; 3214 } 3215 3216 Runnable onAnimationCompleteRunnable = new Runnable() { 3217 @Override 3218 public void run() { 3219 // When dragging and dropping from customization tray, we deal with creating 3220 // widgets/shortcuts/folders in a slightly different way 3221 switch (pendingInfo.itemType) { 3222 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 3223 int span[] = new int[2]; 3224 span[0] = item.spanX; 3225 span[1] = item.spanY; 3226 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo, 3227 container, screenId, mTargetCell, span, null); 3228 break; 3229 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 3230 mLauncher.processShortcutFromDrop(pendingInfo.componentName, 3231 container, screenId, mTargetCell, null); 3232 break; 3233 default: 3234 throw new IllegalStateException("Unknown item type: " + 3235 pendingInfo.itemType); 3236 } 3237 } 3238 }; 3239 View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET 3240 ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null; 3241 3242 if (finalView instanceof AppWidgetHostView && updateWidgetSize) { 3243 AppWidgetHostView awhv = (AppWidgetHostView) finalView; 3244 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX, 3245 item.spanY); 3246 } 3247 3248 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR; 3249 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && 3250 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) { 3251 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN; 3252 } 3253 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable, 3254 animationStyle, finalView, true); 3255 } else { 3256 // This is for other drag/drop cases, like dragging from All Apps 3257 View view = null; 3258 3259 switch (info.itemType) { 3260 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 3261 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 3262 if (info.container == NO_ID && info instanceof ApplicationInfo) { 3263 // Came from all apps -- make a copy 3264 info = new ShortcutInfo((ApplicationInfo) info); 3265 } 3266 view = mLauncher.createShortcut(R.layout.application, cellLayout, 3267 (ShortcutInfo) info); 3268 break; 3269 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 3270 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout, 3271 (FolderInfo) info, mIconCache); 3272 break; 3273 default: 3274 throw new IllegalStateException("Unknown item type: " + info.itemType); 3275 } 3276 3277 // First we find the cell nearest to point at which the item is 3278 // dropped, without any consideration to whether there is an item there. 3279 if (touchXY != null) { 3280 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY, 3281 cellLayout, mTargetCell); 3282 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0], 3283 mDragViewVisualCenter[1], mTargetCell); 3284 d.postAnimationRunnable = exitSpringLoadedRunnable; 3285 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance, 3286 true, d.dragView, d.postAnimationRunnable)) { 3287 return; 3288 } 3289 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d, 3290 true)) { 3291 return; 3292 } 3293 } 3294 3295 if (touchXY != null) { 3296 // when dragging and dropping, just find the closest free spot 3297 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0], 3298 (int) mDragViewVisualCenter[1], 1, 1, 1, 1, 3299 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL); 3300 } else { 3301 cellLayout.findCellForSpan(mTargetCell, 1, 1); 3302 } 3303 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX, 3304 info.spanY, insertAtFirst); 3305 cellLayout.onDropChild(view); 3306 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 3307 cellLayout.getShortcutsAndWidgets().measureChild(view); 3308 3309 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, 3310 lp.cellX, lp.cellY); 3311 3312 if (d.dragView != null) { 3313 // We wrap the animation call in the temporary set and reset of the current 3314 // cellLayout to its final transform -- this means we animate the drag view to 3315 // the correct final location. 3316 setFinalTransitionTransform(cellLayout); 3317 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, 3318 exitSpringLoadedRunnable); 3319 resetTransitionTransform(cellLayout); 3320 } 3321 } 3322 } 3323 3324 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) { 3325 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX, 3326 widgetInfo.spanY, widgetInfo, false); 3327 int visibility = layout.getVisibility(); 3328 layout.setVisibility(VISIBLE); 3329 3330 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY); 3331 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY); 3332 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1], 3333 Bitmap.Config.ARGB_8888); 3334 Canvas c = new Canvas(b); 3335 3336 layout.measure(width, height); 3337 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]); 3338 layout.draw(c); 3339 c.setBitmap(null); 3340 layout.setVisibility(visibility); 3341 return b; 3342 } 3343 3344 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY, 3345 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell, 3346 boolean external, boolean scale) { 3347 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final 3348 // location and size on the home screen. 3349 int spanX = info.spanX; 3350 int spanY = info.spanY; 3351 3352 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY); 3353 loc[0] = r.left; 3354 loc[1] = r.top; 3355 3356 setFinalTransitionTransform(layout); 3357 float cellLayoutScale = 3358 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc); 3359 resetTransitionTransform(layout); 3360 3361 float dragViewScaleX; 3362 float dragViewScaleY; 3363 if (scale) { 3364 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth(); 3365 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight(); 3366 } else { 3367 dragViewScaleX = 1f; 3368 dragViewScaleY = 1f; 3369 } 3370 3371 // The animation will scale the dragView about its center, so we need to center about 3372 // the final location. 3373 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2; 3374 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2; 3375 3376 scaleXY[0] = dragViewScaleX * cellLayoutScale; 3377 scaleXY[1] = dragViewScaleY * cellLayoutScale; 3378 } 3379 3380 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView, 3381 final Runnable onCompleteRunnable, int animationType, final View finalView, 3382 boolean external) { 3383 Rect from = new Rect(); 3384 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from); 3385 3386 int[] finalPos = new int[2]; 3387 float scaleXY[] = new float[2]; 3388 boolean scalePreview = !(info instanceof PendingAddShortcutInfo); 3389 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell, 3390 external, scalePreview); 3391 3392 Resources res = mLauncher.getResources(); 3393 int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; 3394 3395 // In the case where we've prebound the widget, we remove it from the DragLayer 3396 if (finalView instanceof AppWidgetHostView && external) { 3397 Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView"); 3398 mLauncher.getDragLayer().removeView(finalView); 3399 } 3400 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) { 3401 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView); 3402 dragView.setCrossFadeBitmap(crossFadeBitmap); 3403 dragView.crossFade((int) (duration * 0.8f)); 3404 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) { 3405 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]); 3406 } 3407 3408 DragLayer dragLayer = mLauncher.getDragLayer(); 3409 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) { 3410 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f, 3411 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration); 3412 } else { 3413 int endStyle; 3414 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) { 3415 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE; 3416 } else { 3417 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;; 3418 } 3419 3420 Runnable onComplete = new Runnable() { 3421 @Override 3422 public void run() { 3423 if (finalView != null) { 3424 finalView.setVisibility(VISIBLE); 3425 } 3426 if (onCompleteRunnable != null) { 3427 onCompleteRunnable.run(); 3428 } 3429 } 3430 }; 3431 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0], 3432 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle, 3433 duration, this); 3434 } 3435 } 3436 3437 public void setFinalTransitionTransform(CellLayout layout) { 3438 if (isSwitchingState()) { 3439 int index = indexOfChild(layout); 3440 mCurrentScaleX = layout.getScaleX(); 3441 mCurrentScaleY = layout.getScaleY(); 3442 mCurrentTranslationX = layout.getTranslationX(); 3443 mCurrentTranslationY = layout.getTranslationY(); 3444 mCurrentRotationY = layout.getRotationY(); 3445 layout.setScaleX(mNewScaleXs[index]); 3446 layout.setScaleY(mNewScaleYs[index]); 3447 layout.setTranslationX(mNewTranslationXs[index]); 3448 layout.setTranslationY(mNewTranslationYs[index]); 3449 layout.setRotationY(mNewRotationYs[index]); 3450 } 3451 } 3452 public void resetTransitionTransform(CellLayout layout) { 3453 if (isSwitchingState()) { 3454 mCurrentScaleX = layout.getScaleX(); 3455 mCurrentScaleY = layout.getScaleY(); 3456 mCurrentTranslationX = layout.getTranslationX(); 3457 mCurrentTranslationY = layout.getTranslationY(); 3458 mCurrentRotationY = layout.getRotationY(); 3459 layout.setScaleX(mCurrentScaleX); 3460 layout.setScaleY(mCurrentScaleY); 3461 layout.setTranslationX(mCurrentTranslationX); 3462 layout.setTranslationY(mCurrentTranslationY); 3463 layout.setRotationY(mCurrentRotationY); 3464 } 3465 } 3466 3467 /** 3468 * Return the current {@link CellLayout}, correctly picking the destination 3469 * screen while a scroll is in progress. 3470 */ 3471 public CellLayout getCurrentDropLayout() { 3472 return (CellLayout) getChildAt(getNextPage()); 3473 } 3474 3475 /** 3476 * Return the current CellInfo describing our current drag; this method exists 3477 * so that Launcher can sync this object with the correct info when the activity is created/ 3478 * destroyed 3479 * 3480 */ 3481 public CellLayout.CellInfo getDragInfo() { 3482 return mDragInfo; 3483 } 3484 3485 /** 3486 * Calculate the nearest cell where the given object would be dropped. 3487 * 3488 * pixelX and pixelY should be in the coordinate system of layout 3489 */ 3490 private int[] findNearestArea(int pixelX, int pixelY, 3491 int spanX, int spanY, CellLayout layout, int[] recycle) { 3492 return layout.findNearestArea( 3493 pixelX, pixelY, spanX, spanY, recycle); 3494 } 3495 3496 void setup(DragController dragController) { 3497 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher); 3498 mDragController = dragController; 3499 3500 // hardware layers on children are enabled on startup, but should be disabled until 3501 // needed 3502 updateChildrenLayersEnabled(false); 3503 setWallpaperDimension(); 3504 } 3505 3506 /** 3507 * Called at the end of a drag which originated on the workspace. 3508 */ 3509 public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, 3510 boolean success) { 3511 if (success) { 3512 if (target != this) { 3513 if (mDragInfo != null) { 3514 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); 3515 if (mDragInfo.cell instanceof DropTarget) { 3516 mDragController.removeDropTarget((DropTarget) mDragInfo.cell); 3517 } 3518 } 3519 } 3520 } else if (mDragInfo != null) { 3521 CellLayout cellLayout; 3522 if (mLauncher.isHotseatLayout(target)) { 3523 cellLayout = mLauncher.getHotseat().getLayout(); 3524 } else { 3525 cellLayout = getScreenWithId(mDragInfo.screenId); 3526 } 3527 cellLayout.onDropChild(mDragInfo.cell); 3528 } 3529 if (d.cancelled && mDragInfo.cell != null) { 3530 mDragInfo.cell.setVisibility(VISIBLE); 3531 } 3532 mDragOutline = null; 3533 mDragInfo = null; 3534 3535 stripEmptyScreens(); 3536 3537 // Hide the scrolling indicator after you pick up an item 3538 hideScrollingIndicator(false); 3539 } 3540 3541 void updateItemLocationsInDatabase(CellLayout cl) { 3542 int count = cl.getShortcutsAndWidgets().getChildCount(); 3543 3544 long screenId = getIdForScreen(cl); 3545 int container = Favorites.CONTAINER_DESKTOP; 3546 3547 if (mLauncher.isHotseatLayout(cl)) { 3548 screenId = -1; 3549 container = Favorites.CONTAINER_HOTSEAT; 3550 } 3551 3552 for (int i = 0; i < count; i++) { 3553 View v = cl.getShortcutsAndWidgets().getChildAt(i); 3554 ItemInfo info = (ItemInfo) v.getTag(); 3555 // Null check required as the AllApps button doesn't have an item info 3556 if (info != null && info.requiresDbUpdate) { 3557 info.requiresDbUpdate = false; 3558 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX, 3559 info.cellY, info.spanX, info.spanY); 3560 } 3561 } 3562 } 3563 3564 ArrayList<ComponentName> stripDuplicateApps() { 3565 ArrayList<ComponentName> uniqueIntents = new ArrayList<ComponentName>(); 3566 stripDuplicateApps((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents); 3567 int count = getChildCount(); 3568 for (int i = 0; i < count; i++) { 3569 CellLayout cl = (CellLayout) getChildAt(i); 3570 stripDuplicateApps(cl, uniqueIntents); 3571 } 3572 return uniqueIntents; 3573 } 3574 3575 void stripDuplicateApps(CellLayout cl, ArrayList<ComponentName> uniqueIntents) { 3576 int count = cl.getShortcutsAndWidgets().getChildCount(); 3577 3578 ArrayList<View> children = new ArrayList<View>(); 3579 for (int i = 0; i < count; i++) { 3580 View v = cl.getShortcutsAndWidgets().getChildAt(i); 3581 children.add(v); 3582 } 3583 3584 for (int i = 0; i < count; i++) { 3585 View v = children.get(i); 3586 ItemInfo info = (ItemInfo) v.getTag(); 3587 // Null check required as the AllApps button doesn't have an item info 3588 if (info instanceof ShortcutInfo) { 3589 ShortcutInfo si = (ShortcutInfo) info; 3590 ComponentName cn = si.intent.getComponent(); 3591 3592 if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 3593 continue; 3594 } 3595 3596 if (!uniqueIntents.contains(cn)) { 3597 uniqueIntents.add(cn); 3598 } else { 3599 cl.removeViewInLayout(v); 3600 LauncherModel.deleteItemFromDatabase(mLauncher, si); 3601 } 3602 } 3603 if (v instanceof FolderIcon) { 3604 FolderIcon fi = (FolderIcon) v; 3605 ArrayList<View> items = fi.getFolder().getItemsInReadingOrder(); 3606 for (int j = 0; j < items.size(); j++) { 3607 if (items.get(j).getTag() instanceof ShortcutInfo) { 3608 ShortcutInfo si = (ShortcutInfo) items.get(j).getTag(); 3609 ComponentName cn = si.intent.getComponent(); 3610 3611 if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 3612 continue; 3613 } 3614 if (!uniqueIntents.contains(cn)) { 3615 uniqueIntents.add(cn); 3616 } else { 3617 fi.getFolderInfo().remove(si); 3618 LauncherModel.deleteItemFromDatabase(mLauncher, si); 3619 } 3620 } 3621 } 3622 } 3623 } 3624 } 3625 3626 void saveWorkspaceToDb() { 3627 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout()); 3628 int count = getChildCount(); 3629 for (int i = 0; i < count; i++) { 3630 CellLayout cl = (CellLayout) getChildAt(i); 3631 saveWorkspaceScreenToDb(cl); 3632 } 3633 } 3634 3635 void saveWorkspaceScreenToDb(CellLayout cl) { 3636 int count = cl.getShortcutsAndWidgets().getChildCount(); 3637 3638 long screenId = getIdForScreen(cl); 3639 int container = Favorites.CONTAINER_DESKTOP; 3640 3641 Hotseat hotseat = mLauncher.getHotseat(); 3642 if (mLauncher.isHotseatLayout(cl)) { 3643 screenId = -1; 3644 container = Favorites.CONTAINER_HOTSEAT; 3645 } 3646 3647 for (int i = 0; i < count; i++) { 3648 View v = cl.getShortcutsAndWidgets().getChildAt(i); 3649 ItemInfo info = (ItemInfo) v.getTag(); 3650 // Null check required as the AllApps button doesn't have an item info 3651 if (info != null) { 3652 int cellX = info.cellX; 3653 int cellY = info.cellY; 3654 if (container == Favorites.CONTAINER_HOTSEAT) { 3655 cellX = hotseat.getCellXFromOrder((int) info.screenId); 3656 cellY = hotseat.getCellYFromOrder((int) info.screenId); 3657 } 3658 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, 3659 cellY, false); 3660 } 3661 if (v instanceof FolderIcon) { 3662 FolderIcon fi = (FolderIcon) v; 3663 fi.getFolder().addItemLocationsInDatabase(); 3664 } 3665 } 3666 } 3667 3668 @Override 3669 public boolean supportsFlingToDelete() { 3670 return true; 3671 } 3672 3673 @Override 3674 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { 3675 // Do nothing 3676 } 3677 3678 @Override 3679 public void onFlingToDeleteCompleted() { 3680 // Do nothing 3681 } 3682 3683 public boolean isDropEnabled() { 3684 return true; 3685 } 3686 3687 @Override 3688 protected void onRestoreInstanceState(Parcelable state) { 3689 super.onRestoreInstanceState(state); 3690 Launcher.setScreen(mCurrentPage); 3691 } 3692 3693 @Override 3694 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 3695 // We don't dispatch restoreInstanceState to our children using this code path. 3696 // Some pages will be restored immediately as their items are bound immediately, and 3697 // others we will need to wait until after their items are bound. 3698 mSavedStates = container; 3699 } 3700 3701 public void restoreInstanceStateForChild(int child) { 3702 if (mSavedStates != null) { 3703 mRestoredPages.add(child); 3704 CellLayout cl = (CellLayout) getChildAt(child); 3705 cl.restoreInstanceState(mSavedStates); 3706 } 3707 } 3708 3709 public void restoreInstanceStateForRemainingPages() { 3710 int count = getChildCount(); 3711 for (int i = 0; i < count; i++) { 3712 if (!mRestoredPages.contains(i)) { 3713 restoreInstanceStateForChild(i); 3714 } 3715 } 3716 mRestoredPages.clear(); 3717 } 3718 3719 @Override 3720 public void scrollLeft() { 3721 if (!isSmall() && !mIsSwitchingState) { 3722 super.scrollLeft(); 3723 } 3724 Folder openFolder = getOpenFolder(); 3725 if (openFolder != null) { 3726 openFolder.completeDragExit(); 3727 } 3728 } 3729 3730 @Override 3731 public void scrollRight() { 3732 if (!isSmall() && !mIsSwitchingState) { 3733 super.scrollRight(); 3734 } 3735 Folder openFolder = getOpenFolder(); 3736 if (openFolder != null) { 3737 openFolder.completeDragExit(); 3738 } 3739 } 3740 3741 @Override 3742 public boolean onEnterScrollArea(int x, int y, int direction) { 3743 // Ignore the scroll area if we are dragging over the hot seat 3744 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext()); 3745 if (mLauncher.getHotseat() != null && isPortrait) { 3746 Rect r = new Rect(); 3747 mLauncher.getHotseat().getHitRect(r); 3748 if (r.contains(x, y)) { 3749 return false; 3750 } 3751 } 3752 3753 boolean result = false; 3754 if (!isSmall() && !mIsSwitchingState) { 3755 mInScrollArea = true; 3756 3757 final int page = getNextPage() + 3758 (direction == DragController.SCROLL_LEFT ? -1 : 1); 3759 3760 // We always want to exit the current layout to ensure parity of enter / exit 3761 setCurrentDropLayout(null); 3762 3763 if (0 <= page && page < getChildCount()) { 3764 CellLayout layout = (CellLayout) getChildAt(page); 3765 setCurrentDragOverlappingLayout(layout); 3766 3767 // Workspace is responsible for drawing the edge glow on adjacent pages, 3768 // so we need to redraw the workspace when this may have changed. 3769 invalidate(); 3770 result = true; 3771 } 3772 } 3773 return result; 3774 } 3775 3776 @Override 3777 public boolean onExitScrollArea() { 3778 boolean result = false; 3779 if (mInScrollArea) { 3780 invalidate(); 3781 CellLayout layout = getCurrentDropLayout(); 3782 setCurrentDropLayout(layout); 3783 setCurrentDragOverlappingLayout(layout); 3784 3785 result = true; 3786 mInScrollArea = false; 3787 } 3788 return result; 3789 } 3790 3791 private void onResetScrollArea() { 3792 setCurrentDragOverlappingLayout(null); 3793 mInScrollArea = false; 3794 } 3795 3796 /** 3797 * Returns a specific CellLayout 3798 */ 3799 CellLayout getParentCellLayoutForView(View v) { 3800 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts(); 3801 for (CellLayout layout : layouts) { 3802 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) { 3803 return layout; 3804 } 3805 } 3806 return null; 3807 } 3808 3809 /** 3810 * Returns a list of all the CellLayouts in the workspace. 3811 */ 3812 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() { 3813 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>(); 3814 int screenCount = getChildCount(); 3815 for (int screen = 0; screen < screenCount; screen++) { 3816 layouts.add(((CellLayout) getChildAt(screen))); 3817 } 3818 if (mLauncher.getHotseat() != null) { 3819 layouts.add(mLauncher.getHotseat().getLayout()); 3820 } 3821 return layouts; 3822 } 3823 3824 /** 3825 * We should only use this to search for specific children. Do not use this method to modify 3826 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from 3827 * the hotseat and workspace pages 3828 */ 3829 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() { 3830 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = 3831 new ArrayList<ShortcutAndWidgetContainer>(); 3832 int screenCount = getChildCount(); 3833 for (int screen = 0; screen < screenCount; screen++) { 3834 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets()); 3835 } 3836 if (mLauncher.getHotseat() != null) { 3837 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets()); 3838 } 3839 return childrenLayouts; 3840 } 3841 3842 public Folder getFolderForTag(Object tag) { 3843 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = 3844 getAllShortcutAndWidgetContainers(); 3845 for (ShortcutAndWidgetContainer layout: childrenLayouts) { 3846 int count = layout.getChildCount(); 3847 for (int i = 0; i < count; i++) { 3848 View child = layout.getChildAt(i); 3849 if (child instanceof Folder) { 3850 Folder f = (Folder) child; 3851 if (f.getInfo() == tag && f.getInfo().opened) { 3852 return f; 3853 } 3854 } 3855 } 3856 } 3857 return null; 3858 } 3859 3860 public View getViewForTag(Object tag) { 3861 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = 3862 getAllShortcutAndWidgetContainers(); 3863 for (ShortcutAndWidgetContainer layout: childrenLayouts) { 3864 int count = layout.getChildCount(); 3865 for (int i = 0; i < count; i++) { 3866 View child = layout.getChildAt(i); 3867 if (child.getTag() == tag) { 3868 return child; 3869 } 3870 } 3871 } 3872 return null; 3873 } 3874 3875 void clearDropTargets() { 3876 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = 3877 getAllShortcutAndWidgetContainers(); 3878 for (ShortcutAndWidgetContainer layout: childrenLayouts) { 3879 int childCount = layout.getChildCount(); 3880 for (int j = 0; j < childCount; j++) { 3881 View v = layout.getChildAt(j); 3882 if (v instanceof DropTarget) { 3883 mDragController.removeDropTarget((DropTarget) v); 3884 } 3885 } 3886 } 3887 } 3888 3889 // Removes ALL items that match a given package name, this is usually called when a package 3890 // has been removed and we want to remove all components (widgets, shortcuts, apps) that 3891 // belong to that package. 3892 void removeItemsByPackageName(final ArrayList<String> packages) { 3893 HashSet<String> packageNames = new HashSet<String>(); 3894 packageNames.addAll(packages); 3895 3896 // Just create a hash table of all the specific components that this will affect 3897 HashSet<ComponentName> cns = new HashSet<ComponentName>(); 3898 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts(); 3899 for (CellLayout layoutParent : cellLayouts) { 3900 ViewGroup layout = layoutParent.getShortcutsAndWidgets(); 3901 int childCount = layout.getChildCount(); 3902 for (int i = 0; i < childCount; ++i) { 3903 View view = layout.getChildAt(i); 3904 Object tag = view.getTag(); 3905 3906 if (tag instanceof ShortcutInfo) { 3907 ShortcutInfo info = (ShortcutInfo) tag; 3908 ComponentName cn = info.intent.getComponent(); 3909 if ((cn != null) && packageNames.contains(cn.getPackageName())) { 3910 cns.add(cn); 3911 } 3912 } else if (tag instanceof FolderInfo) { 3913 FolderInfo info = (FolderInfo) tag; 3914 for (ShortcutInfo s : info.contents) { 3915 ComponentName cn = s.intent.getComponent(); 3916 if ((cn != null) && packageNames.contains(cn.getPackageName())) { 3917 cns.add(cn); 3918 } 3919 } 3920 } else if (tag instanceof LauncherAppWidgetInfo) { 3921 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 3922 ComponentName cn = info.providerName; 3923 if ((cn != null) && packageNames.contains(cn.getPackageName())) { 3924 cns.add(cn); 3925 } 3926 } 3927 } 3928 } 3929 3930 // Remove all the things 3931 removeItemsByComponentName(cns); 3932 } 3933 3934 // Removes items that match the application info specified, when applications are removed 3935 // as a part of an update, this is called to ensure that other widgets and application 3936 // shortcuts are not removed. 3937 void removeItemsByApplicationInfo(final ArrayList<ApplicationInfo> appInfos) { 3938 // Just create a hash table of all the specific components that this will affect 3939 HashSet<ComponentName> cns = new HashSet<ComponentName>(); 3940 for (ApplicationInfo info : appInfos) { 3941 cns.add(info.componentName); 3942 } 3943 3944 // Remove all the things 3945 removeItemsByComponentName(cns); 3946 } 3947 3948 void removeItemsByComponentName(final HashSet<ComponentName> componentNames) { 3949 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts(); 3950 for (final CellLayout layoutParent: cellLayouts) { 3951 final ViewGroup layout = layoutParent.getShortcutsAndWidgets(); 3952 3953 // Avoid ANRs by treating each screen separately 3954 post(new Runnable() { 3955 public void run() { 3956 final ArrayList<View> childrenToRemove = new ArrayList<View>(); 3957 childrenToRemove.clear(); 3958 3959 int childCount = layout.getChildCount(); 3960 for (int j = 0; j < childCount; j++) { 3961 final View view = layout.getChildAt(j); 3962 Object tag = view.getTag(); 3963 3964 if (tag instanceof ShortcutInfo) { 3965 final ShortcutInfo info = (ShortcutInfo) tag; 3966 final Intent intent = info.intent; 3967 final ComponentName name = intent.getComponent(); 3968 3969 if (name != null) { 3970 if (componentNames.contains(name)) { 3971 LauncherModel.deleteItemFromDatabase(mLauncher, info); 3972 childrenToRemove.add(view); 3973 } 3974 } 3975 } else if (tag instanceof FolderInfo) { 3976 final FolderInfo info = (FolderInfo) tag; 3977 final ArrayList<ShortcutInfo> contents = info.contents; 3978 final int contentsCount = contents.size(); 3979 final ArrayList<ShortcutInfo> appsToRemoveFromFolder = 3980 new ArrayList<ShortcutInfo>(); 3981 3982 for (int k = 0; k < contentsCount; k++) { 3983 final ShortcutInfo appInfo = contents.get(k); 3984 final Intent intent = appInfo.intent; 3985 final ComponentName name = intent.getComponent(); 3986 3987 if (name != null) { 3988 if (componentNames.contains(name)) { 3989 appsToRemoveFromFolder.add(appInfo); 3990 } 3991 } 3992 } 3993 for (ShortcutInfo item: appsToRemoveFromFolder) { 3994 info.remove(item); 3995 LauncherModel.deleteItemFromDatabase(mLauncher, item); 3996 } 3997 } else if (tag instanceof LauncherAppWidgetInfo) { 3998 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag; 3999 final ComponentName provider = info.providerName; 4000 if (provider != null) { 4001 if (componentNames.contains(provider)) { 4002 LauncherModel.deleteItemFromDatabase(mLauncher, info); 4003 childrenToRemove.add(view); 4004 } 4005 } 4006 } 4007 } 4008 4009 childCount = childrenToRemove.size(); 4010 for (int j = 0; j < childCount; j++) { 4011 View child = childrenToRemove.get(j); 4012 // Note: We can not remove the view directly from CellLayoutChildren as this 4013 // does not re-mark the spaces as unoccupied. 4014 layoutParent.removeViewInLayout(child); 4015 if (child instanceof DropTarget) { 4016 mDragController.removeDropTarget((DropTarget)child); 4017 } 4018 } 4019 4020 if (childCount > 0) { 4021 layout.requestLayout(); 4022 layout.invalidate(); 4023 } 4024 } 4025 }); 4026 } 4027 4028 // Clean up new-apps animation list 4029 final Context context = getContext(); 4030 post(new Runnable() { 4031 @Override 4032 public void run() { 4033 String spKey = LauncherAppState.getSharedPreferencesKey(); 4034 SharedPreferences sp = context.getSharedPreferences(spKey, 4035 Context.MODE_PRIVATE); 4036 Set<String> newApps = sp.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, 4037 null); 4038 4039 // Remove all queued items that match the same package 4040 if (newApps != null) { 4041 synchronized (newApps) { 4042 Iterator<String> iter = newApps.iterator(); 4043 while (iter.hasNext()) { 4044 try { 4045 Intent intent = Intent.parseUri(iter.next(), 0); 4046 if (componentNames.contains(intent.getComponent())) { 4047 iter.remove(); 4048 } 4049 4050 // It is possible that we've queued an item to be loaded, yet it has 4051 // not been added to the workspace, so remove those items as well. 4052 ArrayList<ItemInfo> shortcuts; 4053 shortcuts = LauncherModel.getWorkspaceShortcutItemInfosWithIntent( 4054 intent); 4055 for (ItemInfo info : shortcuts) { 4056 LauncherModel.deleteItemFromDatabase(context, info); 4057 } 4058 } catch (URISyntaxException e) {} 4059 } 4060 } 4061 } 4062 } 4063 }); 4064 } 4065 4066 void updateShortcuts(ArrayList<ApplicationInfo> apps) { 4067 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers(); 4068 for (ShortcutAndWidgetContainer layout: childrenLayouts) { 4069 int childCount = layout.getChildCount(); 4070 for (int j = 0; j < childCount; j++) { 4071 final View view = layout.getChildAt(j); 4072 Object tag = view.getTag(); 4073 if (tag instanceof ShortcutInfo) { 4074 ShortcutInfo info = (ShortcutInfo) tag; 4075 // We need to check for ACTION_MAIN otherwise getComponent() might 4076 // return null for some shortcuts (for instance, for shortcuts to 4077 // web pages.) 4078 final Intent intent = info.intent; 4079 final ComponentName name = intent.getComponent(); 4080 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 4081 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 4082 final int appCount = apps.size(); 4083 for (int k = 0; k < appCount; k++) { 4084 ApplicationInfo app = apps.get(k); 4085 if (app.componentName.equals(name)) { 4086 BubbleTextView shortcut = (BubbleTextView) view; 4087 info.updateIcon(mIconCache); 4088 info.title = app.title.toString(); 4089 shortcut.applyFromShortcutInfo(info, mIconCache); 4090 } 4091 } 4092 } 4093 } 4094 } 4095 } 4096 } 4097 4098 void moveToDefaultScreen(boolean animate) { 4099 if (!isSmall()) { 4100 if (animate) { 4101 snapToPage(mDefaultPage); 4102 } else { 4103 setCurrentPage(mDefaultPage); 4104 } 4105 } 4106 getChildAt(mDefaultPage).requestFocus(); 4107 } 4108 4109 @Override 4110 public void syncPages() { 4111 } 4112 4113 @Override 4114 public void syncPageItems(int page, boolean immediate) { 4115 } 4116 4117 @Override 4118 protected String getCurrentPageDescription() { 4119 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; 4120 return String.format(getContext().getString(R.string.workspace_scroll_format), 4121 page + 1, getChildCount()); 4122 } 4123 4124 public void getLocationInDragLayer(int[] loc) { 4125 mLauncher.getDragLayer().getLocationInDragLayer(this, loc); 4126 } 4127 4128 void setFadeForOverScroll(float fade) { 4129 if (!isScrollingIndicatorEnabled()) return; 4130 4131 mOverscrollFade = fade; 4132 float reducedFade = 0.5f + 0.5f * (1 - fade); 4133 final ViewGroup parent = (ViewGroup) getParent(); 4134 final ImageView qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider)); 4135 final ImageView dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider)); 4136 final View scrollIndicator = getScrollingIndicator(); 4137 4138 cancelScrollingIndicatorAnimations(); 4139 if (qsbDivider != null) qsbDivider.setAlpha(reducedFade); 4140 if (dockDivider != null) dockDivider.setAlpha(reducedFade); 4141 scrollIndicator.setAlpha(1 - fade); 4142 } 4143} 4144