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