Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2008 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 com.android.internal.app;
     18 
     19 import com.android.internal.R;
     20 import com.android.internal.content.PackageMonitor;
     21 
     22 import android.app.ActivityManager;
     23 import android.app.ActivityManagerNative;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.pm.LabeledIntent;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.PackageManager.NameNotFoundException;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.res.Resources;
     34 import android.graphics.drawable.Drawable;
     35 import android.net.Uri;
     36 import android.os.Bundle;
     37 import android.os.PatternMatcher;
     38 import android.os.RemoteException;
     39 import android.os.UserHandle;
     40 import android.util.Log;
     41 import android.view.LayoutInflater;
     42 import android.view.View;
     43 import android.view.ViewGroup;
     44 import android.widget.AdapterView;
     45 import android.widget.BaseAdapter;
     46 import android.widget.Button;
     47 import android.widget.GridView;
     48 import android.widget.ImageView;
     49 import android.widget.ListView;
     50 import android.widget.TextView;
     51 
     52 import java.util.ArrayList;
     53 import java.util.Collections;
     54 import java.util.HashSet;
     55 import java.util.Iterator;
     56 import java.util.List;
     57 import java.util.Set;
     58 
     59 /**
     60  * This activity is displayed when the system attempts to start an Intent for
     61  * which there is more than one matching activity, allowing the user to decide
     62  * which to go to.  It is not normally used directly by application developers.
     63  */
     64 public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {
     65     private static final String TAG = "ResolverActivity";
     66     private static final boolean DEBUG = false;
     67 
     68     private int mLaunchedFromUid;
     69     private ResolveListAdapter mAdapter;
     70     private PackageManager mPm;
     71     private boolean mAlwaysUseOption;
     72     private boolean mShowExtended;
     73     private GridView mGrid;
     74     private Button mAlwaysButton;
     75     private Button mOnceButton;
     76     private int mIconDpi;
     77     private int mIconSize;
     78     private int mMaxColumns;
     79     private int mLastSelected = GridView.INVALID_POSITION;
     80 
     81     private boolean mRegistered;
     82     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
     83         @Override public void onSomePackagesChanged() {
     84             mAdapter.handlePackagesChanged();
     85         }
     86     };
     87 
     88     private Intent makeMyIntent() {
     89         Intent intent = new Intent(getIntent());
     90         // The resolver activity is set to be hidden from recent tasks.
     91         // we don't want this attribute to be propagated to the next activity
     92         // being launched.  Note that if the original Intent also had this
     93         // flag set, we are now losing it.  That should be a very rare case
     94         // and we can live with this.
     95         intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
     96         return intent;
     97     }
     98 
     99     @Override
    100     protected void onCreate(Bundle savedInstanceState) {
    101         onCreate(savedInstanceState, makeMyIntent(),
    102                 getResources().getText(com.android.internal.R.string.whichApplication),
    103                 null, null, true);
    104     }
    105 
    106     protected void onCreate(Bundle savedInstanceState, Intent intent,
    107             CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
    108             boolean alwaysUseOption) {
    109         setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
    110         super.onCreate(savedInstanceState);
    111         try {
    112             mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
    113                     getActivityToken());
    114         } catch (RemoteException e) {
    115             mLaunchedFromUid = -1;
    116         }
    117         mPm = getPackageManager();
    118         mAlwaysUseOption = alwaysUseOption;
    119         mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
    120         intent.setComponent(null);
    121 
    122         AlertController.AlertParams ap = mAlertParams;
    123 
    124         ap.mTitle = title;
    125 
    126         mPackageMonitor.register(this, getMainLooper(), false);
    127         mRegistered = true;
    128 
    129         final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    130         mIconDpi = am.getLauncherLargeIconDensity();
    131         mIconSize = am.getLauncherLargeIconSize();
    132 
    133         mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
    134                 mLaunchedFromUid);
    135         int count = mAdapter.getCount();
    136         if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
    137             // Gulp!
    138             finish();
    139             return;
    140         } else if (count > 1) {
    141             ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
    142             mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
    143             mGrid.setAdapter(mAdapter);
    144             mGrid.setOnItemClickListener(this);
    145             mGrid.setOnItemLongClickListener(new ItemLongClickListener());
    146 
    147             if (alwaysUseOption) {
    148                 mGrid.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    149             }
    150 
    151             resizeGrid();
    152         } else if (count == 1) {
    153             startActivity(mAdapter.intentForPosition(0));
    154             mPackageMonitor.unregister();
    155             mRegistered = false;
    156             finish();
    157             return;
    158         } else {
    159             ap.mMessage = getResources().getText(R.string.noApplications);
    160         }
    161 
    162         setupAlert();
    163 
    164         if (alwaysUseOption) {
    165             final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
    166             if (buttonLayout != null) {
    167                 buttonLayout.setVisibility(View.VISIBLE);
    168                 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
    169                 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
    170             } else {
    171                 mAlwaysUseOption = false;
    172             }
    173         }
    174     }
    175 
    176     void resizeGrid() {
    177         final int itemCount = mAdapter.getCount();
    178         mGrid.setNumColumns(Math.min(itemCount, mMaxColumns));
    179     }
    180 
    181     Drawable getIcon(Resources res, int resId) {
    182         Drawable result;
    183         try {
    184             result = res.getDrawableForDensity(resId, mIconDpi);
    185         } catch (Resources.NotFoundException e) {
    186             result = null;
    187         }
    188 
    189         return result;
    190     }
    191 
    192     Drawable loadIconForResolveInfo(ResolveInfo ri) {
    193         Drawable dr;
    194         try {
    195             if (ri.resolvePackageName != null && ri.icon != 0) {
    196                 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
    197                 if (dr != null) {
    198                     return dr;
    199                 }
    200             }
    201             final int iconRes = ri.getIconResource();
    202             if (iconRes != 0) {
    203                 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
    204                 if (dr != null) {
    205                     return dr;
    206                 }
    207             }
    208         } catch (NameNotFoundException e) {
    209             Log.e(TAG, "Couldn't find resources for package", e);
    210         }
    211         return ri.loadIcon(mPm);
    212     }
    213 
    214     @Override
    215     protected void onRestart() {
    216         super.onRestart();
    217         if (!mRegistered) {
    218             mPackageMonitor.register(this, getMainLooper(), false);
    219             mRegistered = true;
    220         }
    221         mAdapter.handlePackagesChanged();
    222     }
    223 
    224     @Override
    225     protected void onStop() {
    226         super.onStop();
    227         if (mRegistered) {
    228             mPackageMonitor.unregister();
    229             mRegistered = false;
    230         }
    231         if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
    232             // This resolver is in the unusual situation where it has been
    233             // launched at the top of a new task.  We don't let it be added
    234             // to the recent tasks shown to the user, and we need to make sure
    235             // that each time we are launched we get the correct launching
    236             // uid (not re-using the same resolver from an old launching uid),
    237             // so we will now finish ourself since being no longer visible,
    238             // the user probably can't get back to us.
    239             if (!isChangingConfigurations()) {
    240                 finish();
    241             }
    242         }
    243     }
    244 
    245     @Override
    246     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    247         super.onRestoreInstanceState(savedInstanceState);
    248         if (mAlwaysUseOption) {
    249             final int checkedPos = mGrid.getCheckedItemPosition();
    250             final boolean enabled = checkedPos != GridView.INVALID_POSITION;
    251             mLastSelected = checkedPos;
    252             mAlwaysButton.setEnabled(enabled);
    253             mOnceButton.setEnabled(enabled);
    254             if (enabled) {
    255                 mGrid.setSelection(checkedPos);
    256             }
    257         }
    258     }
    259 
    260     @Override
    261     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    262         final int checkedPos = mGrid.getCheckedItemPosition();
    263         final boolean hasValidSelection = checkedPos != GridView.INVALID_POSITION;
    264         if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
    265             mAlwaysButton.setEnabled(hasValidSelection);
    266             mOnceButton.setEnabled(hasValidSelection);
    267             if (hasValidSelection) {
    268                 mGrid.smoothScrollToPosition(checkedPos);
    269             }
    270             mLastSelected = checkedPos;
    271         } else {
    272             startSelected(position, false);
    273         }
    274     }
    275 
    276     public void onButtonClick(View v) {
    277         final int id = v.getId();
    278         startSelected(mGrid.getCheckedItemPosition(), id == R.id.button_always);
    279         dismiss();
    280     }
    281 
    282     void startSelected(int which, boolean always) {
    283         ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
    284         Intent intent = mAdapter.intentForPosition(which);
    285         onIntentSelected(ri, intent, always);
    286         finish();
    287     }
    288 
    289     protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
    290         if (alwaysCheck) {
    291             // Build a reasonable intent filter, based on what matched.
    292             IntentFilter filter = new IntentFilter();
    293 
    294             if (intent.getAction() != null) {
    295                 filter.addAction(intent.getAction());
    296             }
    297             Set<String> categories = intent.getCategories();
    298             if (categories != null) {
    299                 for (String cat : categories) {
    300                     filter.addCategory(cat);
    301                 }
    302             }
    303             filter.addCategory(Intent.CATEGORY_DEFAULT);
    304 
    305             int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
    306             Uri data = intent.getData();
    307             if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
    308                 String mimeType = intent.resolveType(this);
    309                 if (mimeType != null) {
    310                     try {
    311                         filter.addDataType(mimeType);
    312                     } catch (IntentFilter.MalformedMimeTypeException e) {
    313                         Log.w("ResolverActivity", e);
    314                         filter = null;
    315                     }
    316                 }
    317             }
    318             if (data != null && data.getScheme() != null) {
    319                 // We need the data specification if there was no type,
    320                 // OR if the scheme is not one of our magical "file:"
    321                 // or "content:" schemes (see IntentFilter for the reason).
    322                 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
    323                         || (!"file".equals(data.getScheme())
    324                                 && !"content".equals(data.getScheme()))) {
    325                     filter.addDataScheme(data.getScheme());
    326 
    327                     // Look through the resolved filter to determine which part
    328                     // of it matched the original Intent.
    329                     Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
    330                     if (aIt != null) {
    331                         while (aIt.hasNext()) {
    332                             IntentFilter.AuthorityEntry a = aIt.next();
    333                             if (a.match(data) >= 0) {
    334                                 int port = a.getPort();
    335                                 filter.addDataAuthority(a.getHost(),
    336                                         port >= 0 ? Integer.toString(port) : null);
    337                                 break;
    338                             }
    339                         }
    340                     }
    341                     Iterator<PatternMatcher> pIt = ri.filter.pathsIterator();
    342                     if (pIt != null) {
    343                         String path = data.getPath();
    344                         while (path != null && pIt.hasNext()) {
    345                             PatternMatcher p = pIt.next();
    346                             if (p.match(path)) {
    347                                 filter.addDataPath(p.getPath(), p.getType());
    348                                 break;
    349                             }
    350                         }
    351                     }
    352                 }
    353             }
    354 
    355             if (filter != null) {
    356                 final int N = mAdapter.mList.size();
    357                 ComponentName[] set = new ComponentName[N];
    358                 int bestMatch = 0;
    359                 for (int i=0; i<N; i++) {
    360                     ResolveInfo r = mAdapter.mList.get(i).ri;
    361                     set[i] = new ComponentName(r.activityInfo.packageName,
    362                             r.activityInfo.name);
    363                     if (r.match > bestMatch) bestMatch = r.match;
    364                 }
    365                 getPackageManager().addPreferredActivity(filter, bestMatch, set,
    366                         intent.getComponent());
    367             }
    368         }
    369 
    370         if (intent != null) {
    371             startActivity(intent);
    372         }
    373     }
    374 
    375     void showAppDetails(ResolveInfo ri) {
    376         Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS")
    377                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
    378                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    379         startActivity(in);
    380     }
    381 
    382     private final class DisplayResolveInfo {
    383         ResolveInfo ri;
    384         CharSequence displayLabel;
    385         Drawable displayIcon;
    386         CharSequence extendedInfo;
    387         Intent origIntent;
    388 
    389         DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
    390                 CharSequence pInfo, Intent pOrigIntent) {
    391             ri = pri;
    392             displayLabel = pLabel;
    393             extendedInfo = pInfo;
    394             origIntent = pOrigIntent;
    395         }
    396     }
    397 
    398     private final class ResolveListAdapter extends BaseAdapter {
    399         private final Intent[] mInitialIntents;
    400         private final List<ResolveInfo> mBaseResolveList;
    401         private final Intent mIntent;
    402         private final int mLaunchedFromUid;
    403         private final LayoutInflater mInflater;
    404 
    405         private List<DisplayResolveInfo> mList;
    406 
    407         public ResolveListAdapter(Context context, Intent intent,
    408                 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
    409             mIntent = new Intent(intent);
    410             mIntent.setComponent(null);
    411             mInitialIntents = initialIntents;
    412             mBaseResolveList = rList;
    413             mLaunchedFromUid = launchedFromUid;
    414             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    415             mList = new ArrayList<DisplayResolveInfo>();
    416             rebuildList();
    417         }
    418 
    419         public void handlePackagesChanged() {
    420             final int oldItemCount = getCount();
    421             rebuildList();
    422             notifyDataSetChanged();
    423             final int newItemCount = getCount();
    424             if (newItemCount == 0) {
    425                 // We no longer have any items...  just finish the activity.
    426                 finish();
    427             } else if (newItemCount != oldItemCount) {
    428                 resizeGrid();
    429             }
    430         }
    431 
    432         private void rebuildList() {
    433             List<ResolveInfo> currentResolveList;
    434 
    435             mList.clear();
    436             if (mBaseResolveList != null) {
    437                 currentResolveList = mBaseResolveList;
    438             } else {
    439                 currentResolveList = mPm.queryIntentActivities(
    440                         mIntent, PackageManager.MATCH_DEFAULT_ONLY
    441                         | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
    442                 // Filter out any activities that the launched uid does not
    443                 // have permission for.  We don't do this when we have an explicit
    444                 // list of resolved activities, because that only happens when
    445                 // we are being subclassed, so we can safely launch whatever
    446                 // they gave us.
    447                 if (currentResolveList != null) {
    448                     for (int i=currentResolveList.size()-1; i >= 0; i--) {
    449                         ActivityInfo ai = currentResolveList.get(i).activityInfo;
    450                         int granted = ActivityManager.checkComponentPermission(
    451                                 ai.permission, mLaunchedFromUid,
    452                                 ai.applicationInfo.uid, ai.exported);
    453                         if (granted != PackageManager.PERMISSION_GRANTED) {
    454                             // Access not allowed!
    455                             currentResolveList.remove(i);
    456                         }
    457                     }
    458                 }
    459             }
    460             int N;
    461             if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
    462                 // Only display the first matches that are either of equal
    463                 // priority or have asked to be default options.
    464                 ResolveInfo r0 = currentResolveList.get(0);
    465                 for (int i=1; i<N; i++) {
    466                     ResolveInfo ri = currentResolveList.get(i);
    467                     if (DEBUG) Log.v(
    468                         "ResolveListActivity",
    469                         r0.activityInfo.name + "=" +
    470                         r0.priority + "/" + r0.isDefault + " vs " +
    471                         ri.activityInfo.name + "=" +
    472                         ri.priority + "/" + ri.isDefault);
    473                     if (r0.priority != ri.priority ||
    474                         r0.isDefault != ri.isDefault) {
    475                         while (i < N) {
    476                             currentResolveList.remove(i);
    477                             N--;
    478                         }
    479                     }
    480                 }
    481                 if (N > 1) {
    482                     ResolveInfo.DisplayNameComparator rComparator =
    483                             new ResolveInfo.DisplayNameComparator(mPm);
    484                     Collections.sort(currentResolveList, rComparator);
    485                 }
    486                 // First put the initial items at the top.
    487                 if (mInitialIntents != null) {
    488                     for (int i=0; i<mInitialIntents.length; i++) {
    489                         Intent ii = mInitialIntents[i];
    490                         if (ii == null) {
    491                             continue;
    492                         }
    493                         ActivityInfo ai = ii.resolveActivityInfo(
    494                                 getPackageManager(), 0);
    495                         if (ai == null) {
    496                             Log.w("ResolverActivity", "No activity found for "
    497                                     + ii);
    498                             continue;
    499                         }
    500                         ResolveInfo ri = new ResolveInfo();
    501                         ri.activityInfo = ai;
    502                         if (ii instanceof LabeledIntent) {
    503                             LabeledIntent li = (LabeledIntent)ii;
    504                             ri.resolvePackageName = li.getSourcePackage();
    505                             ri.labelRes = li.getLabelResource();
    506                             ri.nonLocalizedLabel = li.getNonLocalizedLabel();
    507                             ri.icon = li.getIconResource();
    508                         }
    509                         mList.add(new DisplayResolveInfo(ri,
    510                                 ri.loadLabel(getPackageManager()), null, ii));
    511                     }
    512                 }
    513 
    514                 // Check for applications with same name and use application name or
    515                 // package name if necessary
    516                 r0 = currentResolveList.get(0);
    517                 int start = 0;
    518                 CharSequence r0Label =  r0.loadLabel(mPm);
    519                 mShowExtended = false;
    520                 for (int i = 1; i < N; i++) {
    521                     if (r0Label == null) {
    522                         r0Label = r0.activityInfo.packageName;
    523                     }
    524                     ResolveInfo ri = currentResolveList.get(i);
    525                     CharSequence riLabel = ri.loadLabel(mPm);
    526                     if (riLabel == null) {
    527                         riLabel = ri.activityInfo.packageName;
    528                     }
    529                     if (riLabel.equals(r0Label)) {
    530                         continue;
    531                     }
    532                     processGroup(currentResolveList, start, (i-1), r0, r0Label);
    533                     r0 = ri;
    534                     r0Label = riLabel;
    535                     start = i;
    536                 }
    537                 // Process last group
    538                 processGroup(currentResolveList, start, (N-1), r0, r0Label);
    539             }
    540         }
    541 
    542         private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
    543                 CharSequence roLabel) {
    544             // Process labels from start to i
    545             int num = end - start+1;
    546             if (num == 1) {
    547                 // No duplicate labels. Use label for entry at start
    548                 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
    549             } else {
    550                 mShowExtended = true;
    551                 boolean usePkg = false;
    552                 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
    553                 if (startApp == null) {
    554                     usePkg = true;
    555                 }
    556                 if (!usePkg) {
    557                     // Use HashSet to track duplicates
    558                     HashSet<CharSequence> duplicates =
    559                         new HashSet<CharSequence>();
    560                     duplicates.add(startApp);
    561                     for (int j = start+1; j <= end ; j++) {
    562                         ResolveInfo jRi = rList.get(j);
    563                         CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
    564                         if ( (jApp == null) || (duplicates.contains(jApp))) {
    565                             usePkg = true;
    566                             break;
    567                         } else {
    568                             duplicates.add(jApp);
    569                         }
    570                     }
    571                     // Clear HashSet for later use
    572                     duplicates.clear();
    573                 }
    574                 for (int k = start; k <= end; k++) {
    575                     ResolveInfo add = rList.get(k);
    576                     if (usePkg) {
    577                         // Use application name for all entries from start to end-1
    578                         mList.add(new DisplayResolveInfo(add, roLabel,
    579                                 add.activityInfo.packageName, null));
    580                     } else {
    581                         // Use package name for all entries from start to end-1
    582                         mList.add(new DisplayResolveInfo(add, roLabel,
    583                                 add.activityInfo.applicationInfo.loadLabel(mPm), null));
    584                     }
    585                 }
    586             }
    587         }
    588 
    589         public ResolveInfo resolveInfoForPosition(int position) {
    590             return mList.get(position).ri;
    591         }
    592 
    593         public Intent intentForPosition(int position) {
    594             DisplayResolveInfo dri = mList.get(position);
    595 
    596             Intent intent = new Intent(dri.origIntent != null
    597                     ? dri.origIntent : mIntent);
    598             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
    599                     |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
    600             ActivityInfo ai = dri.ri.activityInfo;
    601             intent.setComponent(new ComponentName(
    602                     ai.applicationInfo.packageName, ai.name));
    603             return intent;
    604         }
    605 
    606         public int getCount() {
    607             return mList.size();
    608         }
    609 
    610         public Object getItem(int position) {
    611             return mList.get(position);
    612         }
    613 
    614         public long getItemId(int position) {
    615             return position;
    616         }
    617 
    618         public View getView(int position, View convertView, ViewGroup parent) {
    619             View view;
    620             if (convertView == null) {
    621                 view = mInflater.inflate(
    622                         com.android.internal.R.layout.resolve_list_item, parent, false);
    623 
    624                 // Fix the icon size even if we have different sized resources
    625                 ImageView icon = (ImageView)view.findViewById(R.id.icon);
    626                 ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) icon.getLayoutParams();
    627                 lp.width = lp.height = mIconSize;
    628             } else {
    629                 view = convertView;
    630             }
    631             bindView(view, mList.get(position));
    632             return view;
    633         }
    634 
    635         private final void bindView(View view, DisplayResolveInfo info) {
    636             TextView text = (TextView)view.findViewById(com.android.internal.R.id.text1);
    637             TextView text2 = (TextView)view.findViewById(com.android.internal.R.id.text2);
    638             ImageView icon = (ImageView)view.findViewById(R.id.icon);
    639             text.setText(info.displayLabel);
    640             if (mShowExtended) {
    641                 text2.setVisibility(View.VISIBLE);
    642                 text2.setText(info.extendedInfo);
    643             } else {
    644                 text2.setVisibility(View.GONE);
    645             }
    646             if (info.displayIcon == null) {
    647                 info.displayIcon = loadIconForResolveInfo(info.ri);
    648             }
    649             icon.setImageDrawable(info.displayIcon);
    650         }
    651     }
    652 
    653     class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
    654 
    655         @Override
    656         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
    657             ResolveInfo ri = mAdapter.resolveInfoForPosition(position);
    658             showAppDetails(ri);
    659             return true;
    660         }
    661 
    662     }
    663 }
    664 
    665