blob: 3a8a68d56a4601cd7e6c41ba865c6fa2874bfaa8 [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;
Adam Cohen0c872ba2011-05-05 10:34:16 -070032import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.view.View;
34import android.view.View.OnClickListener;
35import android.widget.AdapterView;
36import android.widget.Button;
37import android.widget.LinearLayout;
Adam Cohendf2cc412011-04-27 16:56:57 -070038import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.widget.AdapterView.OnItemClickListener;
40import android.widget.AdapterView.OnItemLongClickListener;
41
Romain Guyedcce092010-03-04 13:03:17 -080042import com.android.launcher.R;
Adam Cohena9cf38f2011-05-02 15:36:58 -070043import com.android.launcher2.FolderInfo.FolderListener;
Adam Cohen0c872ba2011-05-05 10:34:16 -070044import com.android.launcher2.Workspace.ShrinkState;
Romain Guyedcce092010-03-04 13:03:17 -080045
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046/**
47 * Represents a set of icons chosen by the user or generated by the system.
48 */
49public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
Adam Cohena9cf38f2011-05-02 15:36:58 -070050 OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051
Joe Onorato00acb122009-08-04 16:04:30 -040052 protected DragController mDragController;
Adam Cohendf2cc412011-04-27 16:56:57 -070053
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054 protected Launcher mLauncher;
55
56 protected Button mCloseButton;
Adam Cohendf2cc412011-04-27 16:56:57 -070057
The Android Open Source Project31dd5032009-03-03 19:32:27 -080058 protected FolderInfo mInfo;
59
60 /**
61 * Which item is being dragged
62 */
Joe Onorato0589f0f2010-02-08 13:44:00 -080063 protected ShortcutInfo mDragItem;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
Adam Cohendf2cc412011-04-27 16:56:57 -070065 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;
Adam Cohena9cf38f2011-05-02 15:36:58 -070077 private int[] mDragItemPosition = new int[2];
Adam Cohendf2cc412011-04-27 16:56:57 -070078
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079 /**
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);
Adam Cohendf2cc412011-04-27 16:56:57 -070088 mInflater = LayoutInflater.from(context);
89 mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
90 mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080091 }
92
93 @Override
94 protected void onFinishInflate() {
95 super.onFinishInflate();
96
Joe Onorato92442302009-09-21 15:37:59 -040097 mCloseButton = (Button) findViewById(R.id.folder_close);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 mCloseButton.setOnClickListener(this);
99 mCloseButton.setOnLongClickListener(this);
Adam Cohendf2cc412011-04-27 16:56:57 -0700100 mContent = (CellLayout) findViewById(R.id.folder_content);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800101 }
102
103 public void onItemClick(AdapterView parent, View v, int position, long id) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800104 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
Romain Guyfb5411e2010-02-24 10:04:17 -0800105 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()));
Joe Onoratof984e852010-03-25 09:47:45 -0700109 mLauncher.startActivitySafely(app.intent, app);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800110 }
111
112 public void onClick(View v) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700113 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 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800125 }
126
127 public boolean onLongClick(View v) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700128 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);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700138 mDragItemPosition[0] = item.cellX;
139 mDragItemPosition[1] = item.cellY;
Adam Cohendf2cc412011-04-27 16:56:57 -0700140 mLauncher.closeFolder(this);
141 mDragItem = item;
142 } else {
143 mLauncher.closeFolder(this);
144 mLauncher.showRenameDialog(mInfo);
145 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800146 return true;
147 }
148
Adam Cohen0c872ba2011-05-05 10:34:16 -0700149 /**
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
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800157 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
158 if (!view.isInTouchMode()) {
159 return false;
160 }
161
Joe Onorato0589f0f2010-02-08 13:44:00 -0800162 ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800163
Joe Onorato00acb122009-08-04 16:04:30 -0400164 mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800165 mLauncher.closeFolder(this);
166 mDragItem = app;
167
168 return true;
169 }
170
Joe Onorato00acb122009-08-04 16:04:30 -0400171 public void setDragController(DragController dragController) {
172 mDragController = dragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800173 }
174
Patrick Dubroya669d792010-11-23 14:40:33 -0800175 public void onDragViewVisible() {
176 }
177
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800178 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
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800189 void onOpen() {
Adam Cohendf2cc412011-04-27 16:56:57 -0700190 // 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();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800195 }
196
197 void onClose() {
198 final Workspace workspace = mLauncher.getWorkspace();
Michael Jurka0142d492010-08-25 17:46:15 -0700199 workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800200 }
201
202 void bind(FolderInfo info) {
203 mInfo = info;
204 mCloseButton.setText(info.title);
Adam Cohendf2cc412011-04-27 16:56:57 -0700205 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 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700214 mInfo.addListener(this);
Adam Cohendf2cc412011-04-27 16:56:57 -0700215 }
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);
Adam Cohendf2cc412011-04-27 16:56:57 -0700366 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;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800424 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700425
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 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800446}