blob: 28f44e4e786594b0b2c086cc10f74d3b6581ed4d [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
Michael Jurkaaf91de02010-11-23 16:23:58 -080019import com.android.launcher.R;
Winson Chung321e9ee2010-08-09 13:37:56 -070020
21import android.content.ComponentName;
22import android.content.Context;
Michael Jurka72b079e2010-12-10 01:03:53 -080023import android.content.res.Resources;
Winson Chung321e9ee2010-08-09 13:37:56 -070024import android.content.res.TypedArray;
Michael Jurkad3ef3062010-11-23 16:23:58 -080025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.drawable.Drawable;
Winson Chung321e9ee2010-08-09 13:37:56 -070028import android.util.AttributeSet;
29import android.view.LayoutInflater;
30import android.view.View;
Winson Chung321e9ee2010-08-09 13:37:56 -070031import android.view.animation.AnimationUtils;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070032import android.widget.Checkable;
Winson Chung321e9ee2010-08-09 13:37:56 -070033import android.widget.TextView;
34
Michael Jurkaaf91de02010-11-23 16:23:58 -080035import java.util.ArrayList;
36import java.util.Collections;
37import java.util.HashSet;
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 */
Michael Jurka72b079e2010-12-10 01:03:53 -080043public class AllAppsPagedView extends PagedViewWithDraggableItems implements AllAppsView,
44 View.OnClickListener, DragSource, DropTarget {
Winson Chung321e9ee2010-08-09 13:37:56 -070045
46 private static final String TAG = "AllAppsPagedView";
Winson Chung321e9ee2010-08-09 13:37:56 -070047
48 private Launcher mLauncher;
49 private DragController mDragController;
50
51 // preserve compatibility with 3D all apps:
52 // 0.0 -> hidden
53 // 1.0 -> shown and opaque
54 // intermediate values -> partially shown & partially opaque
55 private float mZoom;
56
57 // set of all applications
58 private ArrayList<ApplicationInfo> mApps;
59 private ArrayList<ApplicationInfo> mFilteredApps;
60
61 // the types of applications to filter
62 static final int ALL_APPS_FLAG = -1;
63 private int mAppFilter = ALL_APPS_FLAG;
64
Winson Chung321e9ee2010-08-09 13:37:56 -070065 private final LayoutInflater mInflater;
Michael Jurkaabded662011-03-04 12:06:57 -080066 private boolean mAllowHardwareLayerCreation;
Winson Chung321e9ee2010-08-09 13:37:56 -070067
Michael Jurka12ac0d62011-02-23 11:48:32 -080068 private int mPageContentWidth;
Patrick Dubroydea9e932010-09-22 15:04:29 -070069
Patrick Dubroye61c6a32011-05-19 16:48:48 -070070 private int mLastMeasureWidth = -1;
71 private int mLastMeasureHeight = -1;
Michael Jurka87b14902011-05-25 22:13:09 -070072 private boolean mWaitingToInitPages = true;
Patrick Dubroye61c6a32011-05-19 16:48:48 -070073
Patrick Dubroy1a009332011-05-23 16:15:09 -070074 private int mMaxCellCountY;
75
Winson Chung321e9ee2010-08-09 13:37:56 -070076 public AllAppsPagedView(Context context) {
77 this(context, null);
78 }
79
80 public AllAppsPagedView(Context context, AttributeSet attrs) {
81 this(context, attrs, 0);
82 }
83
84 public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
85 super(context, attrs, defStyle);
86 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
Winson Chung321e9ee2010-08-09 13:37:56 -070087 mInflater = LayoutInflater.from(context);
Winson Chung8b534782011-02-23 13:43:59 -080088 mApps = new ArrayList<ApplicationInfo>();
89 mFilteredApps = new ArrayList<ApplicationInfo>();
Winson Chung321e9ee2010-08-09 13:37:56 -070090 a.recycle();
91 setSoundEffectsEnabled(false);
Michael Jurka72b079e2010-12-10 01:03:53 -080092
Patrick Dubroy1a009332011-05-23 16:15:09 -070093 final Resources r = context.getResources();
Michael Jurka72b079e2010-12-10 01:03:53 -080094 setDragSlopeThreshold(
95 r.getInteger(R.integer.config_allAppsDrawerDragSlopeThreshold) / 100.0f);
Michael Jurka12ac0d62011-02-23 11:48:32 -080096
97 // Create a dummy page and set it up to find out the content width (used by our parent)
98 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
99 setupPage(layout);
100 mPageContentWidth = layout.getContentWidth();
Patrick Dubroy1a009332011-05-23 16:15:09 -0700101 mMaxCellCountY = r.getInteger(R.integer.all_apps_view_maxCellCountY);
Winson Chung321e9ee2010-08-09 13:37:56 -0700102 }
103
104 @Override
Winson Chung7da10252010-10-28 16:07:04 -0700105 protected void init() {
106 super.init();
107 mCenterPagesVertically = false;
108 }
109
Michael Jurkaea2daff2011-05-17 18:21:03 -0700110 @Override
111 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Patrick Dubroye61c6a32011-05-19 16:48:48 -0700112 final int width = MeasureSpec.getSize(widthMeasureSpec);
113 final int height = MeasureSpec.getSize(heightMeasureSpec);
Michael Jurkaea2daff2011-05-17 18:21:03 -0700114
Patrick Dubroye61c6a32011-05-19 16:48:48 -0700115 if (mLastMeasureWidth != width || mLastMeasureHeight != height) {
Michael Jurkaea2daff2011-05-17 18:21:03 -0700116 // Create a dummy page and set it up to find out the content width (used by our parent)
117 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
118 setupPage(layout);
119 mPageContentWidth = layout.getContentWidth();
Patrick Dubroye61c6a32011-05-19 16:48:48 -0700120
121 mCellCountX = determineCellCountX(width, layout);
122 mCellCountY = determineCellCountY(height, layout);
123 mLastMeasureWidth = width;
124 mLastMeasureHeight = height;
Michael Jurka87b14902011-05-25 22:13:09 -0700125 removeAllViews();
126 invalidatePageData();
Michael Jurkaea2daff2011-05-17 18:21:03 -0700127 }
Patrick Dubroye61c6a32011-05-19 16:48:48 -0700128 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
129 }
130
131 @Override
132 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurka87b14902011-05-25 22:13:09 -0700133 if (mWaitingToInitPages) {
134 mWaitingToInitPages = false;
Patrick Dubroye61c6a32011-05-19 16:48:48 -0700135 invalidatePageData();
136
137 // invalidatePageData() is what causes the child pages to be created. We need the
138 // children to be measured before layout, so force a new measure here.
139 measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
140 MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
141 }
142 super.onLayout(changed, left, top, right, bottom);
143 }
144
145 private int determineCellCountX(int availableWidth, PagedViewCellLayout layout) {
146 int cellCountX = 0;
147 final int cellWidth = layout.getCellWidth();
148
149 // Subtract padding for current page and adjacent pages
150 availableWidth -= mPageLayoutPaddingLeft * 2 + mPageLayoutPaddingRight * 2;
151
152 availableWidth -= cellWidth; // Assume at least one column
153 cellCountX = 1 + availableWidth / (cellWidth + mPageLayoutWidthGap);
154 availableWidth = availableWidth % (cellWidth + mPageLayoutWidthGap);
155
156 // Ensures that we show at least 30% of the holo icons on each side
157 final int minLeftoverWidth = (int) (cellWidth * 0.6f);
158
159 // Reserve room for the holo outlines
160 if (cellCountX <= 4) {
161 // When we're really tight on space, just pack the icons a bit closer together
162 final int missingWidth = minLeftoverWidth - availableWidth;
163 if (missingWidth > 0) {
164 mPageLayoutWidthGap -= Math.ceil(missingWidth * 1.0f / (cellCountX - 1));
165 }
166 } else {
167 if (cellCountX >= 8) {
168 // Carve out a few extra columns for very large widths
169 cellCountX = (int) (cellCountX * 0.9f);
170 } else if (availableWidth < minLeftoverWidth) {
171 cellCountX -= 1;
172 }
173 }
174 return cellCountX;
175 }
176
177 private int determineCellCountY(int availableHeight, PagedViewCellLayout layout) {
178 final int cellHeight = layout.getCellHeight();
179 final int screenHeight = mLauncher.getResources().getDisplayMetrics().heightPixels;
180
181 availableHeight -= mPageLayoutPaddingTop + mPageLayoutPaddingBottom;
182 availableHeight -= cellHeight; // Assume at least one row
183 availableHeight -= screenHeight * 0.16f;
Patrick Dubroy1a009332011-05-23 16:15:09 -0700184 if (availableHeight > 0) {
185 return Math.min(mMaxCellCountY,
186 1 + availableHeight / (cellHeight + mPageLayoutHeightGap));
187 }
188 return 0;
189 }
190
191 int getCellCountX() {
192 return mCellCountX;
193 }
194
195 int getCellCountY() {
196 return mCellCountY;
Michael Jurkaea2daff2011-05-17 18:21:03 -0700197 }
198
Michael Jurkaabded662011-03-04 12:06:57 -0800199 void allowHardwareLayerCreation() {
200 // This is called after the first time we launch into All Apps. Before that point,
201 // there's no need for hardware layers here since there's a hardware layer set on the
202 // parent, AllAppsTabbed, during the AllApps transition -- creating hardware layers here
203 // before the animation is done slows down the animation
204 if (mAllowHardwareLayerCreation) {
205 return;
206 }
207 mAllowHardwareLayerCreation = true;
208 int childCount = getChildCount();
209 for (int i = 0; i < childCount; i++) {
210 PagedViewCellLayout page = (PagedViewCellLayout) getChildAt(i);
211 page.allowHardwareLayerCreation();
212 }
213 }
214
Winson Chung7da10252010-10-28 16:07:04 -0700215 @Override
Winson Chung321e9ee2010-08-09 13:37:56 -0700216 public void setLauncher(Launcher launcher) {
217 mLauncher = launcher;
Patrick Dubroy2b9ff372010-09-07 17:49:27 -0700218 mLauncher.setAllAppsPagedView(this);
Winson Chung321e9ee2010-08-09 13:37:56 -0700219 }
220
221 @Override
222 public void setDragController(DragController dragger) {
223 mDragController = dragger;
224 }
225
226 public void setAppFilter(int filterType) {
227 mAppFilter = filterType;
Winson Chung80baf5a2010-08-09 16:03:15 -0700228 if (mApps != null) {
229 mFilteredApps = rebuildFilteredApps(mApps);
Winson Chung86f77532010-08-24 11:08:22 -0700230 setCurrentPage(0);
Winson Chung80baf5a2010-08-09 16:03:15 -0700231 invalidatePageData();
232 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700233 }
234
235 @Override
236 public void zoom(float zoom, boolean animate) {
237 mZoom = zoom;
238 cancelLongPress();
239
240 if (isVisible()) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700241 if (animate) {
242 startAnimation(AnimationUtils.loadAnimation(getContext(),
243 R.anim.all_apps_2d_fade_in));
244 } else {
245 onAnimationEnd();
246 }
247 } else {
248 if (animate) {
249 startAnimation(AnimationUtils.loadAnimation(getContext(),
250 R.anim.all_apps_2d_fade_out));
251 } else {
252 onAnimationEnd();
253 }
254 }
255 }
256
257 protected void onAnimationEnd() {
258 if (!isVisible()) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700259 mZoom = 0.0f;
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700260
261 endChoiceMode();
Winson Chung321e9ee2010-08-09 13:37:56 -0700262 } else {
263 mZoom = 1.0f;
264 }
265
266 if (mLauncher != null)
267 mLauncher.zoomed(mZoom);
268 }
269
270 private int getChildIndexForGrandChild(View v) {
271 final int childCount = getChildCount();
272 for (int i = 0; i < childCount; ++i) {
Michael Jurka8245a862011-02-01 17:53:59 -0800273 final Page layout = (Page) getChildAt(i);
274 if (layout.indexOfChildOnPage(v) > -1) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700275 return i;
276 }
277 }
278 return -1;
279 }
280
281 @Override
282 public void onClick(View v) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700283 // if we are already in a choice mode, then just change the selection
284 if (v instanceof Checkable) {
285 if (!isChoiceMode(CHOICE_MODE_NONE)) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700286 Checkable c = (Checkable) v;
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700287 if (isChoiceMode(CHOICE_MODE_SINGLE)) {
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700288 // Uncheck all the other grandchildren, and toggle the clicked one
289 boolean wasChecked = c.isChecked();
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700290 resetCheckedGrandchildren();
Patrick Dubroy9f7aec82010-09-06 11:03:37 -0700291 c.setChecked(!wasChecked);
292 } else {
293 c.toggle();
294 }
295 if (getCheckedGrandchildren().size() == 0) {
296 endChoiceMode();
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700297 }
298
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700299 return;
300 }
301 }
302
303 // otherwise continue and launch the application
Winson Chung321e9ee2010-08-09 13:37:56 -0700304 int childIndex = getChildIndexForGrandChild(v);
Winson Chung86f77532010-08-24 11:08:22 -0700305 if (childIndex == getCurrentPage()) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700306 final ApplicationInfo app = (ApplicationInfo) v.getTag();
307
Winson Chung80baf5a2010-08-09 16:03:15 -0700308 // animate some feedback to the click
309 animateClickFeedback(v, new Runnable() {
Winson Chung321e9ee2010-08-09 13:37:56 -0700310 @Override
Winson Chung80baf5a2010-08-09 16:03:15 -0700311 public void run() {
Winson Chung321e9ee2010-08-09 13:37:56 -0700312 mLauncher.startActivitySafely(app.intent, app);
313 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700314 });
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700315
316 endChoiceMode();
Winson Chung321e9ee2010-08-09 13:37:56 -0700317 }
318 }
319
Patrick Dubroycd953712011-02-28 15:16:42 -0800320 private void setupDragMode(ApplicationInfo info) {
Michael Jurkaaf91de02010-11-23 16:23:58 -0800321 mLauncher.getWorkspace().shrink(Workspace.ShrinkState.BOTTOM_VISIBLE);
Patrick Dubroycd953712011-02-28 15:16:42 -0800322
323 // Only show the uninstall button if the app is uninstallable.
324 if ((info.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0) {
325 DeleteZone allAppsDeleteZone = (DeleteZone)
326 mLauncher.findViewById(R.id.all_apps_delete_zone);
327 allAppsDeleteZone.setDragAndDropEnabled(true);
328
329 if ((info.flags & ApplicationInfo.UPDATED_SYSTEM_APP_FLAG) != 0) {
330 allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps_system_app);
331 } else {
332 allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps);
333 }
334 }
Michael Jurkab8e14472010-12-20 16:06:10 -0800335
Adam Cohencdc30d52010-12-01 15:09:47 -0800336 ApplicationInfoDropTarget allAppsInfoButton =
337 (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target);
338 allAppsInfoButton.setDragAndDropEnabled(true);
Adam Cohencdc30d52010-12-01 15:09:47 -0800339 }
340
341 private void tearDownDragMode() {
342 post(new Runnable() {
343 // Once the drag operation has fully completed, hence the post, we want to disable the
344 // deleteZone and the appInfoButton in all apps, and re-enable the instance which
345 // live in the workspace
346 public void run() {
Michael Jurkab8e14472010-12-20 16:06:10 -0800347 DeleteZone allAppsDeleteZone =
348 (DeleteZone) mLauncher.findViewById(R.id.all_apps_delete_zone);
Michael Jurkac31820d2011-01-16 16:57:05 -0800349 // if onDestroy was called on Launcher, we might have already deleted the
350 // all apps delete zone / info button, so check if they are null
351 if (allAppsDeleteZone != null) allAppsDeleteZone.setDragAndDropEnabled(false);
Michael Jurkab8e14472010-12-20 16:06:10 -0800352
Adam Cohencdc30d52010-12-01 15:09:47 -0800353 ApplicationInfoDropTarget allAppsInfoButton =
354 (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target);
Michael Jurkac31820d2011-01-16 16:57:05 -0800355 if (allAppsInfoButton != null) allAppsInfoButton.setDragAndDropEnabled(false);
Adam Cohencdc30d52010-12-01 15:09:47 -0800356 }
357 });
358 resetCheckedGrandchildren();
359 mDragController.removeDropTarget(this);
360 }
361
Winson Chung321e9ee2010-08-09 13:37:56 -0700362 @Override
Michael Jurka72b079e2010-12-10 01:03:53 -0800363 protected boolean beginDragging(View v) {
Winson Chung304dcde2011-01-07 11:17:23 -0800364 if (!v.isInTouchMode()) return false;
365 if (!super.beginDragging(v)) return false;
Winson Chung321e9ee2010-08-09 13:37:56 -0700366
367 ApplicationInfo app = (ApplicationInfo) v.getTag();
368 app = new ApplicationInfo(app);
369
Patrick Dubroycd953712011-02-28 15:16:42 -0800370 // Start drag mode after the item is selected
371 setupDragMode(app);
372
Michael Jurkad3ef3062010-11-23 16:23:58 -0800373 // get icon (top compound drawable, index is 1)
Winson Chungcd4bc492010-12-09 18:52:32 -0800374 final TextView tv = (TextView) v;
375 final Drawable icon = tv.getCompoundDrawables()[1];
376 Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
Michael Jurkad3ef3062010-11-23 16:23:58 -0800377 Bitmap.Config.ARGB_8888);
378 Canvas c = new Canvas(b);
Winson Chungcd4bc492010-12-09 18:52:32 -0800379 c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
Michael Jurkad3ef3062010-11-23 16:23:58 -0800380 icon.draw(c);
Winson Chungcd4bc492010-12-09 18:52:32 -0800381
382 // We toggle the checked state _after_ we create the view for the drag in case toggling the
383 // checked state changes the view's look
384 if (v instanceof Checkable) {
385 // In preparation for drag, we always reset the checked grand children regardless of
386 // what choice mode we are in
387 resetCheckedGrandchildren();
388
389 // Toggle the selection on the dragged app
390 Checkable checkable = (Checkable) v;
Winson Chung59e1f9a2010-12-21 11:31:54 -0800391
392 // Note: we toggle the checkable state to actually cause an alpha fade for the duration
393 // of the drag of the item. (The fade-in will occur when all checked states are
394 // disabled when dragging ends)
Winson Chungcd4bc492010-12-09 18:52:32 -0800395 checkable.toggle();
396 }
397
398 // Start the drag
Winson Chung400438b2011-01-16 17:53:48 -0800399 mLauncher.lockScreenOrientation();
Michael Jurkad3ef3062010-11-23 16:23:58 -0800400 mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
401 mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, null);
Winson Chungcd4bc492010-12-09 18:52:32 -0800402 b.recycle();
Winson Chung321e9ee2010-08-09 13:37:56 -0700403 return true;
404 }
405
406 @Override
Patrick Dubroya669d792010-11-23 14:40:33 -0800407 public void onDragViewVisible() {
408 }
409
410 @Override
Patrick Dubroy5f445422011-02-18 14:35:21 -0800411 public void onDropCompleted(View target, Object dragInfo, boolean success) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700412 // close the choice action mode if we have a proper drop
413 if (target != this) {
414 endChoiceMode();
415 }
Adam Cohencdc30d52010-12-01 15:09:47 -0800416 tearDownDragMode();
Patrick Dubroy7bccb422011-01-20 14:50:55 -0800417 mLauncher.getWorkspace().onDragStopped(success);
Winson Chung400438b2011-01-16 17:53:48 -0800418 mLauncher.unlockScreenOrientation();
Winson Chung321e9ee2010-08-09 13:37:56 -0700419 }
420
Michael Jurka12ac0d62011-02-23 11:48:32 -0800421 int getPageContentWidth() {
422 return mPageContentWidth;
423 }
424
Winson Chung321e9ee2010-08-09 13:37:56 -0700425 @Override
426 public boolean isVisible() {
427 return mZoom > 0.001f;
428 }
429
430 @Override
431 public boolean isAnimating() {
432 return (getAnimation() != null);
433 }
434
435 private ArrayList<ApplicationInfo> rebuildFilteredApps(ArrayList<ApplicationInfo> apps) {
436 ArrayList<ApplicationInfo> filteredApps = new ArrayList<ApplicationInfo>();
437 if (mAppFilter == ALL_APPS_FLAG) {
438 return apps;
439 } else {
440 final int length = apps.size();
441 for (int i = 0; i < length; ++i) {
442 ApplicationInfo info = apps.get(i);
443 if ((info.flags & mAppFilter) > 0) {
444 filteredApps.add(info);
445 }
446 }
Winson Chung78403fe2011-01-21 15:38:02 -0800447 Collections.sort(filteredApps, LauncherModel.APP_INSTALL_TIME_COMPARATOR);
Winson Chung321e9ee2010-08-09 13:37:56 -0700448 }
449 return filteredApps;
450 }
451
452 @Override
453 public void setApps(ArrayList<ApplicationInfo> list) {
454 mApps = list;
Winson Chung80baf5a2010-08-09 16:03:15 -0700455 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
Winson Chung321e9ee2010-08-09 13:37:56 -0700456 mFilteredApps = rebuildFilteredApps(mApps);
Winson Chung04998342011-01-05 13:54:43 -0800457 mPageViewIconCache.retainAllApps(list);
Winson Chung321e9ee2010-08-09 13:37:56 -0700458 invalidatePageData();
459 }
460
Winson Chung80baf5a2010-08-09 16:03:15 -0700461 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
462 // we add it in place, in alphabetical order
463 final int count = list.size();
464 for (int i = 0; i < count; ++i) {
465 final ApplicationInfo info = list.get(i);
466 final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
467 if (index < 0) {
468 mApps.add(-(index + 1), info);
Winson Chung452821f2011-01-14 16:39:47 -0800469 } else {
470 mApps.add(index, info);
Winson Chung80baf5a2010-08-09 16:03:15 -0700471 }
472 }
473 mFilteredApps = rebuildFilteredApps(mApps);
474 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700475 @Override
476 public void addApps(ArrayList<ApplicationInfo> list) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700477 addAppsWithoutInvalidate(list);
Winson Chung321e9ee2010-08-09 13:37:56 -0700478 invalidatePageData();
479 }
480
Winson Chung80baf5a2010-08-09 16:03:15 -0700481 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
Winson Chung10fefb12010-11-01 11:57:06 -0700482 // End the choice mode if any of the items in the list that are being removed are
483 // currently selected
484 ArrayList<Checkable> checkedList = getCheckedGrandchildren();
485 HashSet<ApplicationInfo> checkedAppInfos = new HashSet<ApplicationInfo>();
486 for (Checkable checked : checkedList) {
487 PagedViewIcon icon = (PagedViewIcon) checked;
488 checkedAppInfos.add((ApplicationInfo) icon.getTag());
489 }
490 for (ApplicationInfo info : list) {
491 if (checkedAppInfos.contains(info)) {
492 endChoiceMode();
493 break;
494 }
495 }
496
497 // Loop through all the apps and remove apps that have the same component
Winson Chung321e9ee2010-08-09 13:37:56 -0700498 final int length = list.size();
499 for (int i = 0; i < length; ++i) {
Winson Chung241c3b42010-08-25 16:53:03 -0700500 final ApplicationInfo info = list.get(i);
501 int removeIndex = findAppByComponent(mApps, info);
Winson Chung321e9ee2010-08-09 13:37:56 -0700502 if (removeIndex > -1) {
503 mApps.remove(removeIndex);
Winson Chung04998342011-01-05 13:54:43 -0800504 mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
Winson Chung321e9ee2010-08-09 13:37:56 -0700505 }
506 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700507 mFilteredApps = rebuildFilteredApps(mApps);
508 }
Michael Jurkaabded662011-03-04 12:06:57 -0800509
Winson Chung80baf5a2010-08-09 16:03:15 -0700510 @Override
511 public void removeApps(ArrayList<ApplicationInfo> list) {
512 removeAppsWithoutInvalidate(list);
Winson Chung321e9ee2010-08-09 13:37:56 -0700513 invalidatePageData();
514 }
515
516 @Override
517 public void updateApps(ArrayList<ApplicationInfo> list) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700518 removeAppsWithoutInvalidate(list);
519 addAppsWithoutInvalidate(list);
520 invalidatePageData();
Winson Chung321e9ee2010-08-09 13:37:56 -0700521 }
522
523 private int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
Winson Chung8b534782011-02-23 13:43:59 -0800524 if (item != null && item.intent != null) {
525 ComponentName removeComponent = item.intent.getComponent();
526 final int length = list.size();
527 for (int i = 0; i < length; ++i) {
528 ApplicationInfo info = list.get(i);
529 if (info.intent.getComponent().equals(removeComponent)) {
530 return i;
531 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700532 }
533 }
534 return -1;
535 }
536
537 @Override
538 public void dumpState() {
539 ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
540 }
541
542 @Override
543 public void surrender() {
544 // do nothing?
545 }
546
Michael Jurka12ac0d62011-02-23 11:48:32 -0800547 private void setupPage(PagedViewCellLayout layout) {
548 layout.setCellCount(mCellCountX, mCellCountY);
549 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, mPageLayoutPaddingRight,
550 mPageLayoutPaddingBottom);
551 layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
552 }
553
Winson Chung321e9ee2010-08-09 13:37:56 -0700554 @Override
Michael Jurka87b14902011-05-25 22:13:09 -0700555 protected void invalidatePageData() {
556 if (mWaitingToInitPages || mCellCountX <= 0 || mCellCountY <= 0) {
Michael Jurkaea2daff2011-05-17 18:21:03 -0700557 // We don't know our size yet, which means we haven't calculated cell count x/y;
558 // onMeasure will call us once we figure out our size
559 return;
560 }
Michael Jurka87b14902011-05-25 22:13:09 -0700561 super.invalidatePageData();
562 }
563
564 @Override
565 public void syncPages() {
Winson Chung96785572010-10-14 13:37:13 -0700566 // ensure that we have the right number of pages (min of 1, since we have placeholders)
567 int numPages = Math.max(1,
568 (int) Math.ceil((float) mFilteredApps.size() / (mCellCountX * mCellCountY)));
Winson Chung321e9ee2010-08-09 13:37:56 -0700569 int curNumPages = getChildCount();
570 // remove any extra pages after the "last" page
571 int extraPageDiff = curNumPages - numPages;
572 for (int i = 0; i < extraPageDiff; ++i) {
573 removeViewAt(numPages);
574 }
575 // add any necessary pages
576 for (int i = curNumPages; i < numPages; ++i) {
577 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
Michael Jurkaabded662011-03-04 12:06:57 -0800578 if (mAllowHardwareLayerCreation) {
579 layout.allowHardwareLayerCreation();
580 }
Michael Jurka12ac0d62011-02-23 11:48:32 -0800581 setupPage(layout);
Winson Chung321e9ee2010-08-09 13:37:56 -0700582 addView(layout);
583 }
584
585 // bound the current page
Winson Chung86f77532010-08-24 11:08:22 -0700586 setCurrentPage(Math.max(0, Math.min(numPages - 1, getCurrentPage())));
Winson Chung321e9ee2010-08-09 13:37:56 -0700587 }
588
589 @Override
590 public void syncPageItems(int page) {
Winson Chung96785572010-10-14 13:37:13 -0700591 // Ensure that we have the right number of items on the pages
Winson Chung80baf5a2010-08-09 16:03:15 -0700592 final int cellsPerPage = mCellCountX * mCellCountY;
593 final int startIndex = page * cellsPerPage;
594 final int endIndex = Math.min(startIndex + cellsPerPage, mFilteredApps.size());
Winson Chung321e9ee2010-08-09 13:37:56 -0700595 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
Winson Chung80baf5a2010-08-09 16:03:15 -0700596
Winson Chung96785572010-10-14 13:37:13 -0700597 if (!mFilteredApps.isEmpty()) {
Michael Jurka8245a862011-02-01 17:53:59 -0800598 int curNumPageItems = layout.getPageChildCount();
Winson Chung96785572010-10-14 13:37:13 -0700599 int numPageItems = endIndex - startIndex;
Winson Chung80baf5a2010-08-09 16:03:15 -0700600
Winson Chung96785572010-10-14 13:37:13 -0700601 // If we were previously an empty page, then restart anew
602 boolean wasEmptyPage = false;
603 if (curNumPageItems == 1) {
Michael Jurka8245a862011-02-01 17:53:59 -0800604 View icon = layout.getChildOnPageAt(0);
Winson Chung96785572010-10-14 13:37:13 -0700605 if (icon.getTag() == null) {
606 wasEmptyPage = true;
607 }
608 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700609
Winson Chung96785572010-10-14 13:37:13 -0700610 if (wasEmptyPage) {
611 // Remove all the previous items
612 curNumPageItems = 0;
Michael Jurka8245a862011-02-01 17:53:59 -0800613 layout.removeAllViewsOnPage();
Winson Chung96785572010-10-14 13:37:13 -0700614 } else {
615 // Remove any extra items
616 int extraPageItemsDiff = curNumPageItems - numPageItems;
617 for (int i = 0; i < extraPageItemsDiff; ++i) {
Michael Jurka8245a862011-02-01 17:53:59 -0800618 layout.removeViewOnPageAt(numPageItems);
Winson Chung96785572010-10-14 13:37:13 -0700619 }
620 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700621
Winson Chung96785572010-10-14 13:37:13 -0700622 // Add any necessary items
623 for (int i = curNumPageItems; i < numPageItems; ++i) {
624 TextView text = (TextView) mInflater.inflate(
625 R.layout.all_apps_paged_view_application, layout, false);
626 text.setOnClickListener(this);
627 text.setOnLongClickListener(this);
Michael Jurka72b079e2010-12-10 01:03:53 -0800628 text.setOnTouchListener(this);
Winson Chung321e9ee2010-08-09 13:37:56 -0700629
Winson Chung96785572010-10-14 13:37:13 -0700630 layout.addViewToCellLayout(text, -1, i,
631 new PagedViewCellLayout.LayoutParams(0, 0, 1, 1));
632 }
633
634 // Actually reapply to the existing text views
Winson Chung04998342011-01-05 13:54:43 -0800635 final int numPages = getPageCount();
Winson Chung96785572010-10-14 13:37:13 -0700636 for (int i = startIndex; i < endIndex; ++i) {
637 final int index = i - startIndex;
638 final ApplicationInfo info = mFilteredApps.get(i);
Michael Jurka8245a862011-02-01 17:53:59 -0800639 PagedViewIcon icon = (PagedViewIcon) layout.getChildOnPageAt(index);
Winson Chung04998342011-01-05 13:54:43 -0800640 icon.applyFromApplicationInfo(info, mPageViewIconCache, true, (numPages > 1));
Winson Chung96785572010-10-14 13:37:13 -0700641
642 PagedViewCellLayout.LayoutParams params =
643 (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
644 params.cellX = index % mCellCountX;
645 params.cellY = index / mCellCountX;
646 }
647
648 // Default to left-aligned icons
649 layout.enableCenteredContent(false);
650 } else {
651 // There are no items, so show the user a small message
652 TextView icon = (TextView) mInflater.inflate(
653 R.layout.all_apps_no_items_placeholder, layout, false);
654 switch (mAppFilter) {
Winson Chung96785572010-10-14 13:37:13 -0700655 case ApplicationInfo.DOWNLOADED_FLAG:
656 icon.setText(mContext.getString(R.string.all_apps_no_downloads));
657 break;
658 default: break;
659 }
660
661 // Center-align the message
662 layout.enableCenteredContent(true);
Michael Jurka8245a862011-02-01 17:53:59 -0800663 layout.removeAllViewsOnPage();
Winson Chung96785572010-10-14 13:37:13 -0700664 layout.addViewToCellLayout(icon, -1, 0,
Winson Chung532a52a2011-01-18 12:18:00 -0800665 new PagedViewCellLayout.LayoutParams(0, 0, 4, 1));
Winson Chung321e9ee2010-08-09 13:37:56 -0700666 }
Michael Jurkac5e49022011-02-16 12:04:02 -0800667 layout.createHardwareLayers();
Winson Chung321e9ee2010-08-09 13:37:56 -0700668 }
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700669
670 /*
671 * We don't actually use AllAppsPagedView as a drop target... it's only used to intercept a drop
672 * to the workspace.
673 */
674 @Override
675 public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
676 DragView dragView, Object dragInfo) {
677 return false;
678 }
679 @Override
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700680 public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset,
681 int yOffset, DragView dragView, Object dragInfo) {
682 return null;
683 }
684 @Override
685 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
686 DragView dragView, Object dragInfo) {}
687 @Override
688 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
689 DragView dragView, Object dragInfo) {}
690 @Override
691 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
692 DragView dragView, Object dragInfo) {}
693 @Override
694 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
695 DragView dragView, Object dragInfo) {}
Michael Jurka0280c3b2010-09-17 15:00:07 -0700696
697 public boolean isDropEnabled() {
698 return true;
699 }
Winson Chung321e9ee2010-08-09 13:37:56 -0700700}