LauncherActivity.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
1/* 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; 21import android.content.pm.PackageManager; 22import android.content.pm.ResolveInfo; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.Canvas; 26import android.graphics.Paint; 27import android.graphics.PaintFlagsDrawFilter; 28import android.graphics.PixelFormat; 29import android.graphics.Rect; 30import android.graphics.drawable.BitmapDrawable; 31import android.graphics.drawable.Drawable; 32import android.graphics.drawable.PaintDrawable; 33import android.os.Bundle; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.Window; 38import android.widget.BaseAdapter; 39import android.widget.Filter; 40import android.widget.Filterable; 41import android.widget.ListView; 42import android.widget.TextView; 43 44import java.util.ArrayList; 45import java.util.Collections; 46import java.util.List; 47 48 49/** 50 * Displays a list of all activities which can be performed 51 * for a given intent. Launches when clicked. 52 * 53 */ 54public abstract class LauncherActivity extends ListActivity { 55 56 Intent mIntent; 57 PackageManager mPackageManager; 58 59 /** 60 * An item in the list 61 */ 62 public static class ListItem { 63 public CharSequence label; 64 //public CharSequence description; 65 public Drawable icon; 66 public String packageName; 67 public String className; 68 69 ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { 70 label = resolveInfo.loadLabel(pm); 71 if (label == null && resolveInfo.activityInfo != null) { 72 label = resolveInfo.activityInfo.name; 73 } 74 75 /* 76 if (resolveInfo.activityInfo != null && 77 resolveInfo.activityInfo.applicationInfo != null) { 78 description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); 79 } 80 */ 81 82 icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); 83 packageName = resolveInfo.activityInfo.applicationInfo.packageName; 84 className = resolveInfo.activityInfo.name; 85 } 86 87 public ListItem() { 88 } 89 } 90 91 /** 92 * Adapter which shows the set of activities that can be performed for a given intent. 93 */ 94 private class ActivityAdapter extends BaseAdapter implements Filterable { 95 private final Object lock = new Object(); 96 private ArrayList<ListItem> mOriginalValues; 97 98 protected final LayoutInflater mInflater; 99 100 protected List<ListItem> mActivitiesList; 101 102 private Filter mFilter; 103 104 public ActivityAdapter() { 105 mInflater = (LayoutInflater) LauncherActivity.this.getSystemService( 106 Context.LAYOUT_INFLATER_SERVICE); 107 mActivitiesList = makeListItems(); 108 } 109 110 public Intent intentForPosition(int position) { 111 if (mActivitiesList == null) { 112 return null; 113 } 114 115 Intent intent = new Intent(mIntent); 116 ListItem item = mActivitiesList.get(position); 117 intent.setClassName(item.packageName, item.className); 118 return intent; 119 } 120 121 public int getCount() { 122 return mActivitiesList != null ? mActivitiesList.size() : 0; 123 } 124 125 public Object getItem(int position) { 126 return position; 127 } 128 129 public long getItemId(int position) { 130 return position; 131 } 132 133 public View getView(int position, View convertView, ViewGroup parent) { 134 View view; 135 if (convertView == null) { 136 view = mInflater.inflate( 137 com.android.internal.R.layout.activity_list_item_2, parent, false); 138 } else { 139 view = convertView; 140 } 141 bindView(view, mActivitiesList.get(position)); 142 return view; 143 } 144 145 private void bindView(View view, ListItem item) { 146 TextView text = (TextView) view; 147 text.setText(item.label); 148 text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); 149 } 150 151 public Filter getFilter() { 152 if (mFilter == null) { 153 mFilter = new ArrayFilter(); 154 } 155 return mFilter; 156 } 157 158 /** 159 * An array filters constrains the content of the array adapter with a prefix. Each 160 * item that does not start with the supplied prefix is removed from the list. 161 */ 162 private class ArrayFilter extends Filter { 163 @Override 164 protected FilterResults performFiltering(CharSequence prefix) { 165 FilterResults results = new FilterResults(); 166 167 if (mOriginalValues == null) { 168 synchronized (lock) { 169 mOriginalValues = new ArrayList<ListItem>(mActivitiesList); 170 } 171 } 172 173 if (prefix == null || prefix.length() == 0) { 174 synchronized (lock) { 175 ArrayList<ListItem> list = new ArrayList<ListItem>(mOriginalValues); 176 results.values = list; 177 results.count = list.size(); 178 } 179 } else { 180 final String prefixString = prefix.toString().toLowerCase(); 181 182 ArrayList<ListItem> values = mOriginalValues; 183 int count = values.size(); 184 185 ArrayList<ListItem> newValues = new ArrayList<ListItem>(count); 186 187 for (int i = 0; i < count; i++) { 188 ListItem item = values.get(i); 189 190 String[] words = item.label.toString().toLowerCase().split(" "); 191 int wordCount = words.length; 192 193 for (int k = 0; k < wordCount; k++) { 194 final String word = words[k]; 195 196 if (word.startsWith(prefixString)) { 197 newValues.add(item); 198 break; 199 } 200 } 201 } 202 203 results.values = newValues; 204 results.count = newValues.size(); 205 } 206 207 return results; 208 } 209 210 @Override 211 protected void publishResults(CharSequence constraint, FilterResults results) { 212 //noinspection unchecked 213 mActivitiesList = (List<ListItem>) results.values; 214 if (results.count > 0) { 215 notifyDataSetChanged(); 216 } else { 217 notifyDataSetInvalidated(); 218 } 219 } 220 } 221 } 222 223 /** 224 * Utility class to resize icons to match default icon size. 225 */ 226 public class IconResizer { 227 // Code is borrowed from com.android.launcher.Utilities. 228 private int mIconWidth = -1; 229 private int mIconHeight = -1; 230 231 private final Rect mOldBounds = new Rect(); 232 private Canvas mCanvas = new Canvas(); 233 234 public IconResizer() { 235 mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, 236 Paint.FILTER_BITMAP_FLAG)); 237 238 final Resources resources = LauncherActivity.this.getResources(); 239 mIconWidth = mIconHeight = (int) resources.getDimension( 240 android.R.dimen.app_icon_size); 241 } 242 243 /** 244 * Returns a Drawable representing the thumbnail of the specified Drawable. 245 * The size of the thumbnail is defined by the dimension 246 * android.R.dimen.launcher_application_icon_size. 247 * 248 * This method is not thread-safe and should be invoked on the UI thread only. 249 * 250 * @param icon The icon to get a thumbnail of. 251 * 252 * @return A thumbnail for the specified icon or the icon itself if the 253 * thumbnail could not be created. 254 */ 255 public Drawable createIconThumbnail(Drawable icon) { 256 int width = mIconWidth; 257 int height = mIconHeight; 258 259 final int iconWidth = icon.getIntrinsicWidth(); 260 final int iconHeight = icon.getIntrinsicHeight(); 261 262 if (icon instanceof PaintDrawable) { 263 PaintDrawable painter = (PaintDrawable) icon; 264 painter.setIntrinsicWidth(width); 265 painter.setIntrinsicHeight(height); 266 } 267 268 if (width > 0 && height > 0) { 269 if (width < iconWidth || height < iconHeight) { 270 final float ratio = (float) iconWidth / iconHeight; 271 272 if (iconWidth > iconHeight) { 273 height = (int) (width / ratio); 274 } else if (iconHeight > iconWidth) { 275 width = (int) (height * ratio); 276 } 277 278 final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? 279 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; 280 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); 281 final Canvas canvas = mCanvas; 282 canvas.setBitmap(thumb); 283 // Copy the old bounds to restore them later 284 // If we were to do oldBounds = icon.getBounds(), 285 // the call to setBounds() that follows would 286 // change the same instance and we would lose the 287 // old bounds 288 mOldBounds.set(icon.getBounds()); 289 final int x = (mIconWidth - width) / 2; 290 final int y = (mIconHeight - height) / 2; 291 icon.setBounds(x, y, x + width, y + height); 292 icon.draw(canvas); 293 icon.setBounds(mOldBounds); 294 icon = new BitmapDrawable(thumb); 295 } else if (iconWidth < width && iconHeight < height) { 296 final Bitmap.Config c = Bitmap.Config.ARGB_8888; 297 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); 298 final Canvas canvas = mCanvas; 299 canvas.setBitmap(thumb); 300 mOldBounds.set(icon.getBounds()); 301 final int x = (width - iconWidth) / 2; 302 final int y = (height - iconHeight) / 2; 303 icon.setBounds(x, y, x + iconWidth, y + iconHeight); 304 icon.draw(canvas); 305 icon.setBounds(mOldBounds); 306 icon = new BitmapDrawable(thumb); 307 } 308 } 309 310 return icon; 311 } 312 } 313 314 @Override 315 protected void onCreate(Bundle icicle) { 316 super.onCreate(icicle); 317 318 mPackageManager = getPackageManager(); 319 320 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 321 setProgressBarIndeterminateVisibility(true); 322 setContentView(com.android.internal.R.layout.activity_list); 323 324 325 mIntent = new Intent(getTargetIntent()); 326 mIntent.setComponent(null); 327 mAdapter = new ActivityAdapter(); 328 329 setListAdapter(mAdapter); 330 getListView().setTextFilterEnabled(true); 331 332 setProgressBarIndeterminateVisibility(false); 333 } 334 335 @Override 336 protected void onListItemClick(ListView l, View v, int position, long id) { 337 Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position); 338 339 startActivity(intent); 340 } 341 342 /** 343 * Return the actual Intent for a specific position in our 344 * {@link android.widget.ListView}. 345 * @param position The item whose Intent to return 346 */ 347 protected Intent intentForPosition(int position) { 348 ActivityAdapter adapter = (ActivityAdapter) mAdapter; 349 return adapter.intentForPosition(position); 350 } 351 352 /** 353 * Get the base intent to use when running 354 * {@link PackageManager#queryIntentActivities(Intent, int)}. 355 */ 356 protected Intent getTargetIntent() { 357 return new Intent(); 358 } 359 360 /** 361 * Perform the query to determine which results to show and return a list of them. 362 */ 363 public List<ListItem> makeListItems() { 364 // Load all matching activities and sort correctly 365 List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent, 366 /* no flags */ 0); 367 Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager)); 368 369 IconResizer resizer = new IconResizer(); 370 371 ArrayList<ListItem> result = new ArrayList<ListItem>(list.size()); 372 int listSize = list.size(); 373 for (int i = 0; i < listSize; i++) { 374 ResolveInfo resolveInfo = list.get(i); 375 result.add(new ListItem(mPackageManager, resolveInfo, resizer)); 376 } 377 378 return result; 379 } 380} 381