Folder.java revision bfbfd26c627a18f8e1e3e6d0e53e78feab360203
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher2; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.animation.PropertyValuesHolder; 23import android.animation.ValueAnimator; 24import android.animation.ValueAnimator.AnimatorUpdateListener; 25import android.content.Context; 26import android.graphics.Color; 27import android.graphics.Rect; 28import android.graphics.drawable.Drawable; 29import android.util.AttributeSet; 30import android.view.LayoutInflater; 31import android.view.MotionEvent; 32import android.view.View; 33import android.view.View.OnClickListener; 34import android.view.animation.AccelerateInterpolator; 35import android.view.animation.DecelerateInterpolator; 36import android.widget.AdapterView; 37import android.widget.LinearLayout; 38import android.widget.TextView; 39import android.widget.AdapterView.OnItemClickListener; 40import android.widget.AdapterView.OnItemLongClickListener; 41 42import com.android.launcher.R; 43import com.android.launcher2.FolderInfo.FolderListener; 44 45import java.util.ArrayList; 46 47/** 48 * Represents a set of icons chosen by the user or generated by the system. 49 */ 50public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener, 51 OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener { 52 53 protected DragController mDragController; 54 55 protected Launcher mLauncher; 56 57 protected FolderInfo mInfo; 58 59 private static final String TAG = "Launcher.Folder"; 60 61 static final int STATE_NONE = -1; 62 static final int STATE_SMALL = 0; 63 static final int STATE_ANIMATING = 1; 64 static final int STATE_OPEN = 2; 65 66 private int mExpandDuration; 67 protected CellLayout mContent; 68 private final LayoutInflater mInflater; 69 private final IconCache mIconCache; 70 private int mState = STATE_NONE; 71 private int[] mDragItemPosition = new int[2]; 72 private static final int FULL_GROW = 0; 73 private static final int PARTIAL_GROW = 1; 74 private static final int REORDER_ANIMATION_DURATION = 230; 75 private static final int ON_EXIT_CLOSE_DELAY = 800; 76 private int mMode = PARTIAL_GROW; 77 private boolean mRearrangeOnClose = false; 78 private FolderIcon mFolderIcon; 79 private int mMaxCountX; 80 private int mMaxCountY; 81 private Rect mNewSize = new Rect(); 82 private ArrayList<View> mItemsInReadingOrder = new ArrayList<View>(); 83 private Drawable mIconDrawable; 84 boolean mItemsInvalidated = false; 85 private ShortcutInfo mCurrentDragInfo; 86 private View mCurrentDragView; 87 boolean mSuppressOnAdd = false; 88 private int[] mTargetCell = new int[2]; 89 private int[] mPreviousTargetCell = new int[2]; 90 private int[] mEmptyCell = new int[2]; 91 private Alarm mReorderAlarm = new Alarm(); 92 private Alarm mOnExitAlarm = new Alarm(); 93 94 /** 95 * Used to inflate the Workspace from XML. 96 * 97 * @param context The application's context. 98 * @param attrs The attribtues set containing the Workspace's customization values. 99 */ 100 public Folder(Context context, AttributeSet attrs) { 101 super(context, attrs); 102 setAlwaysDrawnWithCacheEnabled(false); 103 mInflater = LayoutInflater.from(context); 104 mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache(); 105 mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration); 106 107 mMaxCountX = LauncherModel.getCellCountX() - 1; 108 mMaxCountY = LauncherModel.getCellCountY() - 1; 109 } 110 111 @Override 112 protected void onFinishInflate() { 113 super.onFinishInflate(); 114 mContent = (CellLayout) findViewById(R.id.folder_content); 115 mContent.setGridSize(0, 0); 116 mContent.enableHardwareLayers(); 117 } 118 119 public void onItemClick(AdapterView parent, View v, int position, long id) { 120 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); 121 int[] pos = new int[2]; 122 v.getLocationOnScreen(pos); 123 app.intent.setSourceBounds(new Rect(pos[0], pos[1], 124 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 125 mLauncher.startActivitySafely(app.intent, app); 126 } 127 128 public void onClick(View v) { 129 Object tag = v.getTag(); 130 if (tag instanceof ShortcutInfo) { 131 // refactor this code from Folder 132 ShortcutInfo item = (ShortcutInfo) tag; 133 int[] pos = new int[2]; 134 v.getLocationOnScreen(pos); 135 item.intent.setSourceBounds(new Rect(pos[0], pos[1], 136 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 137 mLauncher.startActivitySafely(item.intent, item); 138 } 139 } 140 141 public boolean onLongClick(View v) { 142 Object tag = v.getTag(); 143 if (tag instanceof ShortcutInfo) { 144 ShortcutInfo item = (ShortcutInfo) tag; 145 if (!v.isInTouchMode()) { 146 return false; 147 } 148 149 mLauncher.getWorkspace().onDragStartedWithItem(v); 150 mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY); 151 mDragItemPosition[0] = item.cellX; 152 mDragItemPosition[1] = item.cellY; 153 mIconDrawable = ((TextView) v).getCompoundDrawables()[1]; 154 155 mCurrentDragInfo = item; 156 mEmptyCell[0] = item.cellX; 157 mEmptyCell[1] = item.cellY; 158 mCurrentDragView = v; 159 mContent.removeView(mCurrentDragView); 160 mInfo.remove(item); 161 } else { 162 mLauncher.closeFolder(this); 163 mLauncher.showRenameDialog(mInfo); 164 } 165 return true; 166 } 167 168 public Drawable getDragDrawable() { 169 return mIconDrawable; 170 } 171 172 /** 173 * We need to handle touch events to prevent them from falling through to the workspace below. 174 */ 175 @Override 176 public boolean onTouchEvent(MotionEvent ev) { 177 return true; 178 } 179 180 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 181 if (!view.isInTouchMode()) { 182 return false; 183 } 184 185 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); 186 187 mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY); 188 mLauncher.closeFolder(this); 189 return true; 190 } 191 192 public void setDragController(DragController dragController) { 193 mDragController = dragController; 194 } 195 196 public void onDragViewVisible() { 197 } 198 199 void setLauncher(Launcher launcher) { 200 mLauncher = launcher; 201 } 202 203 void setFolderIcon(FolderIcon icon) { 204 mFolderIcon = icon; 205 } 206 207 /** 208 * @return the FolderInfo object associated with this folder 209 */ 210 FolderInfo getInfo() { 211 return mInfo; 212 } 213 214 void onOpen() { 215 // When the folder opens, we need to refresh the GridView's selection by 216 // forcing a layout 217 // TODO: find out if this is still necessary 218 mContent.requestLayout(); 219 requestFocus(); 220 } 221 222 void onClose() { 223 final Workspace workspace = mLauncher.getWorkspace(); 224 workspace.getChildAt(workspace.getCurrentPage()).requestFocus(); 225 } 226 227 void bind(FolderInfo info) { 228 mInfo = info; 229 ArrayList<ShortcutInfo> children = info.contents; 230 setupContentForNumItems(children.size()); 231 for (int i = 0; i < children.size(); i++) { 232 ShortcutInfo child = (ShortcutInfo) children.get(i); 233 createAndAddShortcut(child); 234 } 235 mItemsInvalidated = true; 236 mInfo.addListener(this); 237 } 238 239 /** 240 * Creates a new UserFolder, inflated from R.layout.user_folder. 241 * 242 * @param context The application's context. 243 * 244 * @return A new UserFolder. 245 */ 246 static Folder fromXml(Context context) { 247 return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null); 248 } 249 250 /** 251 * This method is intended to make the UserFolder to be visually identical in size and position 252 * to its associated FolderIcon. This allows for a seamless transition into the expanded state. 253 */ 254 private void positionAndSizeAsIcon() { 255 if (!(getParent() instanceof CellLayoutChildren)) return; 256 257 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) mFolderIcon.getLayoutParams(); 258 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 259 260 if (mMode == PARTIAL_GROW) { 261 setScaleX(0.8f); 262 setScaleY(0.8f); 263 setAlpha(0f); 264 } else { 265 lp.width = iconLp.width; 266 lp.height = iconLp.height; 267 lp.x = iconLp.x; 268 lp.y = iconLp.y; 269 mContent.setAlpha(0); 270 } 271 mState = STATE_SMALL; 272 } 273 274 public void animateOpen() { 275 if (mState != STATE_SMALL) { 276 positionAndSizeAsIcon(); 277 } 278 if (!(getParent() instanceof CellLayoutChildren)) return; 279 280 ObjectAnimator oa; 281 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 282 283 centerAboutIcon(); 284 if (mMode == PARTIAL_GROW) { 285 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1); 286 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f); 287 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f); 288 oa = ObjectAnimator.ofPropertyValuesHolder(this, alpha, scaleX, scaleY); 289 } else { 290 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", mNewSize.width()); 291 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", mNewSize.height()); 292 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", mNewSize.left); 293 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", mNewSize.top); 294 oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y); 295 oa.addUpdateListener(new AnimatorUpdateListener() { 296 public void onAnimationUpdate(ValueAnimator animation) { 297 requestLayout(); 298 } 299 }); 300 301 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f); 302 ObjectAnimator alphaOa = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha); 303 alphaOa.setDuration(mExpandDuration); 304 alphaOa.setInterpolator(new AccelerateInterpolator(2.0f)); 305 alphaOa.start(); 306 } 307 308 oa.addListener(new AnimatorListenerAdapter() { 309 @Override 310 public void onAnimationStart(Animator animation) { 311 mState = STATE_ANIMATING; 312 } 313 @Override 314 public void onAnimationEnd(Animator animation) { 315 mState = STATE_OPEN; 316 } 317 }); 318 oa.setDuration(mExpandDuration); 319 oa.start(); 320 } 321 322 public void animateClosed() { 323 if (!(getParent() instanceof CellLayoutChildren)) return; 324 325 CellLayoutChildren clc = (CellLayoutChildren) getParent(); 326 final CellLayout cellLayout = (CellLayout) clc.getParent(); 327 ObjectAnimator oa; 328 329 if (mMode == PARTIAL_GROW) { 330 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0); 331 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f); 332 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.9f); 333 oa = ObjectAnimator.ofPropertyValuesHolder(this, alpha, scaleX, scaleY); 334 } else { 335 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) mFolderIcon.getLayoutParams(); 336 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 337 338 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", iconLp.width); 339 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", iconLp.height); 340 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x",iconLp.x); 341 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", iconLp.y); 342 oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y); 343 oa.addUpdateListener(new AnimatorUpdateListener() { 344 public void onAnimationUpdate(ValueAnimator animation) { 345 requestLayout(); 346 } 347 }); 348 349 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); 350 ObjectAnimator alphaOa = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha); 351 alphaOa.setDuration(mExpandDuration); 352 alphaOa.setInterpolator(new DecelerateInterpolator(2.0f)); 353 alphaOa.start(); 354 } 355 356 oa.addListener(new AnimatorListenerAdapter() { 357 @Override 358 public void onAnimationEnd(Animator animation) { 359 onCloseComplete(); 360 cellLayout.removeViewWithoutMarkingCells(Folder.this); 361 mState = STATE_SMALL; 362 } 363 @Override 364 public void onAnimationStart(Animator animation) { 365 mState = STATE_ANIMATING; 366 } 367 }); 368 oa.setDuration(mExpandDuration); 369 oa.start(); 370 } 371 372 void notifyDataSetChanged() { 373 // recreate all the children if the data set changes under us. We may want to do this more 374 // intelligently (ie just removing the views that should no longer exist) 375 mContent.removeAllViewsInLayout(); 376 bind(mInfo); 377 } 378 379 public boolean acceptDrop(DragObject d) { 380 final ItemInfo item = (ItemInfo) d.dragInfo; 381 final int itemType = item.itemType; 382 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 383 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && 384 !isFull()); 385 } 386 387 protected boolean findAndSetEmptyCells(ShortcutInfo item) { 388 int[] emptyCell = new int[2]; 389 if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) { 390 item.cellX = emptyCell[0]; 391 item.cellY = emptyCell[1]; 392 return true; 393 } else { 394 return false; 395 } 396 } 397 398 protected void createAndAddShortcut(ShortcutInfo item) { 399 final TextView textView = 400 (TextView) mInflater.inflate(R.layout.application_boxed, this, false); 401 textView.setCompoundDrawablesWithIntrinsicBounds(null, 402 new FastBitmapDrawable(item.getIcon(mIconCache)), null, null); 403 textView.setText(item.title); 404 textView.setTag(item); 405 406 textView.setOnClickListener(this); 407 textView.setOnLongClickListener(this); 408 409 CellLayout.LayoutParams lp = 410 new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY); 411 boolean insert = false; 412 mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true); 413 } 414 415 public void onDragEnter(DragObject d) { 416 mPreviousTargetCell[0] = -1; 417 mPreviousTargetCell[1] = -1; 418 mContent.onDragEnter(); 419 mOnExitAlarm.cancelAlarm(); 420 } 421 422 OnAlarmListener mReorderAlarmListener = new OnAlarmListener() { 423 public void onAlarm(Alarm alarm) { 424 realTimeReorder(mEmptyCell, mTargetCell); 425 } 426 }; 427 428 boolean readingOrderGreaterThan(int[] v1, int[] v2) { 429 if (v1[1] > v2[1] || (v1[1] == v2[1] && v1[0] > v2[0])) { 430 return true; 431 } else { 432 return false; 433 } 434 } 435 436 private void realTimeReorder(int[] empty, int[] target) { 437 boolean wrap; 438 int startX; 439 int endX; 440 int startY; 441 if (readingOrderGreaterThan(target, empty)) { 442 wrap = empty[0] >= mContent.getCountX() - 1; 443 startY = wrap ? empty[1] + 1 : empty[1]; 444 for (int y = startY; y <= target[1]; y++) { 445 startX = y == empty[1] ? empty[0] + 1 : 0; 446 endX = y < target[1] ? mContent.getCountX() - 1 : target[0]; 447 for (int x = startX; x <= endX; x++) { 448 View v = mContent.getChildAt(x,y); 449 if (mContent.animateChildToPosition(v, empty[0], empty[1], 450 REORDER_ANIMATION_DURATION)) { 451 empty[0] = x; 452 empty[1] = y; 453 } 454 } 455 } 456 } else { 457 wrap = empty[0] == 0; 458 startY = wrap ? empty[1] - 1 : empty[1]; 459 for (int y = startY; y >= target[1]; y--) { 460 startX = y == empty[1] ? empty[0] - 1 : mContent.getCountX() - 1; 461 endX = y > target[1] ? 0 : target[0]; 462 for (int x = startX; x >= endX; x--) { 463 View v = mContent.getChildAt(x,y); 464 if (mContent.animateChildToPosition(v, empty[0], empty[1], 465 REORDER_ANIMATION_DURATION)) { 466 empty[0] = x; 467 empty[1] = y; 468 } 469 } 470 } 471 } 472 } 473 474 public void onDragOver(DragObject d) { 475 float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, null); 476 mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1], 1, 1, mTargetCell); 477 478 if (mTargetCell[0] != mPreviousTargetCell[0] || mTargetCell[1] != mPreviousTargetCell[1]) { 479 mReorderAlarm.cancelAlarm(); 480 mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); 481 mReorderAlarm.setAlarm(150); 482 mPreviousTargetCell[0] = mTargetCell[0]; 483 mPreviousTargetCell[1] = mTargetCell[1]; 484 } 485 } 486 487 // This is used to compute the visual center of the dragView. The idea is that 488 // the visual center represents the user's interpretation of where the item is, and hence 489 // is the appropriate point to use when determining drop location. 490 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset, 491 DragView dragView, float[] recycle) { 492 float res[]; 493 if (recycle == null) { 494 res = new float[2]; 495 } else { 496 res = recycle; 497 } 498 499 // These represent the visual top and left of drag view if a dragRect was provided. 500 // If a dragRect was not provided, then they correspond to the actual view left and 501 // top, as the dragRect is in that case taken to be the entire dragView. 502 // R.dimen.dragViewOffsetY. 503 int left = x - xOffset; 504 int top = y - yOffset; 505 506 // In order to find the visual center, we shift by half the dragRect 507 res[0] = left + dragView.getDragRegion().width() / 2; 508 res[1] = top + dragView.getDragRegion().height() / 2; 509 510 return res; 511 } 512 513 OnAlarmListener mOnExitAlarmListener = new OnAlarmListener() { 514 public void onAlarm(Alarm alarm) { 515 mLauncher.closeFolder(Folder.this); 516 mCurrentDragInfo = null; 517 mCurrentDragView = null; 518 mSuppressOnAdd = false; 519 mRearrangeOnClose = true; 520 } 521 }; 522 523 public void onDragExit(DragObject d) { 524 // We only close the folder if this is a true drag exit, ie. not because a drop 525 // has occurred above the folder. 526 if (!d.dragComplete) { 527 mOnExitAlarm.setOnAlarmListener(mOnExitAlarmListener); 528 mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY); 529 } 530 mReorderAlarm.cancelAlarm(); 531 mContent.onDragExit(); 532 } 533 534 public void onDropCompleted(View target, DragObject d, boolean success) { 535 mCurrentDragInfo = null; 536 mCurrentDragView = null; 537 mSuppressOnAdd = false; 538 if (!success) { 539 if (d.dragView != null) { 540 if (target instanceof CellLayout) { 541 // TODO: we should animate the item back to the folder in this case 542 } 543 } 544 // TODO: if the drag fails, we need to re-add the item 545 } 546 } 547 548 public boolean isDropEnabled() { 549 return true; 550 } 551 552 public DropTarget getDropTargetDelegate(DragObject d) { 553 return null; 554 } 555 556 private void setupContentDimension(int count) { 557 ArrayList<View> list = getItemsInReadingOrder(); 558 559 int countX = mContent.getCountX(); 560 int countY = mContent.getCountY(); 561 boolean done = false; 562 563 while (!done) { 564 int oldCountX = countX; 565 int oldCountY = countY; 566 if (countX * countY < count) { 567 // Current grid is too small, expand it 568 if (countX <= countY && countX < mMaxCountX) { 569 countX++; 570 } else if (countY < mMaxCountY) { 571 countY++; 572 } 573 if (countY == 0) countY++; 574 } else if ((countY - 1) * countX >= count && countY >= countX) { 575 countY = Math.max(0, countY - 1); 576 } else if ((countX - 1) * countY >= count) { 577 countX = Math.max(0, countX - 1); 578 } 579 done = countX == oldCountX && countY == oldCountY; 580 } 581 mContent.setGridSize(countX, countY); 582 arrangeChildren(list); 583 } 584 585 public boolean isFull() { 586 return getItemCount() >= mMaxCountX * mMaxCountY; 587 } 588 589 private void centerAboutIcon() { 590 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) mFolderIcon.getLayoutParams(); 591 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 592 593 int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); 594 int height = getPaddingTop() + getPaddingBottom() + mContent.getDesiredHeight(); 595 596 int centerX = iconLp.x + iconLp.width / 2; 597 int centerY = iconLp.y + iconLp.height / 2; 598 int centeredLeft = centerX - width / 2; 599 int centeredTop = centerY - height / 2; 600 601 CellLayoutChildren clc = (CellLayoutChildren) getParent(); 602 int parentWidth = 0; 603 int parentHeight = 0; 604 if (clc != null) { 605 parentWidth = clc.getMeasuredWidth(); 606 parentHeight = clc.getMeasuredHeight(); 607 } 608 609 int left = Math.min(Math.max(0, centeredLeft), parentWidth - width); 610 int top = Math.min(Math.max(0, centeredTop), parentHeight - height); 611 612 int folderPivotX = width / 2 + (centeredLeft - left); 613 int folderPivotY = height / 2 + (centeredTop - top); 614 setPivotX(folderPivotX); 615 setPivotY(folderPivotY); 616 int folderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() * 617 (1.0f * folderPivotX / width)); 618 int folderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() * 619 (1.0f * folderPivotY / height)); 620 mFolderIcon.setPivotX(folderIconPivotX); 621 mFolderIcon.setPivotY(folderIconPivotY); 622 623 if (mMode == PARTIAL_GROW) { 624 lp.width = width; 625 lp.height = height; 626 lp.x = left; 627 lp.y = top; 628 } else { 629 mNewSize.set(left, top, left + width, top + height); 630 } 631 } 632 633 private void setupContentForNumItems(int count) { 634 setupContentDimension(count); 635 636 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 637 if (lp == null) { 638 lp = new CellLayout.LayoutParams(0, 0, -1, -1); 639 lp.isLockedToGrid = false; 640 setLayoutParams(lp); 641 } 642 centerAboutIcon(); 643 } 644 645 private void arrangeChildren(ArrayList<View> list) { 646 int[] vacant = new int[2]; 647 if (list == null) { 648 list = getItemsInReadingOrder(); 649 } 650 mContent.removeAllViews(); 651 652 for (int i = 0; i < list.size(); i++) { 653 View v = list.get(i); 654 mContent.getVacantCell(vacant, 1, 1); 655 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); 656 lp.cellX = vacant[0]; 657 lp.cellY = vacant[1]; 658 ItemInfo info = (ItemInfo) v.getTag(); 659 info.cellX = vacant[0]; 660 info.cellY = vacant[1]; 661 boolean insert = false; 662 mContent.addViewToCellLayout(v, insert ? 0 : -1, (int)info.id, lp, true); 663 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, mInfo.id, 0, 664 info.cellX, info.cellY); 665 } 666 mItemsInvalidated = true; 667 } 668 669 public int getItemCount() { 670 return mContent.getChildrenLayout().getChildCount(); 671 } 672 673 public View getItemAt(int index) { 674 return mContent.getChildrenLayout().getChildAt(index); 675 } 676 677 private void onCloseComplete() { 678 if (mRearrangeOnClose) { 679 setupContentForNumItems(getItemCount()); 680 mRearrangeOnClose = false; 681 } 682 } 683 684 public void onDrop(DragObject d) { 685 ShortcutInfo item; 686 if (d.dragInfo instanceof ApplicationInfo) { 687 // Came from all apps -- make a copy 688 item = ((ApplicationInfo) d.dragInfo).makeShortcut(); 689 item.spanX = 1; 690 item.spanY = 1; 691 } else { 692 item = (ShortcutInfo) d.dragInfo; 693 } 694 // Dragged from self onto self 695 if (item == mCurrentDragInfo) { 696 ShortcutInfo si = (ShortcutInfo) mCurrentDragView.getTag(); 697 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mCurrentDragView.getLayoutParams(); 698 si.cellX = lp.cellX = mEmptyCell[0]; 699 si.cellX = lp.cellY = mEmptyCell[1]; 700 mContent.addViewToCellLayout(mCurrentDragView, -1, (int)item.id, lp, true); 701 mSuppressOnAdd = true; 702 mItemsInvalidated = true; 703 setupContentDimension(getItemCount()); 704 } 705 mInfo.add(item); 706 } 707 708 public void onAdd(ShortcutInfo item) { 709 mItemsInvalidated = true; 710 if (mSuppressOnAdd) return; 711 if (!findAndSetEmptyCells(item)) { 712 // The current layout is full, can we expand it? 713 setupContentForNumItems(getItemCount() + 1); 714 findAndSetEmptyCells(item); 715 } 716 createAndAddShortcut(item); 717 LauncherModel.addOrMoveItemInDatabase( 718 mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); 719 } 720 721 public void onRemove(ShortcutInfo item) { 722 mItemsInvalidated = true; 723 if (item == mCurrentDragInfo) return; 724 View v = mContent.getChildAt(mDragItemPosition[0], mDragItemPosition[1]); 725 mContent.removeView(v); 726 if (mState == STATE_ANIMATING) { 727 mRearrangeOnClose = true; 728 } else { 729 setupContentForNumItems(getItemCount()); 730 } 731 } 732 733 public void onItemsChanged() { 734 } 735 736 public ArrayList<View> getItemsInReadingOrder() { 737 return getItemsInReadingOrder(true); 738 } 739 740 public ArrayList<View> getItemsInReadingOrder(boolean includeCurrentDragItem) { 741 if (mItemsInvalidated) { 742 mItemsInReadingOrder.clear(); 743 for (int j = 0; j < mContent.getCountY(); j++) { 744 for (int i = 0; i < mContent.getCountX(); i++) { 745 View v = mContent.getChildAt(i, j); 746 if (v != null) { 747 ShortcutInfo info = (ShortcutInfo) v.getTag(); 748 if (info != mCurrentDragInfo || includeCurrentDragItem) { 749 mItemsInReadingOrder.add(v); 750 } 751 } 752 } 753 } 754 mItemsInvalidated = false; 755 } 756 return mItemsInReadingOrder; 757 } 758} 759