blob: 9ec7f4134a3041e5072d45ed2a70c2ebf0a00a3d [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();
Aaron Whyte1fb617f2014-05-12 22:08:53 -0700343
344 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
345 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
346 setProgressBarIndeterminateVisibility(true);
347 }
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700348 onSetContentView();
Amith Yamasani74bb3552010-11-03 11:26:02 -0700349
Dianne Hackborneb034652009-09-07 00:49:58 -0700350 mIconResizer = new IconResizer();
351
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 mIntent = new Intent(getTargetIntent());
353 mIntent.setComponent(null);
Dianne Hackborneb034652009-09-07 00:49:58 -0700354 mAdapter = new ActivityAdapter(mIconResizer);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 setListAdapter(mAdapter);
357 getListView().setTextFilterEnabled(true);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700358
359 updateAlertTitle();
360 updateButtonText();
361
Aaron Whyte1fb617f2014-05-12 22:08:53 -0700362 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
363 setProgressBarIndeterminateVisibility(false);
364 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 }
366
Amith Yamasani74bb3552010-11-03 11:26:02 -0700367 private void updateAlertTitle() {
368 TextView alertTitle = (TextView) findViewById(com.android.internal.R.id.alertTitle);
369 if (alertTitle != null) {
370 alertTitle.setText(getTitle());
371 }
372 }
373
374 private void updateButtonText() {
375 Button cancelButton = (Button) findViewById(com.android.internal.R.id.button1);
376 if (cancelButton != null) {
377 cancelButton.setOnClickListener(new OnClickListener() {
378 public void onClick(View v) {
379 finish();
380 }
381 });
382 }
383 }
384
385 @Override
386 public void setTitle(CharSequence title) {
387 super.setTitle(title);
388 updateAlertTitle();
389 }
390
391 @Override
392 public void setTitle(int titleId) {
393 super.setTitle(titleId);
394 updateAlertTitle();
395 }
396
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700397 /**
398 * Override to call setContentView() with your own content view to
399 * customize the list layout.
400 */
401 protected void onSetContentView() {
402 setContentView(com.android.internal.R.layout.activity_list);
403 }
404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 @Override
406 protected void onListItemClick(ListView l, View v, int position, long id) {
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700407 Intent intent = intentForPosition(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 startActivity(intent);
409 }
410
411 /**
412 * Return the actual Intent for a specific position in our
413 * {@link android.widget.ListView}.
414 * @param position The item whose Intent to return
415 */
416 protected Intent intentForPosition(int position) {
417 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
418 return adapter.intentForPosition(position);
419 }
420
421 /**
Dianne Hackborn2bacbdf2009-06-24 19:03:15 -0700422 * Return the {@link ListItem} for a specific position in our
423 * {@link android.widget.ListView}.
424 * @param position The item to return
425 */
426 protected ListItem itemForPosition(int position) {
427 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
428 return adapter.itemForPosition(position);
429 }
430
431 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 * Get the base intent to use when running
433 * {@link PackageManager#queryIntentActivities(Intent, int)}.
434 */
435 protected Intent getTargetIntent() {
436 return new Intent();
437 }
438
439 /**
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700440 * Perform query on package manager for list items. The default
441 * implementation queries for activities.
442 */
443 protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
444 return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
445 }
Dianne Hackborn8d866e52012-10-10 18:39:45 -0700446
447 /**
448 * @hide
449 */
450 protected void onSortResultList(List<ResolveInfo> results) {
451 Collections.sort(results, new ResolveInfo.DisplayNameComparator(mPackageManager));
452 }
453
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700454 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 * Perform the query to determine which results to show and return a list of them.
456 */
457 public List<ListItem> makeListItems() {
458 // Load all matching activities and sort correctly
Dianne Hackborn3be63c02009-08-20 19:31:38 -0700459 List<ResolveInfo> list = onQueryPackageManager(mIntent);
Dianne Hackborn8d866e52012-10-10 18:39:45 -0700460 onSortResultList(list);
Amith Yamasani74bb3552010-11-03 11:26:02 -0700461
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
463 int listSize = list.size();
464 for (int i = 0; i < listSize; i++) {
465 ResolveInfo resolveInfo = list.get(i);
Dianne Hackborneb034652009-09-07 00:49:58 -0700466 result.add(new ListItem(mPackageManager, resolveInfo, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 }
468
469 return result;
470 }
Amith Yamasani74bb3552010-11-03 11:26:02 -0700471
472 /**
473 * Whether or not to show icons in the list
474 * @hide keeping this private for now, since only Settings needs it
475 * @return true to show icons beside the activity names, false otherwise
476 */
477 protected boolean onEvaluateShowIcons() {
478 return true;
479 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480}