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