2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.ComponentInfo;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.Paint;
28 import android.graphics.PaintFlagsDrawFilter;
29 import android.graphics.PixelFormat;
30 import android.graphics.Rect;
31 import android.graphics.drawable.BitmapDrawable;
32 import android.graphics.drawable.Drawable;
33 import android.graphics.drawable.PaintDrawable;
34 import android.os.Bundle;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.view.Window;
39 import android.view.View.OnClickListener;
40 import android.widget.BaseAdapter;
41 import android.widget.Button;
42 import android.widget.Filter;
43 import android.widget.Filterable;
44 import android.widget.ListView;
45 import android.widget.TextView;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.List;
53 * Displays a list of all activities which can be performed
54 * for a given intent. Launches when clicked.
57 public abstract class LauncherActivity extends ListActivity {
59 PackageManager mPackageManager;
60 IconResizer mIconResizer;
65 public static class ListItem {
66 public ResolveInfo resolveInfo;
67 public CharSequence label;
69 public String packageName;
70 public String className;
73 ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
74 this.resolveInfo = resolveInfo;
75 label = resolveInfo.loadLabel(pm);
76 ComponentInfo ci = resolveInfo.activityInfo;
77 if (ci == null) ci = resolveInfo.serviceInfo;
78 if (label == null && ci != null) {
79 label = resolveInfo.activityInfo.name;
82 if (resizer != null) {
83 icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
85 packageName = ci.applicationInfo.packageName;
94 * Adapter which shows the set of activities that can be performed for a given intent.
96 private class ActivityAdapter extends BaseAdapter implements Filterable {
97 private final Object lock = new Object();
98 private ArrayList<ListItem> mOriginalValues;
100 protected final IconResizer mIconResizer;
101 protected final LayoutInflater mInflater;
103 protected List<ListItem> mActivitiesList;
105 private Filter mFilter;
106 private final boolean mShowIcons;
108 public ActivityAdapter(IconResizer resizer) {
109 mIconResizer = resizer;
110 mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
111 Context.LAYOUT_INFLATER_SERVICE);
112 mShowIcons = onEvaluateShowIcons();
113 mActivitiesList = makeListItems();
116 public Intent intentForPosition(int position) {
117 if (mActivitiesList == null) {
121 Intent intent = new Intent(mIntent);
122 ListItem item = mActivitiesList.get(position);
123 intent.setClassName(item.packageName, item.className);
124 if (item.extras != null) {
125 intent.putExtras(item.extras);
130 public ListItem itemForPosition(int position) {
131 if (mActivitiesList == null) {
135 return mActivitiesList.get(position);
138 public int getCount() {
139 return mActivitiesList != null ? mActivitiesList.size() : 0;
142 public Object getItem(int position) {
146 public long getItemId(int position) {
150 public View getView(int position, View convertView, ViewGroup parent) {
152 if (convertView == null) {
153 view = mInflater.inflate(
154 com.android.internal.R.layout.activity_list_item_2, parent, false);
158 bindView(view, mActivitiesList.get(position));
162 private void bindView(View view, ListItem item) {
163 TextView text = (TextView) view;
164 text.setText(item.label);
166 if (item.icon == null) {
167 item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
169 text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
173 public Filter getFilter() {
174 if (mFilter == null) {
175 mFilter = new ArrayFilter();
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.
184 private class ArrayFilter extends Filter {
186 protected FilterResults performFiltering(CharSequence prefix) {
187 FilterResults results = new FilterResults();
189 if (mOriginalValues == null) {
190 synchronized (lock) {
191 mOriginalValues = new ArrayList<ListItem>(mActivitiesList);
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();
202 final String prefixString = prefix.toString().toLowerCase();
204 ArrayList<ListItem> values = mOriginalValues;
205 int count = values.size();
207 ArrayList<ListItem> newValues = new ArrayList<ListItem>(count);
209 for (int i = 0; i < count; i++) {
210 ListItem item = values.get(i);
212 String[] words = item.label.toString().toLowerCase().split(" ");
213 int wordCount = words.length;
215 for (int k = 0; k < wordCount; k++) {
216 final String word = words[k];
218 if (word.startsWith(prefixString)) {
225 results.values = newValues;
226 results.count = newValues.size();
233 protected void publishResults(CharSequence constraint, FilterResults results) {
234 //noinspection unchecked
235 mActivitiesList = (List<ListItem>) results.values;
236 if (results.count > 0) {
237 notifyDataSetChanged();
239 notifyDataSetInvalidated();
246 * Utility class to resize icons to match default icon size.
248 public class IconResizer {
249 // Code is borrowed from com.android.launcher.Utilities.
250 private int mIconWidth = -1;
251 private int mIconHeight = -1;
253 private final Rect mOldBounds = new Rect();
254 private Canvas mCanvas = new Canvas();
256 public IconResizer() {
257 mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
258 Paint.FILTER_BITMAP_FLAG));
260 final Resources resources = LauncherActivity.this.getResources();
261 mIconWidth = mIconHeight = (int) resources.getDimension(
262 android.R.dimen.app_icon_size);
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.
270 * This method is not thread-safe and should be invoked on the UI thread only.
272 * @param icon The icon to get a thumbnail of.
274 * @return A thumbnail for the specified icon or the icon itself if the
275 * thumbnail could not be created.
277 public Drawable createIconThumbnail(Drawable icon) {
278 int width = mIconWidth;
279 int height = mIconHeight;
281 final int iconWidth = icon.getIntrinsicWidth();
282 final int iconHeight = icon.getIntrinsicHeight();
284 if (icon instanceof PaintDrawable) {
285 PaintDrawable painter = (PaintDrawable) icon;
286 painter.setIntrinsicWidth(width);
287 painter.setIntrinsicHeight(height);
290 if (width > 0 && height > 0) {
291 if (width < iconWidth || height < iconHeight) {
292 final float ratio = (float) iconWidth / iconHeight;
294 if (iconWidth > iconHeight) {
295 height = (int) (width / ratio);
296 } else if (iconHeight > iconWidth) {
297 width = (int) (height * ratio);
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
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);
315 icon.setBounds(mOldBounds);
316 icon = new BitmapDrawable(getResources(), thumb);
317 canvas.setBitmap(null);
318 } 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);
328 icon.setBounds(mOldBounds);
329 icon = new BitmapDrawable(getResources(), thumb);
330 canvas.setBitmap(null);
339 protected void onCreate(Bundle icicle) {
340 super.onCreate(icicle);
342 mPackageManager = getPackageManager();
344 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
345 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
346 setProgressBarIndeterminateVisibility(true);
350 mIconResizer = new IconResizer();
352 mIntent = new Intent(getTargetIntent());
353 mIntent.setComponent(null);
354 mAdapter = new ActivityAdapter(mIconResizer);
356 setListAdapter(mAdapter);
357 getListView().setTextFilterEnabled(true);
362 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
363 setProgressBarIndeterminateVisibility(false);
367 private void updateAlertTitle() {
368 TextView alertTitle = (TextView) findViewById(com.android.internal.R.id.alertTitle);
369 if (alertTitle != null) {
370 alertTitle.setText(getTitle());
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) {
386 public void setTitle(CharSequence title) {
387 super.setTitle(title);
392 public void setTitle(int titleId) {
393 super.setTitle(titleId);
398 * Override to call setContentView() with your own content view to
399 * customize the list layout.
401 protected void onSetContentView() {
402 setContentView(com.android.internal.R.layout.activity_list);
406 protected void onListItemClick(ListView l, View v, int position, long id) {
407 Intent intent = intentForPosition(position);
408 startActivity(intent);
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
416 protected Intent intentForPosition(int position) {
417 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
418 return adapter.intentForPosition(position);
422 * Return the {@link ListItem} for a specific position in our
423 * {@link android.widget.ListView}.
424 * @param position The item to return
426 protected ListItem itemForPosition(int position) {
427 ActivityAdapter adapter = (ActivityAdapter) mAdapter;
428 return adapter.itemForPosition(position);
432 * Get the base intent to use when running
433 * {@link PackageManager#queryIntentActivities(Intent, int)}.
435 protected Intent getTargetIntent() {
440 * Perform query on package manager for list items. The default
441 * implementation queries for activities.
443 protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
444 return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
450 protected void onSortResultList(List<ResolveInfo> results) {
451 Collections.sort(results, new ResolveInfo.DisplayNameComparator(mPackageManager));
455 * Perform the query to determine which results to show and return a list of them.
457 public List<ListItem> makeListItems() {
458 // Load all matching activities and sort correctly
459 List<ResolveInfo> list = onQueryPackageManager(mIntent);
460 onSortResultList(list);
462 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);
466 result.add(new ListItem(mPackageManager, resolveInfo, null));
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
477 protected boolean onEvaluateShowIcons() {