blob: f832b62bdfc3a0cb36593adfbdc8cf68790cbfc4 [file] [log] [blame]
Winson Chung80baf5a2010-08-09 16:03:15 -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 Jurka0280c3b2010-09-17 15:00:07 -070019import com.android.launcher.R;
Winson Chung80baf5a2010-08-09 16:03:15 -070020
21import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.res.Resources;
Winson Chunge3193b92010-09-10 11:44:42 -070029import android.content.res.TypedArray;
Winson Chung80baf5a2010-08-09 16:03:15 -070030import android.graphics.Bitmap;
Winson Chung86f77532010-08-24 11:08:22 -070031import android.graphics.Canvas;
32import android.graphics.Rect;
Michael Jurka0280c3b2010-09-17 15:00:07 -070033import android.graphics.Bitmap.Config;
Winson Chung80baf5a2010-08-09 16:03:15 -070034import android.graphics.Region.Op;
Winson Chung80baf5a2010-08-09 16:03:15 -070035import android.graphics.drawable.Drawable;
36import android.provider.LiveFolders;
37import android.util.AttributeSet;
38import android.util.Log;
Winson Chungd0d43012010-09-26 17:26:45 -070039import android.view.ActionMode;
Winson Chunge3193b92010-09-10 11:44:42 -070040import android.view.Gravity;
Winson Chung80baf5a2010-08-09 16:03:15 -070041import android.view.LayoutInflater;
Winson Chungd0d43012010-09-26 17:26:45 -070042import android.view.Menu;
43import android.view.MenuItem;
Winson Chunge3193b92010-09-10 11:44:42 -070044import android.view.MotionEvent;
Winson Chung80baf5a2010-08-09 16:03:15 -070045import android.view.View;
Winson Chungd0d43012010-09-26 17:26:45 -070046import android.view.ViewGroup;
47import android.widget.Checkable;
Winson Chunge3193b92010-09-10 11:44:42 -070048import android.widget.ImageView;
49import android.widget.LinearLayout;
Winson Chung80baf5a2010-08-09 16:03:15 -070050import android.widget.TextView;
51
Michael Jurka0280c3b2010-09-17 15:00:07 -070052import java.util.ArrayList;
53import java.util.Collections;
54import java.util.Comparator;
55import java.util.List;
Winson Chung80baf5a2010-08-09 16:03:15 -070056
57public class CustomizePagedView extends PagedView
Winson Chunge8878e32010-09-15 20:37:09 -070058 implements View.OnLongClickListener, View.OnClickListener,
Winson Chungd0d43012010-09-26 17:26:45 -070059 DragSource, ActionMode.Callback {
Winson Chung80baf5a2010-08-09 16:03:15 -070060
61 public enum CustomizationType {
62 WidgetCustomization,
63 FolderCustomization,
64 ShortcutCustomization,
Winson Chung5ffd8ea2010-09-23 18:40:29 -070065 WallpaperCustomization,
66 ApplicationCustomization
Winson Chung80baf5a2010-08-09 16:03:15 -070067 }
68
69 private static final String TAG = "CustomizeWorkspace";
70 private static final boolean DEBUG = false;
71
72 private Launcher mLauncher;
73 private DragController mDragController;
74 private PackageManager mPackageManager;
75
76 private CustomizationType mCustomizationType;
77
Winson Chunge3193b92010-09-10 11:44:42 -070078 // The layout used to emulate the workspace in resolve the cell dimensions of a widget
79 private PagedViewCellLayout mWorkspaceWidgetLayout;
80
81 // The mapping between the pages and the widgets that will be laid out on them
82 private ArrayList<ArrayList<AppWidgetProviderInfo>> mWidgetPages;
83
84 // The max dimensions for the ImageView we use for displaying the widget
85 private int mMaxWidgetWidth;
86
87 // The max number of widget cells to take a "page" of widget
88 private int mMaxWidgetsCellHSpan;
89
90 // The raw sources of data for each of the different tabs of the customization page
Winson Chung80baf5a2010-08-09 16:03:15 -070091 private List<AppWidgetProviderInfo> mWidgetList;
92 private List<ResolveInfo> mFolderList;
93 private List<ResolveInfo> mShortcutList;
Winson Chunge8878e32010-09-15 20:37:09 -070094 private List<ResolveInfo> mWallpaperList;
Winson Chung5ffd8ea2010-09-23 18:40:29 -070095 private List<ApplicationInfo> mApps;
Winson Chung80baf5a2010-08-09 16:03:15 -070096
Winson Chunge3193b92010-09-10 11:44:42 -070097 private static final int sMinWidgetCellHSpan = 2;
98 private static final int sMaxWidgetCellHSpan = 4;
99
Michael Jurka3125d9d2010-09-27 11:30:20 -0700100 private int mChoiceModeTitleText;
101
Winson Chunge3193b92010-09-10 11:44:42 -0700102 // The scale factor for widget previews inside the widget drawer
103 private static final float sScaleFactor = 0.75f;
Winson Chung80baf5a2010-08-09 16:03:15 -0700104
105 private final Canvas mCanvas = new Canvas();
106 private final LayoutInflater mInflater;
107
108 public CustomizePagedView(Context context) {
Winson Chunge3193b92010-09-10 11:44:42 -0700109 this(context, null, 0);
Winson Chung80baf5a2010-08-09 16:03:15 -0700110 }
111
112 public CustomizePagedView(Context context, AttributeSet attrs) {
Winson Chunge3193b92010-09-10 11:44:42 -0700113 this(context, attrs, 0);
114 }
115
116 public CustomizePagedView(Context context, AttributeSet attrs, int defStyle) {
117 super(context, attrs, defStyle);
118
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700119 TypedArray a;
120 a = context.obtainStyledAttributes(attrs, R.styleable.CustomizePagedView,
Winson Chunge3193b92010-09-10 11:44:42 -0700121 defStyle, 0);
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700122 mMaxWidgetsCellHSpan = a.getInt(R.styleable.CustomizePagedView_widgetCellCountX, 8);
123 a.recycle();
124 a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
125 mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 7);
126 mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
Adam Cohen9c4949e2010-10-05 12:27:22 -0700127
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700128 a.recycle();
Winson Chung80baf5a2010-08-09 16:03:15 -0700129 mCustomizationType = CustomizationType.WidgetCustomization;
Winson Chunge3193b92010-09-10 11:44:42 -0700130 mWidgetPages = new ArrayList<ArrayList<AppWidgetProviderInfo>>();
131 mWorkspaceWidgetLayout = new PagedViewCellLayout(context);
Winson Chung80baf5a2010-08-09 16:03:15 -0700132 mInflater = LayoutInflater.from(context);
Winson Chunge3193b92010-09-10 11:44:42 -0700133
Winson Chung80baf5a2010-08-09 16:03:15 -0700134 setVisibility(View.GONE);
135 setSoundEffectsEnabled(false);
Winson Chunge3193b92010-09-10 11:44:42 -0700136 setupWorkspaceLayout();
Winson Chung80baf5a2010-08-09 16:03:15 -0700137 }
138
139 public void setLauncher(Launcher launcher) {
140 Context context = getContext();
141 mLauncher = launcher;
142 mPackageManager = context.getPackageManager();
143 }
144
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700145 /**
146 * Sets the list of applications that launcher has loaded.
147 */
148 public void setApps(ArrayList<ApplicationInfo> list) {
149 mApps = list;
150 Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
Winson Chung5f941722010-09-28 16:36:43 -0700151
152 // Update the widgets/shortcuts to reflect changes in the set of available apps
153 update();
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700154 }
155
156 /**
157 * Convenience function to add new items to the set of applications that were previously loaded.
158 * Called by both updateApps() and setApps().
159 */
160 private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
161 // we add it in place, in alphabetical order
162 final int count = list.size();
163 for (int i = 0; i < count; ++i) {
164 final ApplicationInfo info = list.get(i);
165 final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
166 if (index < 0) {
167 mApps.add(-(index + 1), info);
168 }
169 }
170 }
171
172 /**
173 * Adds new applications to the loaded list, and notifies the paged view to update itself.
174 */
175 public void addApps(ArrayList<ApplicationInfo> list) {
176 addAppsWithoutInvalidate(list);
Winson Chung5f941722010-09-28 16:36:43 -0700177
178 // Update the widgets/shortcuts to reflect changes in the set of available apps
179 update();
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700180 }
181
182 /**
183 * Convenience function to remove items to the set of applications that were previously loaded.
184 * Called by both updateApps() and removeApps().
185 */
186 private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
187 // loop through all the apps and remove apps that have the same component
188 final int length = list.size();
189 for (int i = 0; i < length; ++i) {
190 final ApplicationInfo info = list.get(i);
191 int removeIndex = findAppByComponent(mApps, info);
192 if (removeIndex > -1) {
193 mApps.remove(removeIndex);
194 mPageViewIconCache.removeOutline(info);
195 }
196 }
197 }
198
199 /**
200 * Removes applications from the loaded list, and notifies the paged view to update itself.
201 */
202 public void removeApps(ArrayList<ApplicationInfo> list) {
203 removeAppsWithoutInvalidate(list);
Winson Chung5f941722010-09-28 16:36:43 -0700204
205 // Update the widgets/shortcuts to reflect changes in the set of available apps
206 update();
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700207 }
208
209 /**
210 * Updates a set of applications from the loaded list, and notifies the paged view to update
211 * itself.
212 */
213 public void updateApps(ArrayList<ApplicationInfo> list) {
214 // We remove and re-add the updated applications list because it's properties may have
215 // changed (ie. the title), and this will ensure that the items will be in their proper
216 // place in the list.
217 removeAppsWithoutInvalidate(list);
218 addAppsWithoutInvalidate(list);
Winson Chung5f941722010-09-28 16:36:43 -0700219
220 // Update the widgets/shortcuts to reflect changes in the set of available apps
221 update();
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700222 }
223
224 /**
225 * Convenience function to find matching ApplicationInfos for removal.
226 */
227 private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
228 ComponentName removeComponent = item.intent.getComponent();
229 final int length = list.size();
230 for (int i = 0; i < length; ++i) {
231 ApplicationInfo info = list.get(i);
232 if (info.intent.getComponent().equals(removeComponent)) {
233 return i;
234 }
235 }
236 return -1;
237 }
238
Winson Chung80baf5a2010-08-09 16:03:15 -0700239 public void update() {
240 Context context = getContext();
241
242 // get the list of widgets
243 mWidgetList = AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
244 Collections.sort(mWidgetList, new Comparator<AppWidgetProviderInfo>() {
245 @Override
246 public int compare(AppWidgetProviderInfo object1, AppWidgetProviderInfo object2) {
247 return object1.label.compareTo(object2.label);
248 }
249 });
250
251 Comparator<ResolveInfo> resolveInfoComparator = new Comparator<ResolveInfo>() {
252 @Override
253 public int compare(ResolveInfo object1, ResolveInfo object2) {
254 return object1.loadLabel(mPackageManager).toString().compareTo(
255 object2.loadLabel(mPackageManager).toString());
256 }
257 };
258
259 // get the list of live folder intents
260 Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
261 mFolderList = mPackageManager.queryIntentActivities(liveFolderIntent, 0);
262
263 // manually create a separate entry for creating a folder in Launcher
264 ResolveInfo folder = new ResolveInfo();
265 folder.icon = R.drawable.ic_launcher_folder;
266 folder.labelRes = R.string.group_folder;
267 folder.resolvePackageName = context.getPackageName();
268 mFolderList.add(0, folder);
269 Collections.sort(mFolderList, resolveInfoComparator);
270
271 // get the list of shortcuts
272 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
273 mShortcutList = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
274 Collections.sort(mShortcutList, resolveInfoComparator);
275
Winson Chunge8878e32010-09-15 20:37:09 -0700276 // get the list of wallpapers
277 Intent wallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
278 mWallpaperList = mPackageManager.queryIntentActivities(wallpapersIntent, 0);
279 Collections.sort(mWallpaperList, resolveInfoComparator);
280
Winson Chung241c3b42010-08-25 16:53:03 -0700281 // reset the icon cache
282 mPageViewIconCache.clear();
283
Winson Chunge3193b92010-09-10 11:44:42 -0700284 // Refresh all the tabs
Winson Chung80baf5a2010-08-09 16:03:15 -0700285 invalidatePageData();
286 }
287
288 public void setDragController(DragController dragger) {
289 mDragController = dragger;
290 }
291
292 public void setCustomizationFilter(CustomizationType filterType) {
293 mCustomizationType = filterType;
Winson Chung86f77532010-08-24 11:08:22 -0700294 setCurrentPage(0);
Winson Chung80baf5a2010-08-09 16:03:15 -0700295 invalidatePageData();
Winson Chungd0d43012010-09-26 17:26:45 -0700296
297 // End the current choice mode so that we don't carry selections across tabs
298 endChoiceMode();
Winson Chung80baf5a2010-08-09 16:03:15 -0700299 }
300
301 @Override
302 public void onDropCompleted(View target, boolean success) {
Michael Jurka3e7c7632010-10-02 16:01:03 -0700303 mLauncher.getWorkspace().onDragStopped();
Winson Chung80baf5a2010-08-09 16:03:15 -0700304 }
305
306 @Override
Winson Chunge8878e32010-09-15 20:37:09 -0700307 public void onClick(View v) {
308 if (!v.isInTouchMode()) {
309 return;
310 }
311
Winson Chungd0d43012010-09-26 17:26:45 -0700312 // On certain pages, we allow single tap to mark items as selected so that they can be
313 // dropped onto the mini workspaces
Michael Jurka3125d9d2010-09-27 11:30:20 -0700314 boolean enterChoiceMode = false;
Winson Chungd0d43012010-09-26 17:26:45 -0700315 switch (mCustomizationType) {
316 case WidgetCustomization:
Michael Jurka3125d9d2010-09-27 11:30:20 -0700317 mChoiceModeTitleText = R.string.cab_widget_selection_text;
318 enterChoiceMode = true;
319 break;
Winson Chungd0d43012010-09-26 17:26:45 -0700320 case ApplicationCustomization:
Michael Jurka3125d9d2010-09-27 11:30:20 -0700321 mChoiceModeTitleText = R.string.cab_app_selection_text;
322 enterChoiceMode = true;
323 break;
Winson Chungd0d43012010-09-26 17:26:45 -0700324 case FolderCustomization:
Michael Jurka3125d9d2010-09-27 11:30:20 -0700325 mChoiceModeTitleText = R.string.cab_folder_selection_text;
326 enterChoiceMode = true;
327 break;
Winson Chungd0d43012010-09-26 17:26:45 -0700328 case ShortcutCustomization:
Michael Jurka3125d9d2010-09-27 11:30:20 -0700329 mChoiceModeTitleText = R.string.cab_shortcut_selection_text;
330 enterChoiceMode = true;
331 break;
332 default:
333 break;
334 }
Winson Chungd0d43012010-09-26 17:26:45 -0700335
Michael Jurka3125d9d2010-09-27 11:30:20 -0700336 if (enterChoiceMode) {
Winson Chungd0d43012010-09-26 17:26:45 -0700337 if (v instanceof Checkable) {
338 final Checkable c = (Checkable) v;
339 final boolean wasChecked = c.isChecked();
340 resetCheckedGrandchildren();
341 c.setChecked(!wasChecked);
342
343 // End the current choice mode when we have no items selected
Michael Jurkae17e19c2010-09-28 11:01:39 -0700344 /*if (!c.isChecked()) {
Winson Chungd0d43012010-09-26 17:26:45 -0700345 endChoiceMode();
Michael Jurka3125d9d2010-09-27 11:30:20 -0700346 } else if (isChoiceMode(CHOICE_MODE_NONE)) {
347 endChoiceMode();
348 startChoiceMode(CHOICE_MODE_SINGLE, this);
Michael Jurkae17e19c2010-09-28 11:01:39 -0700349 }*/
350 mChoiceMode = CHOICE_MODE_SINGLE;
351
352 Workspace w = mLauncher.getWorkspace();
353 int currentWorkspaceScreen = mLauncher.getCurrentWorkspaceScreen();
354 final CellLayout cl = (CellLayout)w.getChildAt(currentWorkspaceScreen);
355 cl.setHover(true);
356
357 animateClickFeedback(v, new Runnable() {
358 @Override
359 public void run() {
360 cl.setHover(false);
361 mLauncher.onWorkspaceClick(cl);
362 mChoiceMode = CHOICE_MODE_NONE;
363 }
364 });
Winson Chungd0d43012010-09-26 17:26:45 -0700365 }
366 return;
Winson Chungd0d43012010-09-26 17:26:45 -0700367 }
368
369 // Otherwise, we just handle the single click here
Winson Chunge8878e32010-09-15 20:37:09 -0700370 switch (mCustomizationType) {
371 case WallpaperCustomization:
372 // animate some feedback to the long press
Winson Chungd0d43012010-09-26 17:26:45 -0700373 final View clickView = v;
Winson Chunge8878e32010-09-15 20:37:09 -0700374 animateClickFeedback(v, new Runnable() {
375 @Override
376 public void run() {
377 // add the shortcut
Winson Chungd0d43012010-09-26 17:26:45 -0700378 ResolveInfo info = (ResolveInfo) clickView.getTag();
Winson Chung24ab2f12010-09-16 14:10:47 -0700379 Intent createWallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
380 ComponentName name = new ComponentName(info.activityInfo.packageName,
381 info.activityInfo.name);
382 createWallpapersIntent.setComponent(name);
383 mLauncher.processWallpaper(createWallpapersIntent);
Winson Chunge8878e32010-09-15 20:37:09 -0700384 }
385 });
Winson Chungd0d43012010-09-26 17:26:45 -0700386 break;
387 default:
388 break;
Winson Chunge8878e32010-09-15 20:37:09 -0700389 }
390 }
391
392 @Override
Winson Chung80baf5a2010-08-09 16:03:15 -0700393 public boolean onLongClick(View v) {
394 if (!v.isInTouchMode()) {
395 return false;
396 }
397
Winson Chungd0d43012010-09-26 17:26:45 -0700398 // End the current choice mode before we start dragging anything
399 if (isChoiceMode(CHOICE_MODE_SINGLE)) {
400 endChoiceMode();
401 }
402
403 PendingAddItemInfo createItemInfo;
Winson Chung80baf5a2010-08-09 16:03:15 -0700404 switch (mCustomizationType) {
405 case WidgetCustomization:
Winson Chunge3193b92010-09-10 11:44:42 -0700406 // Get the icon as the drag representation
Winson Chungd0d43012010-09-26 17:26:45 -0700407 final LinearLayout l = (LinearLayout) v;
408 final Drawable icon = ((ImageView) l.findViewById(R.id.widget_preview)).getDrawable();
Winson Chunge3193b92010-09-10 11:44:42 -0700409 Bitmap b = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
410 Bitmap.Config.ARGB_8888);
411 Canvas c = new Canvas(b);
412 icon.draw(c);
Michael Jurka3e7c7632010-10-02 16:01:03 -0700413 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) v.getTag();
Michael Jurkaa63c4522010-08-19 13:52:27 -0700414
Michael Jurka3e7c7632010-10-02 16:01:03 -0700415 mLauncher.getWorkspace().onDragStartedWithItemMinSize(
416 createWidgetInfo.minWidth, createWidgetInfo.minHeight);
417 mDragController.startDrag(v, b, this, createWidgetInfo, DragController.DRAG_ACTION_COPY,
Winson Chungd0d43012010-09-26 17:26:45 -0700418 null);
Winson Chunge3193b92010-09-10 11:44:42 -0700419
420 // Cleanup the icon
421 b.recycle();
Winson Chung80baf5a2010-08-09 16:03:15 -0700422 return true;
423 case FolderCustomization:
Winson Chungd0d43012010-09-26 17:26:45 -0700424 if (v.getTag() instanceof UserFolderInfo) {
425 // The UserFolderInfo tag is only really used for live folders
426 UserFolderInfo folderInfo = (UserFolderInfo) v.getTag();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700427 mDragController.startDrag(
428 v, this, folderInfo, DragController.DRAG_ACTION_COPY, null);
429 } else {
Winson Chungd0d43012010-09-26 17:26:45 -0700430 createItemInfo = (PendingAddItemInfo) v.getTag();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700431 mDragController.startDrag(
432 v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
433 }
Michael Jurka3e7c7632010-10-02 16:01:03 -0700434 mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
Winson Chung80baf5a2010-08-09 16:03:15 -0700435 return true;
436 case ShortcutCustomization:
Winson Chungd0d43012010-09-26 17:26:45 -0700437 createItemInfo = (PendingAddItemInfo) v.getTag();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700438 mDragController.startDrag(
439 v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
Michael Jurka3e7c7632010-10-02 16:01:03 -0700440 mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
Winson Chung80baf5a2010-08-09 16:03:15 -0700441 return true;
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700442 case ApplicationCustomization:
443 // Pick up the application for dropping
444 ApplicationInfo app = (ApplicationInfo) v.getTag();
445 app = new ApplicationInfo(app);
446
447 mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
Michael Jurka3e7c7632010-10-02 16:01:03 -0700448 mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700449 return true;
Winson Chung80baf5a2010-08-09 16:03:15 -0700450 }
451 return false;
452 }
453
Winson Chunge3193b92010-09-10 11:44:42 -0700454 /**
455 * Pre-processes the layout of the different widget pages.
456 * @return the number of pages of widgets that we have
457 */
Winson Chung80baf5a2010-08-09 16:03:15 -0700458 private int relayoutWidgets() {
Winson Chunge3193b92010-09-10 11:44:42 -0700459 if (mWidgetList.isEmpty()) return 0;
Winson Chung80baf5a2010-08-09 16:03:15 -0700460
Winson Chunge3193b92010-09-10 11:44:42 -0700461 // create a new page for the first set of widgets
462 ArrayList<AppWidgetProviderInfo> newPage = new ArrayList<AppWidgetProviderInfo>();
Winson Chung80baf5a2010-08-09 16:03:15 -0700463 mWidgetPages.clear();
Winson Chunge3193b92010-09-10 11:44:42 -0700464 mWidgetPages.add(newPage);
465
466 // do this until we have no more widgets to lay out
467 final int maxNumCellsPerRow = mMaxWidgetsCellHSpan;
468 final int widgetCount = mWidgetList.size();
469 int numCellsInRow = 0;
Winson Chung80baf5a2010-08-09 16:03:15 -0700470 for (int i = 0; i < widgetCount; ++i) {
Winson Chunge3193b92010-09-10 11:44:42 -0700471 final AppWidgetProviderInfo info = mWidgetList.get(i);
Winson Chung80baf5a2010-08-09 16:03:15 -0700472
Winson Chunge3193b92010-09-10 11:44:42 -0700473 // determine the size of the current widget
474 int cellSpanX = Math.max(sMinWidgetCellHSpan, Math.min(sMaxWidgetCellHSpan,
475 mWorkspaceWidgetLayout.estimateCellHSpan(info.minWidth)));
Winson Chung80baf5a2010-08-09 16:03:15 -0700476
Winson Chunge3193b92010-09-10 11:44:42 -0700477 // create a new page if necessary
478 if ((numCellsInRow + cellSpanX) > maxNumCellsPerRow) {
479 numCellsInRow = 0;
480 newPage = new ArrayList<AppWidgetProviderInfo>();
481 mWidgetPages.add(newPage);
Winson Chung80baf5a2010-08-09 16:03:15 -0700482 }
483
Winson Chunge3193b92010-09-10 11:44:42 -0700484 // add the item to the current page
485 newPage.add(info);
486 numCellsInRow += cellSpanX;
Winson Chung80baf5a2010-08-09 16:03:15 -0700487 }
Winson Chunge3193b92010-09-10 11:44:42 -0700488
Winson Chung80baf5a2010-08-09 16:03:15 -0700489 return mWidgetPages.size();
490 }
491
Winson Chunge3193b92010-09-10 11:44:42 -0700492 /**
493 * This method will extract the preview image specified by the widget developer (if it exists),
494 * otherwise, it will try to generate a default image preview with the widget's package icon.
495 * @return the drawable will be used and sized in the ImageView to represent the widget
496 */
497 private Drawable getWidgetIcon(AppWidgetProviderInfo info) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700498 PackageManager packageManager = mLauncher.getPackageManager();
499 String packageName = info.provider.getPackageName();
500 Drawable drawable = null;
501 if (info.previewImage != 0) {
502 drawable = packageManager.getDrawable(packageName, info.previewImage, null);
503 if (drawable == null) {
504 Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon)
505 + " for provider: " + info.provider);
506 } else {
507 return drawable;
508 }
509 }
510
511 // If we don't have a preview image, create a default one
512 if (drawable == null) {
513 Resources resources = mLauncher.getResources();
514
Winson Chung80baf5a2010-08-09 16:03:15 -0700515 // Create a new bitmap to hold the widget preview
Winson Chunge3193b92010-09-10 11:44:42 -0700516 final int minDim = mWorkspaceWidgetLayout.estimateCellWidth(1);
517 final int maxDim = mWorkspaceWidgetLayout.estimateCellWidth(3);
518 int width = (int) (Math.max(minDim, Math.min(maxDim, info.minWidth)) * sScaleFactor);
519 int height = (int) (Math.max(minDim, Math.min(maxDim, info.minHeight)) * sScaleFactor);
Winson Chung80baf5a2010-08-09 16:03:15 -0700520 Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
521 mCanvas.setBitmap(bitmap);
522 // For some reason, we must re-set the clip rect here, otherwise it will be wrong
523 mCanvas.clipRect(0, 0, width, height, Op.REPLACE);
524
525 Drawable background = resources.getDrawable(R.drawable.default_widget_preview);
526 background.setBounds(0, 0, width, height);
527 background.draw(mCanvas);
528
529 // Draw the icon vertically centered, flush left
530 try {
531 Rect tmpRect = new Rect();
532 Drawable icon = null;
Winson Chunge3193b92010-09-10 11:44:42 -0700533 if (info.icon > 0) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700534 icon = packageManager.getDrawable(packageName, info.icon, null);
Winson Chung5f941722010-09-28 16:36:43 -0700535 }
536 if (icon == null) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700537 icon = resources.getDrawable(R.drawable.ic_launcher_application);
538 }
539 background.getPadding(tmpRect);
540
Winson Chunge3193b92010-09-10 11:44:42 -0700541 final int iconSize = minDim / 2;
542 final int offset = iconSize / 4;
543 icon.setBounds(new Rect(offset, offset, offset + iconSize, offset + iconSize));
Winson Chung80baf5a2010-08-09 16:03:15 -0700544 icon.draw(mCanvas);
545 } catch (Resources.NotFoundException e) {
546 // if we can't find the icon, then just don't draw it
547 }
548
Winson Chungb3347bb2010-08-19 14:51:28 -0700549 drawable = new FastBitmapDrawable(bitmap);
Winson Chung80baf5a2010-08-09 16:03:15 -0700550 }
551 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
552 return drawable;
553 }
554
555 private void setupPage(PagedViewCellLayout layout) {
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700556 layout.setCellCount(mCellCountX, mCellCountY);
557 layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, mPageLayoutPaddingRight,
558 mPageLayoutPaddingBottom);
Winson Chung80baf5a2010-08-09 16:03:15 -0700559 }
560
Winson Chunge3193b92010-09-10 11:44:42 -0700561 private void setupWorkspaceLayout() {
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700562 mWorkspaceWidgetLayout.setCellCount(mCellCountX, mCellCountY);
Winson Chunge3193b92010-09-10 11:44:42 -0700563 mWorkspaceWidgetLayout.setPadding(20, 10, 20, 0);
564
565 mMaxWidgetWidth = mWorkspaceWidgetLayout.estimateCellWidth(sMaxWidgetCellHSpan);
566 }
567
Winson Chung80baf5a2010-08-09 16:03:15 -0700568 private void syncWidgetPages() {
569 if (mWidgetList == null) return;
570
Winson Chunge3193b92010-09-10 11:44:42 -0700571 // we need to repopulate with the LinearLayout layout for the widget pages
572 removeAllViews();
Winson Chung80baf5a2010-08-09 16:03:15 -0700573 int numPages = relayoutWidgets();
Winson Chunge3193b92010-09-10 11:44:42 -0700574 for (int i = 0; i < numPages; ++i) {
Winson Chung8e4ec312010-10-13 17:22:51 -0700575 LinearLayout layout = new PagedViewWidgetLayout(getContext());
Winson Chunge3193b92010-09-10 11:44:42 -0700576 layout.setGravity(Gravity.CENTER_HORIZONTAL);
577
578 // Temporary change to prevent the last page from being too small (and items bleeding
579 // onto it). We can remove this once we properly fix the fading algorithm
580 if (i < numPages - 1) {
581 addView(layout, new LinearLayout.LayoutParams(
582 LinearLayout.LayoutParams.WRAP_CONTENT,
583 LinearLayout.LayoutParams.MATCH_PARENT));
584 } else {
585 addView(layout, new LinearLayout.LayoutParams(
586 LinearLayout.LayoutParams.MATCH_PARENT,
587 LinearLayout.LayoutParams.MATCH_PARENT));
588 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700589 }
590 }
591
592 private void syncWidgetPageItems(int page) {
593 // ensure that we have the right number of items on the pages
Winson Chunge3193b92010-09-10 11:44:42 -0700594 LinearLayout layout = (LinearLayout) getChildAt(page);
595 final ArrayList<AppWidgetProviderInfo> list = mWidgetPages.get(page);
Winson Chung80baf5a2010-08-09 16:03:15 -0700596 final int count = list.size();
597 layout.removeAllViews();
598 for (int i = 0; i < count; ++i) {
Winson Chunge3193b92010-09-10 11:44:42 -0700599 AppWidgetProviderInfo info = (AppWidgetProviderInfo) list.get(i);
Michael Jurka3e7c7632010-10-02 16:01:03 -0700600 PendingAddWidgetInfo createItemInfo = new PendingAddWidgetInfo();
Winson Chungd0d43012010-09-26 17:26:45 -0700601 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
602 createItemInfo.componentName = info.provider;
Michael Jurka3e7c7632010-10-02 16:01:03 -0700603 createItemInfo.minWidth = info.minWidth;
604 createItemInfo.minHeight = info.minHeight;
Winson Chungd0d43012010-09-26 17:26:45 -0700605
Winson Chunge3193b92010-09-10 11:44:42 -0700606 LinearLayout l = (LinearLayout) mInflater.inflate(
607 R.layout.customize_paged_view_widget, layout, false);
Winson Chungd0d43012010-09-26 17:26:45 -0700608 l.setTag(createItemInfo);
609 l.setOnClickListener(this);
Winson Chunge3193b92010-09-10 11:44:42 -0700610 l.setOnLongClickListener(this);
Winson Chung80baf5a2010-08-09 16:03:15 -0700611
Winson Chunge3193b92010-09-10 11:44:42 -0700612 final Drawable icon = getWidgetIcon(info);
Michael Jurka9987a5c2010-10-08 16:58:12 -0700613
614 int[] spans = CellLayout.rectToCell(getResources(), info.minWidth, info.minHeight, null);
615 final int hSpan = spans[0];
616 final int vSpan = spans[1];
Winson Chunge3193b92010-09-10 11:44:42 -0700617
Winson Chungd0d43012010-09-26 17:26:45 -0700618 ImageView image = (ImageView) l.findViewById(R.id.widget_preview);
Winson Chunge3193b92010-09-10 11:44:42 -0700619 image.setMaxWidth(mMaxWidgetWidth);
620 image.setImageDrawable(icon);
Winson Chungd0d43012010-09-26 17:26:45 -0700621 TextView name = (TextView) l.findViewById(R.id.widget_name);
Winson Chunge3193b92010-09-10 11:44:42 -0700622 name.setText(info.label);
Winson Chungd0d43012010-09-26 17:26:45 -0700623 TextView dims = (TextView) l.findViewById(R.id.widget_dims);
Winson Chung3a476782010-09-15 15:21:55 -0700624 dims.setText(mContext.getString(R.string.widget_dims_format, hSpan, vSpan));
Winson Chunge3193b92010-09-10 11:44:42 -0700625
626 layout.addView(l);
Winson Chung80baf5a2010-08-09 16:03:15 -0700627 }
628 }
629
630 private void syncListPages(List<ResolveInfo> list) {
Winson Chunge3193b92010-09-10 11:44:42 -0700631 // we need to repopulate with PagedViewCellLayouts
632 removeAllViews();
633
Winson Chung80baf5a2010-08-09 16:03:15 -0700634 // ensure that we have the right number of pages
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700635 int numPages = (int) Math.ceil((float) list.size() / (mCellCountX * mCellCountY));
Winson Chunge3193b92010-09-10 11:44:42 -0700636 for (int i = 0; i < numPages; ++i) {
Winson Chung80baf5a2010-08-09 16:03:15 -0700637 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
638 setupPage(layout);
639 addView(layout);
640 }
641 }
642
643 private void syncListPageItems(int page, List<ResolveInfo> list) {
644 // ensure that we have the right number of items on the pages
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700645 int numCells = mCellCountX * mCellCountY;
Winson Chung80baf5a2010-08-09 16:03:15 -0700646 int startIndex = page * numCells;
647 int endIndex = Math.min(startIndex + numCells, list.size());
648 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
649 // TODO: we can optimize by just re-applying to existing views
650 layout.removeAllViews();
651 for (int i = startIndex; i < endIndex; ++i) {
652 ResolveInfo info = list.get(i);
Winson Chungd0d43012010-09-26 17:26:45 -0700653 PendingAddItemInfo createItemInfo = new PendingAddItemInfo();
654
Winson Chung241c3b42010-08-25 16:53:03 -0700655 PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
656 R.layout.customize_paged_view_item, layout, false);
657 icon.applyFromResolveInfo(info, mPackageManager, mPageViewIconCache);
Winson Chungd0d43012010-09-26 17:26:45 -0700658 switch (mCustomizationType) {
659 case WallpaperCustomization:
Winson Chunge8878e32010-09-15 20:37:09 -0700660 icon.setOnClickListener(this);
Winson Chungd0d43012010-09-26 17:26:45 -0700661 break;
662 case FolderCustomization:
663 if (info.labelRes != R.string.group_folder) {
664 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER;
665 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
666 info.activityInfo.name);
667 icon.setTag(createItemInfo);
668 } else {
669 UserFolderInfo folderInfo = new UserFolderInfo();
670 folderInfo.title = getResources().getText(R.string.folder_name);
671 icon.setTag(folderInfo);
672 }
673 icon.setOnClickListener(this);
Winson Chunge8878e32010-09-15 20:37:09 -0700674 icon.setOnLongClickListener(this);
Winson Chungd0d43012010-09-26 17:26:45 -0700675 break;
676 case ShortcutCustomization:
677 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
678 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
679 info.activityInfo.name);
680 icon.setTag(createItemInfo);
681 icon.setOnClickListener(this);
682 icon.setOnLongClickListener(this);
683 break;
684 default:
685 break;
Winson Chunge8878e32010-09-15 20:37:09 -0700686 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700687
688 final int index = i - startIndex;
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700689 final int x = index % mCellCountX;
690 final int y = index / mCellCountX;
691 setupPage(layout);
692 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
693 }
694 }
695
696 private void syncAppPages() {
697 if (mApps == null) return;
698
699 // We need to repopulate with PagedViewCellLayouts
700 removeAllViews();
701
702 // Ensure that we have the right number of pages
703 int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
704 for (int i = 0; i < numPages; ++i) {
705 PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
706 setupPage(layout);
707 addView(layout);
708 }
709 }
710
711 private void syncAppPageItems(int page) {
712 if (mApps == null) return;
713
714 // ensure that we have the right number of items on the pages
715 int numCells = mCellCountX * mCellCountY;
716 int startIndex = page * numCells;
717 int endIndex = Math.min(startIndex + numCells, mApps.size());
718 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
719 // TODO: we can optimize by just re-applying to existing views
720 layout.removeAllViews();
721 for (int i = startIndex; i < endIndex; ++i) {
722 final ApplicationInfo info = mApps.get(i);
723 PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
724 R.layout.all_apps_paged_view_application, layout, false);
Winson Chungdf4b83d2010-10-20 17:49:27 -0700725 icon.applyFromApplicationInfo(info, mPageViewIconCache, false);
Winson Chungd0d43012010-09-26 17:26:45 -0700726 icon.setOnClickListener(this);
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700727 icon.setOnLongClickListener(this);
728
729 final int index = i - startIndex;
730 final int x = index % mCellCountX;
731 final int y = index / mCellCountX;
Winson Chunge3193b92010-09-10 11:44:42 -0700732 setupPage(layout);
Winson Chung241c3b42010-08-25 16:53:03 -0700733 layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
Winson Chung80baf5a2010-08-09 16:03:15 -0700734 }
735 }
736
Winson Chung80baf5a2010-08-09 16:03:15 -0700737 @Override
738 public void syncPages() {
Winson Chunge3193b92010-09-10 11:44:42 -0700739 boolean centerPagedViewCellLayouts = false;
Winson Chung80baf5a2010-08-09 16:03:15 -0700740 switch (mCustomizationType) {
741 case WidgetCustomization:
742 syncWidgetPages();
743 break;
744 case FolderCustomization:
745 syncListPages(mFolderList);
Winson Chunge3193b92010-09-10 11:44:42 -0700746 centerPagedViewCellLayouts = true;
Winson Chung80baf5a2010-08-09 16:03:15 -0700747 break;
748 case ShortcutCustomization:
749 syncListPages(mShortcutList);
Winson Chunge3193b92010-09-10 11:44:42 -0700750 centerPagedViewCellLayouts = true;
Winson Chung80baf5a2010-08-09 16:03:15 -0700751 break;
752 case WallpaperCustomization:
Winson Chunge8878e32010-09-15 20:37:09 -0700753 syncListPages(mWallpaperList);
Winson Chunge3193b92010-09-10 11:44:42 -0700754 centerPagedViewCellLayouts = true;
Winson Chung80baf5a2010-08-09 16:03:15 -0700755 break;
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700756 case ApplicationCustomization:
757 syncAppPages();
758 centerPagedViewCellLayouts = false;
759 break;
Winson Chung80baf5a2010-08-09 16:03:15 -0700760 default:
761 removeAllViews();
Winson Chung86f77532010-08-24 11:08:22 -0700762 setCurrentPage(0);
Winson Chung80baf5a2010-08-09 16:03:15 -0700763 break;
764 }
765
766 // only try and center the page if there is one page
767 final int childCount = getChildCount();
Winson Chunge3193b92010-09-10 11:44:42 -0700768 if (centerPagedViewCellLayouts) {
769 if (childCount == 1) {
770 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(0);
771 layout.enableCenteredContent(true);
772 } else {
773 for (int i = 0; i < childCount; ++i) {
774 PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
775 layout.enableCenteredContent(false);
776 }
Winson Chung80baf5a2010-08-09 16:03:15 -0700777 }
778 }
779
780 // bound the current page
Winson Chung86f77532010-08-24 11:08:22 -0700781 setCurrentPage(Math.max(0, Math.min(childCount - 1, getCurrentPage())));
Winson Chung80baf5a2010-08-09 16:03:15 -0700782 }
783
784 @Override
785 public void syncPageItems(int page) {
786 switch (mCustomizationType) {
787 case WidgetCustomization:
788 syncWidgetPageItems(page);
789 break;
790 case FolderCustomization:
791 syncListPageItems(page, mFolderList);
792 break;
793 case ShortcutCustomization:
794 syncListPageItems(page, mShortcutList);
795 break;
796 case WallpaperCustomization:
Winson Chunge8878e32010-09-15 20:37:09 -0700797 syncListPageItems(page, mWallpaperList);
Winson Chung80baf5a2010-08-09 16:03:15 -0700798 break;
Winson Chung5ffd8ea2010-09-23 18:40:29 -0700799 case ApplicationCustomization:
800 syncAppPageItems(page);
801 break;
Winson Chung80baf5a2010-08-09 16:03:15 -0700802 }
803 }
Winson Chunge3193b92010-09-10 11:44:42 -0700804
Winson Chungd0d43012010-09-26 17:26:45 -0700805 @Override
Winson Chunge3193b92010-09-10 11:44:42 -0700806 protected int getAssociatedLowerPageBound(int page) {
807 return 0;
808 }
Winson Chungd0d43012010-09-26 17:26:45 -0700809 @Override
Winson Chunge3193b92010-09-10 11:44:42 -0700810 protected int getAssociatedUpperPageBound(int page) {
811 return getChildCount();
812 }
Winson Chungd0d43012010-09-26 17:26:45 -0700813
814 @Override
815 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
Michael Jurka3125d9d2010-09-27 11:30:20 -0700816 mode.setTitle(mChoiceModeTitleText);
Winson Chungd0d43012010-09-26 17:26:45 -0700817 return true;
818 }
819
820 @Override
821 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
822 return true;
823 }
824
825 @Override
826 public void onDestroyActionMode(ActionMode mode) {
827 endChoiceMode();
828 }
829
830 @Override
831 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
832 return false;
833 }
834
Winson Chung80baf5a2010-08-09 16:03:15 -0700835}