Folder.java revision 0c872ba544ecfd9b106bb66137da8680927590de
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 java.util.ArrayList; 20 21import android.animation.Animator; 22import android.animation.AnimatorListenerAdapter; 23import android.animation.AnimatorSet; 24import android.animation.ObjectAnimator; 25import android.animation.PropertyValuesHolder; 26import android.animation.ValueAnimator; 27import android.animation.ValueAnimator.AnimatorUpdateListener; 28import android.content.Context; 29import android.graphics.Rect; 30import android.util.AttributeSet; 31import android.view.LayoutInflater; 32import android.view.MotionEvent; 33import android.view.View; 34import android.view.View.OnClickListener; 35import android.widget.AdapterView; 36import android.widget.Button; 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; 44import com.android.launcher2.Workspace.ShrinkState; 45 46/** 47 * Represents a set of icons chosen by the user or generated by the system. 48 */ 49public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener, 50 OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener { 51 52 protected DragController mDragController; 53 54 protected Launcher mLauncher; 55 56 protected Button mCloseButton; 57 58 protected FolderInfo mInfo; 59 60 /** 61 * Which item is being dragged 62 */ 63 protected ShortcutInfo mDragItem; 64 65 private static final String TAG = "Launcher.Folder"; 66 67 static final int STATE_NONE = -1; 68 static final int STATE_SMALL = 0; 69 static final int STATE_ANIMATING = 1; 70 static final int STATE_OPEN = 2; 71 72 private int mExpandDuration; 73 protected CellLayout mContent; 74 private final LayoutInflater mInflater; 75 private final IconCache mIconCache; 76 private int mState = STATE_NONE; 77 private int[] mDragItemPosition = new int[2]; 78 79 /** 80 * Used to inflate the Workspace from XML. 81 * 82 * @param context The application's context. 83 * @param attrs The attribtues set containing the Workspace's customization values. 84 */ 85 public Folder(Context context, AttributeSet attrs) { 86 super(context, attrs); 87 setAlwaysDrawnWithCacheEnabled(false); 88 mInflater = LayoutInflater.from(context); 89 mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache(); 90 mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration); 91 } 92 93 @Override 94 protected void onFinishInflate() { 95 super.onFinishInflate(); 96 97 mCloseButton = (Button) findViewById(R.id.folder_close); 98 mCloseButton.setOnClickListener(this); 99 mCloseButton.setOnLongClickListener(this); 100 mContent = (CellLayout) findViewById(R.id.folder_content); 101 } 102 103 public void onItemClick(AdapterView parent, View v, int position, long id) { 104 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); 105 int[] pos = new int[2]; 106 v.getLocationOnScreen(pos); 107 app.intent.setSourceBounds(new Rect(pos[0], pos[1], 108 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 109 mLauncher.startActivitySafely(app.intent, app); 110 } 111 112 public void onClick(View v) { 113 Object tag = v.getTag(); 114 if (tag instanceof ShortcutInfo) { 115 // refactor this code from Folder 116 ShortcutInfo item = (ShortcutInfo) tag; 117 int[] pos = new int[2]; 118 v.getLocationOnScreen(pos); 119 item.intent.setSourceBounds(new Rect(pos[0], pos[1], 120 pos[0] + v.getWidth(), pos[1] + v.getHeight())); 121 mLauncher.startActivitySafely(item.intent, item); 122 } else { 123 mLauncher.closeFolder(this); 124 } 125 } 126 127 public boolean onLongClick(View v) { 128 Object tag = v.getTag(); 129 if (tag instanceof ShortcutInfo) { 130 // refactor this code from Folder 131 ShortcutInfo item = (ShortcutInfo) tag; 132 if (!v.isInTouchMode()) { 133 return false; 134 } 135 136 mLauncher.getWorkspace().onDragStartedWithItem(v); 137 mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY); 138 mDragItemPosition[0] = item.cellX; 139 mDragItemPosition[1] = item.cellY; 140 mLauncher.closeFolder(this); 141 mDragItem = item; 142 } else { 143 mLauncher.closeFolder(this); 144 mLauncher.showRenameDialog(mInfo); 145 } 146 return true; 147 } 148 149 /** 150 * We need to handle touch events to prevent them from falling through to the workspace below. 151 */ 152 @Override 153 public boolean onTouchEvent(MotionEvent ev) { 154 return true; 155 } 156 157 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 158 if (!view.isInTouchMode()) { 159 return false; 160 } 161 162 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); 163 164 mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY); 165 mLauncher.closeFolder(this); 166 mDragItem = app; 167 168 return true; 169 } 170 171 public void setDragController(DragController dragController) { 172 mDragController = dragController; 173 } 174 175 public void onDragViewVisible() { 176 } 177 178 void setLauncher(Launcher launcher) { 179 mLauncher = launcher; 180 } 181 182 /** 183 * @return the FolderInfo object associated with this folder 184 */ 185 FolderInfo getInfo() { 186 return mInfo; 187 } 188 189 void onOpen() { 190 // When the folder opens, we need to refresh the GridView's selection by 191 // forcing a layout 192 // TODO: find out if this is still necessary 193 mContent.requestLayout(); 194 requestFocus(); 195 } 196 197 void onClose() { 198 final Workspace workspace = mLauncher.getWorkspace(); 199 workspace.getChildAt(workspace.getCurrentPage()).requestFocus(); 200 } 201 202 void bind(FolderInfo info) { 203 mInfo = info; 204 mCloseButton.setText(info.title); 205 ArrayList<ShortcutInfo> children = info.contents; 206 for (int i = 0; i < children.size(); i++) { 207 ShortcutInfo child = (ShortcutInfo) children.get(i); 208 if ((child.cellX == -1 && child.cellY == -1) || 209 mContent.isOccupied(child.cellX, child.cellY)) { 210 findAndSetEmptyCells(child); 211 } 212 createAndAddShortcut((ShortcutInfo) children.get(i)); 213 } 214 mInfo.addListener(this); 215 } 216 217 /** 218 * Creates a new UserFolder, inflated from R.layout.user_folder. 219 * 220 * @param context The application's context. 221 * 222 * @return A new UserFolder. 223 */ 224 static Folder fromXml(Context context) { 225 return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null); 226 } 227 228 /** 229 * This method is intended to make the UserFolder to be visually identical in size and position 230 * to its associated FolderIcon. This allows for a seamless transition into the expanded state. 231 */ 232 private void positionAndSizeAsIcon() { 233 if (!(getParent() instanceof CellLayoutChildren)) return; 234 235 CellLayoutChildren clc = (CellLayoutChildren) getParent(); 236 CellLayout cellLayout = (CellLayout) clc.getParent(); 237 238 FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY); 239 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams(); 240 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 241 242 lp.width = iconLp.width; 243 lp.height = iconLp.height; 244 lp.x = iconLp.x; 245 lp.y = iconLp.y; 246 247 mContent.setAlpha(0f); 248 mState = STATE_SMALL; 249 } 250 251 public void animateOpen() { 252 if (mState != STATE_SMALL) { 253 positionAndSizeAsIcon(); 254 } 255 if (!(getParent() instanceof CellLayoutChildren)) return; 256 257 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 258 259 CellLayoutChildren clc = (CellLayoutChildren) getParent(); 260 CellLayout cellLayout = (CellLayout) clc.getParent(); 261 Rect r = cellLayout.getContentRect(null); 262 263 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", r.width()); 264 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", r.height()); 265 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", 0); 266 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", 0); 267 268 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y); 269 oa.addUpdateListener(new AnimatorUpdateListener() { 270 public void onAnimationUpdate(ValueAnimator animation) { 271 requestLayout(); 272 } 273 }); 274 275 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f); 276 ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha); 277 278 AnimatorSet set = new AnimatorSet(); 279 set.playTogether(oa, oaContentAlpha); 280 set.setDuration(mExpandDuration); 281 set.addListener(new AnimatorListenerAdapter() { 282 @Override 283 public void onAnimationStart(Animator animation) { 284 mState = STATE_ANIMATING; 285 } 286 @Override 287 public void onAnimationEnd(Animator animation) { 288 mState = STATE_SMALL; 289 } 290 }); 291 set.start(); 292 } 293 294 public void animateClosed() { 295 if (!(getParent() instanceof CellLayoutChildren)) return; 296 297 CellLayoutChildren clc = (CellLayoutChildren) getParent(); 298 final CellLayout cellLayout = (CellLayout) clc.getParent(); 299 300 FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY); 301 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams(); 302 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 303 304 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", iconLp.width); 305 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", iconLp.height); 306 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x",iconLp.x); 307 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", iconLp.y); 308 309 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y); 310 oa.addUpdateListener(new AnimatorUpdateListener() { 311 public void onAnimationUpdate(ValueAnimator animation) { 312 requestLayout(); 313 } 314 }); 315 316 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); 317 ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha); 318 319 AnimatorSet set = new AnimatorSet(); 320 set.playTogether(oa, oaContentAlpha); 321 set.setDuration(mExpandDuration); 322 323 set.addListener(new AnimatorListenerAdapter() { 324 @Override 325 public void onAnimationEnd(Animator animation) { 326 cellLayout.removeViewWithoutMarkingCells(Folder.this); 327 mState = STATE_OPEN; 328 } 329 @Override 330 public void onAnimationStart(Animator animation) { 331 mState = STATE_ANIMATING; 332 } 333 }); 334 set.start(); 335 } 336 337 void notifyDataSetChanged() { 338 // recreate all the children if the data set changes under us. We may want to do this more 339 // intelligently (ie just removing the views that should no longer exist) 340 mContent.removeAllViewsInLayout(); 341 bind(mInfo); 342 } 343 344 public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, 345 DragView dragView, Object dragInfo) { 346 final ItemInfo item = (ItemInfo) dragInfo; 347 final int itemType = item.itemType; 348 return (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 349 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) 350 && item.container != mInfo.id; 351 } 352 353 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, 354 DragView dragView, Object dragInfo) { 355 ShortcutInfo item; 356 if (dragInfo instanceof ApplicationInfo) { 357 // Came from all apps -- make a copy 358 item = ((ApplicationInfo)dragInfo).makeShortcut(); 359 item.spanX = 1; 360 item.spanY = 1; 361 } else { 362 item = (ShortcutInfo)dragInfo; 363 } 364 findAndSetEmptyCells(item); 365 mInfo.add(item); 366 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); 367 } 368 369 protected boolean findAndSetEmptyCells(ShortcutInfo item) { 370 int[] emptyCell = new int[2]; 371 if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) { 372 item.cellX = emptyCell[0]; 373 item.cellY = emptyCell[1]; 374 LauncherModel.addOrMoveItemInDatabase( 375 mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); 376 return true; 377 } else { 378 return false; 379 } 380 } 381 382 protected void createAndAddShortcut(ShortcutInfo item) { 383 final TextView textView = 384 (TextView) mInflater.inflate(R.layout.application_boxed, this, false); 385 textView.setCompoundDrawablesWithIntrinsicBounds(null, 386 new FastBitmapDrawable(item.getIcon(mIconCache)), null, null); 387 textView.setText(item.title); 388 textView.setTag(item); 389 390 textView.setOnClickListener(this); 391 textView.setOnLongClickListener(this); 392 393 CellLayout.LayoutParams lp = 394 new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY); 395 boolean insert = false; 396 mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true); 397 } 398 399 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, 400 DragView dragView, Object dragInfo) { 401 } 402 403 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, 404 DragView dragView, Object dragInfo) { 405 } 406 407 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, 408 DragView dragView, Object dragInfo) { 409 } 410 411 public void onDropCompleted(View target, Object dragInfo, boolean success) { 412 if (success) { 413 mInfo.remove(mDragItem); 414 } 415 } 416 417 public boolean isDropEnabled() { 418 return true; 419 } 420 421 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, 422 DragView dragView, Object dragInfo) { 423 return null; 424 } 425 426 public void onAdd(ShortcutInfo item) { 427 if ((item.cellX == -1 && item.cellY == -1) || 428 mContent.isOccupied(item.cellX, item.cellY)) { 429 findAndSetEmptyCells(item); 430 } 431 createAndAddShortcut(item); 432 } 433 434 public int getItemCount() { 435 return mContent.getChildrenLayout().getChildCount(); 436 } 437 438 public View getItemAt(int index) { 439 return mContent.getChildrenLayout().getChildAt(index); 440 } 441 442 public void onRemove(ShortcutInfo item) { 443 View v = mContent.getChildAt(mDragItemPosition[0], mDragItemPosition[1]); 444 mContent.removeView(v); 445 } 446} 447