blob: 96c7246acf3cd6123ea481ff3b04990059e96d5b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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 android.app;
18
19import android.content.Context;
20import android.content.Intent;
Dianne Hackborn3be63c02009-08-20 19:31:38 -070021import android.content.pm.ComponentInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.pm.PackageManager;
23import android.content.pm.ResolveInfo;
24import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Paint;
28import android.graphics.PaintFlagsDrawFilter;
29import android.graphics.PixelFormat;
30import android.graphics.Rect;
31import android.graphics.drawable.BitmapDrawable;
32import android.graphics.drawable.Drawable;
33import android.graphics.drawable.PaintDrawable;
34import android.os.Bundle;
35import android.view.LayoutInflater;
36import android.view.View;
37import android.view.ViewGroup;
38import android.view.Window;
Amith Yamasani74bb3552010-11-03 11:26:02 -070039import android.view.View.OnClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.widget.BaseAdapter;
Amith Yamasani74bb3552010-11-03 11:26:02 -070041import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.widget.Filter;
43import android.widget.Filterable;
44import android.widget.ListView;
45import android.widget.TextView;
46
47import java.util.ArrayList;
48import java.util.Collections;
49import java.util.List;
50
51
52/**
53 * Displays a list of all activities which can be performed
54 * for a given intent. Launches when clicked.
55 *
56 */
57public abstract class LauncherActivity extends ListActivity {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 Intent mIntent;
59 PackageManager mPackageManager;
Dianne Hackborneb034652009-09-07 00:49:58 -070060 IconResizer mIconResizer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62 /**
63 * An item in the list
64 */
65 public static class ListItem {
Dianne Hackborn2bacbdf2009-06-24 19:03:15 -070066 public ResolveInfo resolveInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 public CharSequence label;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 public Drawable icon;
69 public String packageName;
70 public String className;
The Android Open Source Project10592532009-03-18 17:39:46 -070071 public Bundle extras;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
Dianne Hackborn2bacbdf2009-06-24 19:03:15 -070074 this.resolveInfo = resolveInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 label = resolveInfo.loadLabel(pm);
Dianne Hackborn3be63c02009-08-20 19:31:38 -070076 ComponentInfo ci = resolveInfo.activityInfo;
77 if (ci == null) ci = resolveInfo.serviceInfo;
78 if (label == null && ci != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 label = resolveInfo.activityInfo.name;
80 }
81
Dianne Hackborneb034652009-09-07 00:49:58 -070082 if (resizer != null) {
83 icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
84 }
Dianne Hackborn3be63c02009-08-20 19:31:38 -070085 packageName = ci.applicationInfo.packageName;
86 className = ci.name;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 }
88
89 public ListItem() {
90 }
91 }
92
93 /**
94 * Adapter which shows the set of activities that can be performed for a given intent.
95 */
96 private class ActivityAdapter extends BaseAdapter implements Filterable {
97 private final Object lock = new Object();
98 private ArrayList<ListItem> mOriginalValues;
99
Dianne Hackborneb034652009-09-07 00:49:58 -0700100 protected final IconResizer mIconResizer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 protected final LayoutInflater mInflater;
102
103 protected List<ListItem> mActivitiesList;
104
105 private Filter mFilter;
Amith Yamasani74bb3552010-11-03 11:26:02 -0700106 private final boolean mShowIcons;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Dianne Hackborneb034652009-09-07 00:49:58 -0700108 public ActivityAdapter(IconResizer resizer) {
109 mIconResizer = resizer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
111 Context.LAYOUT_INFLATER_SERVICE);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700112 mShowIcons = onEvaluateShowIcons();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 mActivitiesList = makeListItems();
114 }
115
116 public Intent intentForPosition(int position) {
117 if (mActivitiesList == null) {
118 return null;
119 }
120
121 Intent intent = new Intent(mIntent);
122 ListItem item = mActivitiesList.get(position);
123 intent.setClassName(item.packageName, item.className);
The Android Open Source Project10592532009-03-18 17:39:46 -0700124 if (item.extras != null) {
125 intent.putExtras(item.extras);
126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 return intent;
128 }
129
Dianne Hackborn2bacbdf2009-06-24 19:03:15 -0700130 public ListItem itemForPosition(int position) {
131 if (mActivitiesList == null) {
132 return null;
133 }
134
135 return mActivitiesList.get(position);
136 }
137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 public int getCount() {
139 return mActivitiesList != null ? mActivitiesList.size() : 0;
140 }
141
142 public Object getItem(int position) {
143 return position;
144 }
145
146 public long getItemId(int position) {
147 return position;
148 }
149
150 public View getView(int position, View convertView, ViewGroup parent) {
151 View view;
152 if (convertView == null) {
153 view = mInflater.inflate(
154 com.android.internal.R.layout.activity_list_item_2, parent, false);
155 } else {
156 view = convertView;
157 }
158 bindView(view, mActivitiesList.get(position));
159 return view;
160 }
161
162 private void bindView(View view, ListItem item) {
163 TextView text = (TextView) view;
164 text.setText(item.label);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700165 if (mShowIcons) {
166 if (item.icon == null) {
167 item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
168 }
169 text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
Dianne Hackborneb034652009-09-07 00:49:58 -0700170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
Amith Yamasani74bb3552010-11-03 11:26:02 -0700172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 public Filter getFilter() {
174 if (mFilter == null) {
175 mFilter = new ArrayFilter();
176 }
177 return mFilter;
178 }
179
180 /**
181 * An array filters constrains the content of the array adapter with a prefix. Each
182 * item that does not start with the supplied prefix is removed from the list.
183 */
184 private class ArrayFilter extends Filter {
185 @Override
186 protected FilterResults performFiltering(CharSequence prefix) {
187 FilterResults results = new FilterResults();
188
189 if (mOriginalValues == null) {
190 synchronized (lock) {
191 mOriginalValues = new ArrayList<ListItem>(mActivitiesList);
192 }
193 }
194
195 if (prefix == null || prefix.length() == 0) {
196 synchronized (lock) {
197 ArrayList<ListItem> list = new ArrayList<ListItem>(mOriginalValues);
198 results.values = list;
199 results.count = list.size();
200 }
201 } else {
202 final String prefixString = prefix.toString().toLowerCase();
203
204 ArrayList<ListItem> values = mOriginalValues;
205 int count = values.size();
206
207 ArrayList<ListItem> newValues = new ArrayList<ListItem>(count);
208
209 for (int i = 0; i < count; i++) {
210 ListItem item = values.get(i);
211
212 String[] words = item.label.toString().toLowerCase().split(" ");
213 int wordCount = words.length;
214
215 for (int k = 0; k < wordCount; k++) {
216 final String word = words[k];
217
218 if (word.startsWith(prefixString)) {
219 newValues.add(item);
220 break;
221 }
222 }
223 }
224
225 results.values = newValues;
226 results.count = newValues.size();
227 }
228
229 return results;
230 }
231
232 @Override
233 protected void publishResults(CharSequence constraint, FilterResults results) {
234 //noinspection unchecked
235 mActivitiesList = (List<ListItem>) results.values;
236 if (results.count > 0) {
237 notifyDataSetChanged();
238 } else {
239 notifyDataSetInvalidated();
240 }
241 }
242 }
243 }
244
245 /**
246 * Utility class to resize icons to match default icon size.
247 */
248 public class IconResizer {
249 // Code is borrowed from com.android.launcher.Utilities.
250 private int mIconWidth = -1;
251 private int mIconHeight = -1;
252
253 private final Rect mOldBounds = new Rect();
254 private Canvas mCanvas = new Canvas();
255
256 public IconResizer() {
257 mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
258 Paint.FILTER_BITMAP_FLAG));
259
260 final Resources resources = LauncherActivity.this.getResources();
261 mIconWidth = mIconHeight = (int) resources.getDimension(
262 android.R.dimen.app_icon_size);
263 }
264
265 /**
266 * Returns a Drawable representing the thumbnail of the specified Drawable.
267 * The size of the thumbnail is defined by the dimension
268 * android.R.dimen.launcher_application_icon_size.
269 *
270 * This method is not thread-safe and should be invoked on the UI thread only.
271 *
272 * @param icon The icon to get a thumbnail of.
273 *
274 * @return A thumbnail for the specified icon or the icon itself if the
275 * thumbnail could not be created.
276 */
277 public Drawable createIconThumbnail(Drawable icon) {
278 int width = mIconWidth;
279 int height = mIconHeight;
280
281 final int iconWidth = icon.getIntrinsicWidth();
282 final int iconHeight = icon.getIntrinsicHeight();
283
284 if (icon instanceof PaintDrawable) {
285 PaintDrawable painter = (PaintDrawable) icon;
286 painter.setIntrinsicWidth(width);
287 painter.setIntrinsicHeight(height);
288 }
289
290 if (width > 0 && height > 0) {
291 if (width < iconWidth || height < iconHeight) {
292 final float ratio = (float) iconWidth / iconHeight;
293
294 if (iconWidth > iconHeight) {
295 height = (int) (width / ratio);
296 } else if (iconHeight > iconWidth) {
297 width = (int) (height * ratio);
298 }
299
300 final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
301 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
302 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
303 final Canvas canvas = mCanvas;
304 canvas.setBitmap(thumb);
305 // Copy the old bounds to restore them later
306 // If we were to do oldBounds = icon.getBounds(),
307 // the call to setBounds() that follows would
308 // change the same instance and we would lose the
309 // old bounds
310 mOldBounds.set(icon.getBounds());
311 final int x = (mIconWidth - width) / 2;
312 final int y = (mIconHeight - height) / 2;
313 icon.setBounds(x, y, x + width, y + height);
314 icon.draw(canvas);
315 icon.setBounds(mOldBounds);
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700316 icon = new BitmapDrawable(getResources(), thumb);
Dianne Hackborn6311d0a2011-08-02 16:37:58 -0700317 canvas.setBitmap(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 } else if (iconWidth < width && iconHeight < height) {
319 final Bitmap.Config c = Bitmap.Config.ARGB_8888;
320 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
321 final Canvas canvas = mCanvas;
322 canvas.setBitmap(thumb);
323 mOldBounds.set(icon.getBounds());
324 final int x = (width - iconWidth) / 2;
325 final int y = (height - iconHeight) / 2;
326 icon.setBounds(x, y, x + iconWidth, y + iconHeight);
327 icon.draw(canvas);
328 icon.setBounds(mOldBounds);
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700329 icon = new BitmapDrawable(getResources(), thumb);
Dianne Hackborn6311d0a2011-08-02 16:37:58 -0700330 canvas.setBitmap(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332 }
333
334 return icon;
335 }
336 }
337
338 @Override
339 protected void onCreate(Bundle icicle) {
340 super.onCreate(icicle);
341
342 mPackageManager = getPackageManager();
343
344 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
345 setProgressBarIndeterminateVisibility(true);
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700346 onSetContentView();
Amith Yamasani74bb3552010-11-03 11:26:02 -0700347
Dianne Hackborneb034652009-09-07 00:49:58 -0700348 mIconResizer = new IconResizer();
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 mIntent = new Intent(getTargetIntent());
351 mIntent.setComponent(null);
Dianne Hackborneb034652009-09-07 00:49:58 -0700352 mAdapter = new ActivityAdapter(mIconResizer);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 setListAdapter(mAdapter);
355 getListView().setTextFilterEnabled(true);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700356
357 updateAlertTitle();
358 updateButtonText();
359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 setProgressBarIndeterminateVisibility(false);
361 }
362
Amith Yamasani74bb3552010-11-03 11:26:02 -0700363 private void updateAlertTitle() {
364 TextView alertTitle = (TextView) findViewById(com.android.internal.R.id.alertTitle);
365 if (alertTitle != null) {
366 alertTitle.setText(getTitle());
367 }
368 }
369
370 private void updateButtonText() {
371 Button cancelButton = (Button) findViewById(com.android.internal.R.id.button1);
372 if (cancelButton != null) {
373 cancelButton.setOnClickListener(new OnClickListener() {
374 public void onClick(View v) {
375 finish();
376 }
377 });
378 }
379 }
380
381 @Override
382 public void setTitle(CharSequence title) {
383 super.setTitle(title);
384 updateAlertTitle();
385 }
386
387 @Override
388 public void setTitle(int titleId) {
389 super.setTitle(titleId);
390 updateAlertTitle();
391 }
392
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700393 /**
394 * Override to call setContentView() with your own content view to
395 * customize the list layout.
396 */
397 protected void onSetContentView() {
398 setContentView(com.android.internal.R.layout.activity_list);
399 }
400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 @Override
402 protected void onListItemClick(ListView l, View v, int position, long id) {
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700403 Intent intent = intentForPosition(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 startActivity(intent);
405 }
406
407 /**
408 * Return the actual Intent for a specific position in our
409 * {@link android.widget.ListView}.
410 * @param position The item whose Intent to return
411 */
412 protected Intent intentForPosition(int position) {
413 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
414 return adapter.intentForPosition(position);
415 }
416
417 /**
Dianne Hackborn2bacbdf2009-06-24 19:03:15 -0700418 * Return the {@link ListItem} for a specific position in our
419 * {@link android.widget.ListView}.
420 * @param position The item to return
421 */
422 protected ListItem itemForPosition(int position) {
423 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
424 return adapter.itemForPosition(position);
425 }
426
427 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 * Get the base intent to use when running
429 * {@link PackageManager#queryIntentActivities(Intent, int)}.
430 */
431 protected Intent getTargetIntent() {
432 return new Intent();
433 }
434
435 /**
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700436 * Perform query on package manager for list items. The default
437 * implementation queries for activities.
438 */
439 protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
440 return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
441 }
Dianne Hackborn8d866e52012-10-10 18:39:45 -0700442
443 /**
444 * @hide
445 */
446 protected void onSortResultList(List<ResolveInfo> results) {
447 Collections.sort(results, new ResolveInfo.DisplayNameComparator(mPackageManager));
448 }
449
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700450 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 * Perform the query to determine which results to show and return a list of them.
452 */
453 public List<ListItem> makeListItems() {
454 // Load all matching activities and sort correctly
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700455 List<ResolveInfo> list = onQueryPackageManager(mIntent);
Dianne Hackborn8d866e52012-10-10 18:39:45 -0700456 onSortResultList(list);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
459 int listSize = list.size();
460 for (int i = 0; i < listSize; i++) {
461 ResolveInfo resolveInfo = list.get(i);
Dianne Hackborneb034652009-09-07 00:49:58 -0700462 result.add(new ListItem(mPackageManager, resolveInfo, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 }
464
465 return result;
466 }
Amith Yamasani74bb3552010-11-03 11:26:02 -0700467
468 /**
469 * Whether or not to show icons in the list
470 * @hide keeping this private for now, since only Settings needs it
471 * @return true to show icons beside the activity names, false otherwise
472 */
473 protected boolean onEvaluateShowIcons() {
474 return true;
475 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476}