blob: b1275b8080dc2bb5af37bccc7590e667a3a0f0fb [file] [log] [blame]
Winson Chung321e9ee2010-08-09 13:37:56 -07001/*
2 * Copyright (C) 2010 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
Patrick Dubroy9f7aec82010-09-06 11:03:37 -070019import com.android.launcher.R;
Winson Chung321e9ee2010-08-09 13:37:56 -070020
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.res.TypedArray;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070024import android.graphics.Rect;
Winson Chung321e9ee2010-08-09 13:37:56 -070025import android.util.AttributeSet;
Patrick Dubroy9f7aec82010-09-06 11:03:37 -070026import android.util.Log;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070027import android.view.ActionMode;
Winson Chung321e9ee2010-08-09 13:37:56 -070028import android.view.LayoutInflater;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070029import android.view.Menu;
30import android.view.MenuItem;
Winson Chung321e9ee2010-08-09 13:37:56 -070031import android.view.View;
Winson Chung321e9ee2010-08-09 13:37:56 -070032import android.view.animation.AnimationUtils;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070033import android.widget.Checkable;
Winson Chung321e9ee2010-08-09 13:37:56 -070034import android.widget.TextView;
35
Patrick Dubroy9f7aec82010-09-06 11:03:37 -070036import java.util.ArrayList;
37import java.util.Collections;
Winson Chung321e9ee2010-08-09 13:37:56 -070038
39/**
40 * An implementation of PagedView that populates the pages of the workspace
41 * with all of the user's applications.
42 */
43public class AllAppsPagedView extends PagedView
Winson Chung5f2aa4e2010-08-20 14:49:25 -070044 implements AllAppsView, View.OnClickListener, View.OnLongClickListener, DragSource,
45 DropTarget, ActionMode.Callback {
Winson Chung321e9ee2010-08-09 13:37:56 -070046
47 private static final String TAG = "AllAppsPagedView";
48 private static final boolean DEBUG = false;
49
Patrick Dubroy9f7aec82010-09-06 11:03:37 -070050 private static final int MENU_DELETE_APP = 1;
51 private static final int MENU_APP_INFO = 2;
52
Winson Chung321e9ee2010-08-09 13:37:56 -070053 private Launcher mLauncher;
54 private DragController mDragController;
55
56 // preserve compatibility with 3D all apps:
57 // 0.0 -> hidden
58 // 1.0 -> shown and opaque
59 // intermediate values -> partially shown & partially opaque
60 private float mZoom;
61
62 // set of all applications
63 private ArrayList<ApplicationInfo> mApps;
64 private ArrayList<ApplicationInfo> mFilteredApps;
65
66 // the types of applications to filter
67 static final int ALL_APPS_FLAG = -1;
68 private int mAppFilter = ALL_APPS_FLAG;
69
70 private int mCellCountX;
71 private int mCellCountY;
72
73 private final LayoutInflater mInflater;
74
75 public AllAppsPagedView(Context context) {
76 this(context, null);
77 }
78
79 public AllAppsPagedView(Context context, AttributeSet attrs) {
80 this(context, attrs, 0);
81 }
82
83 public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
84 super(context, attrs, defStyle);
85 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
86 mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6);
87 mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
88 mInflater = LayoutInflater.from(context);
89 a.recycle();
90 setSoundEffectsEnabled(false);
91 }
92
93 @Override
94 public void setLauncher(Launcher launcher) {
95 mLauncher = launcher;
Patrick Dubroy2b9ff372010-09-07 17:49:27 -070096 mLauncher.setAllAppsPagedView(this);
Winson Chung321e9ee2010-08-09 13:37:56 -070097 }
98
99 @Override
100 public void setDragController(DragController dragger) {
101 mDragController = dragger;
102 }
103
104 public void setAppFilter(int filterType) {
105 mAppFilter = filterType;
Winson Chung80baf5a2010-08-09 16:03:15 -0700106 if (mApps != null) {
107 mFilteredApps = rebuildFilteredApps(mApps);
Winson Chung86f77532010-08-24 11:08:22 -0700108 setCurrentPage(0);
Winson Chung80baf5a2010-08-09 16:03:15 -0700109 invalidatePageData();
110 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700111 }
112
113 @Override
114 public void zoom(float zoom, boolean animate) {
115 mZoom = zoom;
116 cancelLongPress();
117
118 if (isVisible()) {
119 getParent().bringChildToFront(this);
120 setVisibility(View.VISIBLE);
121 if (animate) {
122 startAnimation(AnimationUtils.loadAnimation(getContext(),
123 R.anim.all_apps_2d_fade_in));
124 } else {
125 onAnimationEnd();
126 }
127 } else {
128 if (animate) {
129 startAnimation(AnimationUtils.loadAnimation(getContext(),
130 R.anim.all_apps_2d_fade_out));
131 } else {
132 onAnimationEnd();
133 }
134 }
135 }
136
137 protected void onAnimationEnd() {
138 if (!isVisible()) {
139 setVisibility(View.GONE);
140 mZoom = 0.0f;
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700141
142 endChoiceMode();
Winson Chung321e9ee2010-08-09 13:37:56 -0700143 } else {
144 mZoom = 1.0f;
145 }
146
147 if (mLauncher != null)
148 mLauncher.zoomed(mZoom);
149 }
150
151 private int getChildIndexForGrandChild(View v) {
152 final int childCount = getChildCount();
153 for (int i = 0; i < childCount; ++i) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700154 final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
Winson Chung321e9ee2010-08-09 13:37:56 -0700155 if (layout.indexOfChild(v) > -1) {
156 return i;
157 }
158 }
159 return -1;
160 }
161
162 @Override
163 public void onClick(View v) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700164 // if we are already in a choice mode, then just change the selection
165 if (v instanceof Checkable) {
166 if (!isChoiceMode(CHOICE_MODE_NONE)) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700167 Checkable c = (Checkable) v;
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700168 if (isChoiceMode(CHOICE_MODE_SINGLE)) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700169 // Uncheck all the other grandchildren, and toggle the clicked one
170 boolean wasChecked = c.isChecked();
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700171 resetCheckedGrandchildren();
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700172 c.setChecked(!wasChecked);
173 } else {
174 c.toggle();
175 }
176 if (getCheckedGrandchildren().size() == 0) {
177 endChoiceMode();
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700178 }
179
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700180 return;
181 }
182 }
183
184 // otherwise continue and launch the application
Winson Chung321e9ee2010-08-09 13:37:56 -0700185 int childIndex = getChildIndexForGrandChild(v);
Winson Chung86f77532010-08-24 11:08:22 -0700186 if (childIndex == getCurrentPage()) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700187 final ApplicationInfo app = (ApplicationInfo) v.getTag();
188
Winson Chung80baf5a2010-08-09 16:03:15 -0700189 // animate some feedback to the click
190 animateClickFeedback(v, new Runnable() {
Winson Chung321e9ee2010-08-09 13:37:56 -0700191 @Override
Winson Chung80baf5a2010-08-09 16:03:15 -0700192 public void run() {
Winson Chung321e9ee2010-08-09 13:37:56 -0700193 mLauncher.startActivitySafely(app.intent, app);
194 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700195 });
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700196
197 endChoiceMode();
Winson Chung321e9ee2010-08-09 13:37:56 -0700198 }
199 }
200
201 @Override
202 public boolean onLongClick(View v) {
203 if (!v.isInTouchMode()) {
204 return false;
205 }
206
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700207 // start the choice mode, and select the item that was long-pressed
208 if (isChoiceMode(CHOICE_MODE_NONE)) {
209 startChoiceMode(CHOICE_MODE_SINGLE, this);
210 }
211
212 if (v instanceof Checkable) {
213 // In preparation for drag, we always reset the checked grand children regardless of
214 // what choice mode we are in
215 resetCheckedGrandchildren();
216
217 // Toggle the selection on the dragged app
218 Checkable c = (Checkable) v;
219 c.toggle();
220 }
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700221
Winson Chung321e9ee2010-08-09 13:37:56 -0700222 ApplicationInfo app = (ApplicationInfo) v.getTag();
223 app = new ApplicationInfo(app);
224
225 mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
Winson Chung321e9ee2010-08-09 13:37:56 -0700226 return true;
227 }
228
229 @Override
230 public void onDropCompleted(View target, boolean success) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700231 // close the choice action mode if we have a proper drop
232 if (target != this) {
233 endChoiceMode();
234 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700235 }
236
237 @Override
238 public boolean isVisible() {
239 return mZoom > 0.001f;
240 }
241
242 @Override
243 public boolean isAnimating() {
244 return (getAnimation() != null);
245 }
246
247 private ArrayList<ApplicationInfo> rebuildFilteredApps(ArrayList<ApplicationInfo> apps) {
248 ArrayList<ApplicationInfo> filteredApps = new ArrayList<ApplicationInfo>();
249 if (mAppFilter == ALL_APPS_FLAG) {
250 return apps;
251 } else {
252 final int length = apps.size();
253 for (int i = 0; i < length; ++i) {
254 ApplicationInfo info = apps.get(i);
255 if ((info.flags & mAppFilter) > 0) {
256 filteredApps.add(info);
257 }
258 }
259 }
260 return filteredApps;
261 }
262
263 @Override
264 public void setApps(ArrayList<ApplicationInfo> list) {
265 mApps = list;
Winson Chung80baf5a2010-08-09 16:03:15 -0700266 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
Winson Chung321e9ee2010-08-09 13:37:56 -0700267 mFilteredApps = rebuildFilteredApps(mApps);
Winson Chung241c3b42010-08-25 16:53:03 -0700268 mPageViewIconCache.clear();
Winson Chung321e9ee2010-08-09 13:37:56 -0700269 invalidatePageData();
270 }
271
Winson Chung80baf5a2010-08-09 16:03:15 -0700272 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
273 // we add it in place, in alphabetical order
274 final int count = list.size();
275 for (int i = 0; i < count; ++i) {
276 final ApplicationInfo info = list.get(i);
277 final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
278 if (index < 0) {
279 mApps.add(-(index + 1), info);
280 }
281 }
282 mFilteredApps = rebuildFilteredApps(mApps);
283 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700284 @Override
285 public void addApps(ArrayList<ApplicationInfo> list) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700286 addAppsWithoutInvalidate(list);
Winson Chung321e9ee2010-08-09 13:37:56 -0700287 invalidatePageData();
288 }
289
Winson Chung80baf5a2010-08-09 16:03:15 -0700290 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700291 // loop through all the apps and remove apps that have the same component
292 final int length = list.size();
293 for (int i = 0; i < length; ++i) {
Winson Chung241c3b42010-08-25 16:53:03 -0700294 final ApplicationInfo info = list.get(i);
295 int removeIndex = findAppByComponent(mApps, info);
Winson Chung321e9ee2010-08-09 13:37:56 -0700296 if (removeIndex > -1) {
297 mApps.remove(removeIndex);
Winson Chung241c3b42010-08-25 16:53:03 -0700298 mPageViewIconCache.removeOutline(info);
Winson Chung321e9ee2010-08-09 13:37:56 -0700299 }
300 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700301 mFilteredApps = rebuildFilteredApps(mApps);
302 }
303 @Override
304 public void removeApps(ArrayList<ApplicationInfo> list) {
305 removeAppsWithoutInvalidate(list);
Winson Chung321e9ee2010-08-09 13:37:56 -0700306 invalidatePageData();
307 }
308
309 @Override
310 public void updateApps(ArrayList<ApplicationInfo> list) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700311 removeAppsWithoutInvalidate(list);
312 addAppsWithoutInvalidate(list);
313 invalidatePageData();
Winson Chung321e9ee2010-08-09 13:37:56 -0700314 }
315
316 private int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
317 ComponentName removeComponent = item.intent.getComponent();
318 final int length = list.size();
319 for (int i = 0; i < length; ++i) {
320 ApplicationInfo info = list.get(i);
321 if (info.intent.getComponent().equals(removeComponent)) {
322 return i;
323 }
324 }
325 return -1;
326 }
327
328 @Override
329 public void dumpState() {
330 ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
331 }
332
333 @Override
334 public void surrender() {
335 // do nothing?
336 }
337
338 @Override
339 public void syncPages() {
340 // ensure that we have the right number of pages
341 int numPages = (int) Math.ceil((float) mFilteredApps.size() / (mCellCountX * mCellCountY));
342 int curNumPages = getChildCount();
343 // remove any extra pages after the "last" page
344 int extraPageDiff = curNumPages - numPages;
345 for (int i = 0; i < extraPageDiff; ++i) {
346 removeViewAt(numPages);
347 }
348 // add any necessary pages
349 for (int i = curNumPages; i < numPages; ++i) {
350 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
351 layout.setCellCount(mCellCountX, mCellCountY);
Winson Chung321e9ee2010-08-09 13:37:56 -0700352 addView(layout);
353 }
354
355 // bound the current page
Winson Chung86f77532010-08-24 11:08:22 -0700356 setCurrentPage(Math.max(0, Math.min(numPages - 1, getCurrentPage())));
Winson Chung321e9ee2010-08-09 13:37:56 -0700357 }
358
359 @Override
360 public void syncPageItems(int page) {
361 // ensure that we have the right number of items on the pages
Winson Chung80baf5a2010-08-09 16:03:15 -0700362 final int cellsPerPage = mCellCountX * mCellCountY;
363 final int startIndex = page * cellsPerPage;
364 final int endIndex = Math.min(startIndex + cellsPerPage, mFilteredApps.size());
Winson Chung321e9ee2010-08-09 13:37:56 -0700365 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
Winson Chung80baf5a2010-08-09 16:03:15 -0700366
367 final int curNumPageItems = layout.getChildCount();
368 final int numPageItems = endIndex - startIndex;
369
370 // remove any extra items
371 int extraPageItemsDiff = curNumPageItems - numPageItems;
372 for (int i = 0; i < extraPageItemsDiff; ++i) {
373 layout.removeViewAt(numPageItems);
374 }
375 // add any necessary items
376 for (int i = curNumPageItems; i < numPageItems; ++i) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700377 TextView text = (TextView) mInflater.inflate(R.layout.all_apps_paged_view_application, layout, false);
Winson Chung80baf5a2010-08-09 16:03:15 -0700378 text.setOnClickListener(this);
379 text.setOnLongClickListener(this);
380
381 layout.addViewToCellLayout(text, -1, i,
382 new PagedViewCellLayout.LayoutParams(0, 0, 1, 1));
383 }
384
385 // actually reapply to the existing text views
386 for (int i = startIndex; i < endIndex; ++i) {
Winson Chung241c3b42010-08-25 16:53:03 -0700387 final int index = i - startIndex;
388 final ApplicationInfo info = mFilteredApps.get(i);
389 PagedViewIcon icon = (PagedViewIcon) layout.getChildAt(index);
390 icon.applyFromApplicationInfo(info, mPageViewIconCache);
Winson Chung321e9ee2010-08-09 13:37:56 -0700391
Winson Chung80baf5a2010-08-09 16:03:15 -0700392 PagedViewCellLayout.LayoutParams params =
Winson Chung241c3b42010-08-25 16:53:03 -0700393 (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
Winson Chung80baf5a2010-08-09 16:03:15 -0700394 params.cellX = index % mCellCountX;
395 params.cellY = index / mCellCountX;
Winson Chung321e9ee2010-08-09 13:37:56 -0700396 }
397 }
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700398
399 @Override
400 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700401 mode.setTitle(R.string.cab_selection_text);
402 menu.add(0, MENU_APP_INFO, 0, R.string.cab_menu_app_info)
403 .setIcon(R.drawable.info_button);
404 menu.add(0, MENU_DELETE_APP, 0, R.string.cab_menu_delete_app)
405 .setIcon(R.drawable.delete_zone_selector);
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700406 return true;
407 }
408
409 @Override
410 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700411 mDragController.addDropTarget(this);
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700412 return true;
413 }
414
415 @Override
416 public void onDestroyActionMode(ActionMode mode) {
417 mDragController.removeDropTarget(this);
418 endChoiceMode();
419 }
420
421 @Override
422 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700423 final int id = item.getItemId();
424
425 // Assumes that we are in CHOICE_MODE_SINGLE
426 final View chosenView = (View) getSingleCheckedGrandchild();
427 final ApplicationInfo appInfo = (ApplicationInfo) chosenView.getTag();
428
429 if (id == MENU_APP_INFO) {
430 mLauncher.startApplicationDetailsActivity(appInfo.componentName);
431 } else if (id == MENU_DELETE_APP) {
Patrick Dubroy5539af72010-09-07 15:22:01 -0700432 mLauncher.startApplicationUninstallActivity(appInfo);
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700433 }
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700434 return false;
435 }
436
437 /*
438 * We don't actually use AllAppsPagedView as a drop target... it's only used to intercept a drop
439 * to the workspace.
440 */
441 @Override
442 public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
443 DragView dragView, Object dragInfo) {
444 return false;
445 }
446 @Override
447 public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
448 DragView dragView, Object dragInfo, Rect recycle) {
449 return null;
450 }
451 @Override
452 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset,
453 int yOffset, DragView dragView, Object dragInfo) {
454 return null;
455 }
456 @Override
457 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
458 DragView dragView, Object dragInfo) {}
459 @Override
460 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
461 DragView dragView, Object dragInfo) {}
462 @Override
463 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
464 DragView dragView, Object dragInfo) {}
465 @Override
466 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
467 DragView dragView, Object dragInfo) {}
Winson Chung321e9ee2010-08-09 13:37:56 -0700468}