blob: 5a4358d2fd2dd43f6e1c7b729a4e77279dcb274a [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Adam Cohendf2cc412011-04-27 16:56:57 -070019import 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;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.Context;
Romain Guyfb5411e2010-02-24 10:04:17 -080029import android.graphics.Rect;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.util.AttributeSet;
Adam Cohendf2cc412011-04-27 16:56:57 -070031import android.view.LayoutInflater;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.view.View;
33import android.view.View.OnClickListener;
34import android.widget.AdapterView;
35import android.widget.Button;
36import android.widget.LinearLayout;
Adam Cohendf2cc412011-04-27 16:56:57 -070037import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.widget.AdapterView.OnItemClickListener;
39import android.widget.AdapterView.OnItemLongClickListener;
40
Romain Guyedcce092010-03-04 13:03:17 -080041import com.android.launcher.R;
Adam Cohena9cf38f2011-05-02 15:36:58 -070042import com.android.launcher2.FolderInfo.FolderListener;
Romain Guyedcce092010-03-04 13:03:17 -080043
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044/**
45 * Represents a set of icons chosen by the user or generated by the system.
46 */
47public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
Adam Cohena9cf38f2011-05-02 15:36:58 -070048 OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049
Joe Onorato00acb122009-08-04 16:04:30 -040050 protected DragController mDragController;
Adam Cohendf2cc412011-04-27 16:56:57 -070051
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052 protected Launcher mLauncher;
53
54 protected Button mCloseButton;
Adam Cohendf2cc412011-04-27 16:56:57 -070055
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056 protected FolderInfo mInfo;
57
58 /**
59 * Which item is being dragged
60 */
Joe Onorato0589f0f2010-02-08 13:44:00 -080061 protected ShortcutInfo mDragItem;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062
Adam Cohendf2cc412011-04-27 16:56:57 -070063 private static final String TAG = "Launcher.Folder";
64
65 static final int STATE_NONE = -1;
66 static final int STATE_SMALL = 0;
67 static final int STATE_ANIMATING = 1;
68 static final int STATE_OPEN = 2;
69
70 private int mExpandDuration;
71 protected CellLayout mContent;
72 private final LayoutInflater mInflater;
73 private final IconCache mIconCache;
74 private int mState = STATE_NONE;
Adam Cohena9cf38f2011-05-02 15:36:58 -070075 private int[] mDragItemPosition = new int[2];
Adam Cohendf2cc412011-04-27 16:56:57 -070076
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077 /**
78 * Used to inflate the Workspace from XML.
79 *
80 * @param context The application's context.
81 * @param attrs The attribtues set containing the Workspace's customization values.
82 */
83 public Folder(Context context, AttributeSet attrs) {
84 super(context, attrs);
85 setAlwaysDrawnWithCacheEnabled(false);
Adam Cohendf2cc412011-04-27 16:56:57 -070086 mInflater = LayoutInflater.from(context);
87 mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
88 mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089 }
90
91 @Override
92 protected void onFinishInflate() {
93 super.onFinishInflate();
94
Joe Onorato92442302009-09-21 15:37:59 -040095 mCloseButton = (Button) findViewById(R.id.folder_close);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080096 mCloseButton.setOnClickListener(this);
97 mCloseButton.setOnLongClickListener(this);
Adam Cohendf2cc412011-04-27 16:56:57 -070098 mContent = (CellLayout) findViewById(R.id.folder_content);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 }
100
101 public void onItemClick(AdapterView parent, View v, int position, long id) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800102 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
Romain Guyfb5411e2010-02-24 10:04:17 -0800103 int[] pos = new int[2];
104 v.getLocationOnScreen(pos);
105 app.intent.setSourceBounds(new Rect(pos[0], pos[1],
106 pos[0] + v.getWidth(), pos[1] + v.getHeight()));
Joe Onoratof984e852010-03-25 09:47:45 -0700107 mLauncher.startActivitySafely(app.intent, app);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 }
109
110 public void onClick(View v) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700111 Object tag = v.getTag();
112 if (tag instanceof ShortcutInfo) {
113 // refactor this code from Folder
114 ShortcutInfo item = (ShortcutInfo) tag;
115 int[] pos = new int[2];
116 v.getLocationOnScreen(pos);
117 item.intent.setSourceBounds(new Rect(pos[0], pos[1],
118 pos[0] + v.getWidth(), pos[1] + v.getHeight()));
119 mLauncher.startActivitySafely(item.intent, item);
120 } else {
121 mLauncher.closeFolder(this);
122 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123 }
124
125 public boolean onLongClick(View v) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700126 Object tag = v.getTag();
127 if (tag instanceof ShortcutInfo) {
128 // refactor this code from Folder
129 ShortcutInfo item = (ShortcutInfo) tag;
130 if (!v.isInTouchMode()) {
131 return false;
132 }
133
134 mLauncher.getWorkspace().onDragStartedWithItem(v);
135 mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700136 mDragItemPosition[0] = item.cellX;
137 mDragItemPosition[1] = item.cellY;
Adam Cohendf2cc412011-04-27 16:56:57 -0700138 mLauncher.closeFolder(this);
139 mDragItem = item;
140 } else {
141 mLauncher.closeFolder(this);
142 mLauncher.showRenameDialog(mInfo);
143 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800144 return true;
145 }
146
147 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
148 if (!view.isInTouchMode()) {
149 return false;
150 }
151
Joe Onorato0589f0f2010-02-08 13:44:00 -0800152 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153
Joe Onorato00acb122009-08-04 16:04:30 -0400154 mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800155 mLauncher.closeFolder(this);
156 mDragItem = app;
157
158 return true;
159 }
160
Joe Onorato00acb122009-08-04 16:04:30 -0400161 public void setDragController(DragController dragController) {
162 mDragController = dragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800163 }
164
Patrick Dubroya669d792010-11-23 14:40:33 -0800165 public void onDragViewVisible() {
166 }
167
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800168 void setLauncher(Launcher launcher) {
169 mLauncher = launcher;
170 }
171
172 /**
173 * @return the FolderInfo object associated with this folder
174 */
175 FolderInfo getInfo() {
176 return mInfo;
177 }
178
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800179 void onOpen() {
Adam Cohendf2cc412011-04-27 16:56:57 -0700180 // When the folder opens, we need to refresh the GridView's selection by
181 // forcing a layout
182 // TODO: find out if this is still necessary
183 mContent.requestLayout();
184 requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800185 }
186
187 void onClose() {
188 final Workspace workspace = mLauncher.getWorkspace();
Michael Jurka0142d492010-08-25 17:46:15 -0700189 workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800190 }
191
192 void bind(FolderInfo info) {
193 mInfo = info;
194 mCloseButton.setText(info.title);
Adam Cohendf2cc412011-04-27 16:56:57 -0700195 ArrayList<ShortcutInfo> children = info.contents;
196 for (int i = 0; i < children.size(); i++) {
197 ShortcutInfo child = (ShortcutInfo) children.get(i);
198 if ((child.cellX == -1 && child.cellY == -1) ||
199 mContent.isOccupied(child.cellX, child.cellY)) {
200 findAndSetEmptyCells(child);
201 }
202 createAndAddShortcut((ShortcutInfo) children.get(i));
203 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700204 mInfo.addListener(this);
Adam Cohendf2cc412011-04-27 16:56:57 -0700205 }
206
207 /**
208 * Creates a new UserFolder, inflated from R.layout.user_folder.
209 *
210 * @param context The application's context.
211 *
212 * @return A new UserFolder.
213 */
214 static Folder fromXml(Context context) {
215 return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
216 }
217
218 /**
219 * This method is intended to make the UserFolder to be visually identical in size and position
220 * to its associated FolderIcon. This allows for a seamless transition into the expanded state.
221 */
222 private void positionAndSizeAsIcon() {
223 if (!(getParent() instanceof CellLayoutChildren)) return;
224
225 CellLayoutChildren clc = (CellLayoutChildren) getParent();
226 CellLayout cellLayout = (CellLayout) clc.getParent();
227
228 FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY);
229 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams();
230 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
231
232 lp.width = iconLp.width;
233 lp.height = iconLp.height;
234 lp.x = iconLp.x;
235 lp.y = iconLp.y;
236
237 mContent.setAlpha(0f);
238 mState = STATE_SMALL;
239 }
240
241 public void animateOpen() {
242 if (mState != STATE_SMALL) {
243 positionAndSizeAsIcon();
244 }
245 if (!(getParent() instanceof CellLayoutChildren)) return;
246
247 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
248
249 CellLayoutChildren clc = (CellLayoutChildren) getParent();
250 CellLayout cellLayout = (CellLayout) clc.getParent();
251 Rect r = cellLayout.getContentRect(null);
252
253 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", r.width());
254 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", r.height());
255 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", 0);
256 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", 0);
257
258 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
259 oa.addUpdateListener(new AnimatorUpdateListener() {
260 public void onAnimationUpdate(ValueAnimator animation) {
261 requestLayout();
262 }
263 });
264
265 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
266 ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha);
267
268 AnimatorSet set = new AnimatorSet();
269 set.playTogether(oa, oaContentAlpha);
270 set.setDuration(mExpandDuration);
271 set.addListener(new AnimatorListenerAdapter() {
272 @Override
273 public void onAnimationStart(Animator animation) {
274 mState = STATE_ANIMATING;
275 }
276 @Override
277 public void onAnimationEnd(Animator animation) {
278 mState = STATE_SMALL;
279 }
280 });
281 set.start();
282 }
283
284 public void animateClosed() {
285 if (!(getParent() instanceof CellLayoutChildren)) return;
286
287 CellLayoutChildren clc = (CellLayoutChildren) getParent();
288 final CellLayout cellLayout = (CellLayout) clc.getParent();
289
290 FolderIcon fi = (FolderIcon) cellLayout.getChildAt(mInfo.cellX, mInfo.cellY);
291 CellLayout.LayoutParams iconLp = (CellLayout.LayoutParams) fi.getLayoutParams();
292 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
293
294 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", iconLp.width);
295 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", iconLp.height);
296 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x",iconLp.x);
297 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", iconLp.y);
298
299 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
300 oa.addUpdateListener(new AnimatorUpdateListener() {
301 public void onAnimationUpdate(ValueAnimator animation) {
302 requestLayout();
303 }
304 });
305
306 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
307 ObjectAnimator oaContentAlpha = ObjectAnimator.ofPropertyValuesHolder(mContent, alpha);
308
309 AnimatorSet set = new AnimatorSet();
310 set.playTogether(oa, oaContentAlpha);
311 set.setDuration(mExpandDuration);
312
313 set.addListener(new AnimatorListenerAdapter() {
314 @Override
315 public void onAnimationEnd(Animator animation) {
316 cellLayout.removeViewWithoutMarkingCells(Folder.this);
317 mState = STATE_OPEN;
318 }
319 @Override
320 public void onAnimationStart(Animator animation) {
321 mState = STATE_ANIMATING;
322 }
323 });
324 set.start();
325 }
326
327 void notifyDataSetChanged() {
328 // recreate all the children if the data set changes under us. We may want to do this more
329 // intelligently (ie just removing the views that should no longer exist)
330 mContent.removeAllViewsInLayout();
331 bind(mInfo);
332 }
333
334 public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
335 DragView dragView, Object dragInfo) {
336 final ItemInfo item = (ItemInfo) dragInfo;
337 final int itemType = item.itemType;
338 return (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
339 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT)
340 && item.container != mInfo.id;
341 }
342
343 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
344 DragView dragView, Object dragInfo) {
345 ShortcutInfo item;
346 if (dragInfo instanceof ApplicationInfo) {
347 // Came from all apps -- make a copy
348 item = ((ApplicationInfo)dragInfo).makeShortcut();
349 item.spanX = 1;
350 item.spanY = 1;
351 } else {
352 item = (ShortcutInfo)dragInfo;
353 }
354 findAndSetEmptyCells(item);
355 mInfo.add(item);
356 createAndAddShortcut(item);
357 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
358 }
359
360 protected boolean findAndSetEmptyCells(ShortcutInfo item) {
361 int[] emptyCell = new int[2];
362 if (mContent.findCellForSpan(emptyCell, item.spanX, item.spanY)) {
363 item.cellX = emptyCell[0];
364 item.cellY = emptyCell[1];
365 LauncherModel.addOrMoveItemInDatabase(
366 mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
367 return true;
368 } else {
369 return false;
370 }
371 }
372
373 protected void createAndAddShortcut(ShortcutInfo item) {
374 final TextView textView =
375 (TextView) mInflater.inflate(R.layout.application_boxed, this, false);
376 textView.setCompoundDrawablesWithIntrinsicBounds(null,
377 new FastBitmapDrawable(item.getIcon(mIconCache)), null, null);
378 textView.setText(item.title);
379 textView.setTag(item);
380
381 textView.setOnClickListener(this);
382 textView.setOnLongClickListener(this);
383
384 CellLayout.LayoutParams lp =
385 new CellLayout.LayoutParams(item.cellX, item.cellY, item.spanX, item.spanY);
386 boolean insert = false;
387 mContent.addViewToCellLayout(textView, insert ? 0 : -1, (int)item.id, lp, true);
388 }
389
390 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
391 DragView dragView, Object dragInfo) {
392 }
393
394 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
395 DragView dragView, Object dragInfo) {
396 }
397
398 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
399 DragView dragView, Object dragInfo) {
400 }
401
402 public void onDropCompleted(View target, Object dragInfo, boolean success) {
403 if (success) {
404 mInfo.remove(mDragItem);
405 }
406 }
407
408 public boolean isDropEnabled() {
409 return true;
410 }
411
412 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
413 DragView dragView, Object dragInfo) {
414 return null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800415 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700416
417 public void onAdd(ShortcutInfo item) {
418 if ((item.cellX == -1 && item.cellY == -1) ||
419 mContent.isOccupied(item.cellX, item.cellY)) {
420 findAndSetEmptyCells(item);
421 }
422 createAndAddShortcut(item);
423 }
424
425 public int getItemCount() {
426 return mContent.getChildrenLayout().getChildCount();
427 }
428
429 public View getItemAt(int index) {
430 return mContent.getChildrenLayout().getChildAt(index);
431 }
432
433 public void onRemove(ShortcutInfo item) {
434 View v = mContent.getChildAt(mDragItemPosition[0], mDragItemPosition[1]);
435 mContent.removeView(v);
436 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800437}