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