ActivityChooserView.java revision cb8ed39b3fb591be60b9fb1799d4ea4530eab758
1/*
2 * Copyright (C) 2011 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.widget;
18
19import com.android.internal.R;
20
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.database.DataSetObserver;
28import android.graphics.drawable.Drawable;
29import android.util.AttributeSet;
30import android.view.ActionProvider;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.ViewTreeObserver;
35import android.view.ViewTreeObserver.OnGlobalLayoutListener;
36import android.view.accessibility.AccessibilityNodeInfo;
37import android.widget.ActivityChooserModel.ActivityChooserModelClient;
38
39/**
40 * This class is a view for choosing an activity for handling a given {@link Intent}.
41 * <p>
42 * The view is composed of two adjacent buttons:
43 * <ul>
44 * <li>
45 * The left button is an immediate action and allows one click activity choosing.
46 * Tapping this button immediately executes the intent without requiring any further
47 * user input. Long press on this button shows a popup for changing the default
48 * activity.
49 * </li>
50 * <li>
51 * The right button is an overflow action and provides an optimized menu
52 * of additional activities. Tapping this button shows a popup anchored to this
53 * view, listing the most frequently used activities. This list is initially
54 * limited to a small number of items in frequency used order. The last item,
55 * "Show all..." serves as an affordance to display all available activities.
56 * </li>
57 * </ul>
58 * </p>
59 *
60 * @hide
61 */
62public class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient {
63
64    /**
65     * An adapter for displaying the activities in an {@link AdapterView}.
66     */
67    private final ActivityChooserViewAdapter mAdapter;
68
69    /**
70     * Implementation of various interfaces to avoid publishing them in the APIs.
71     */
72    private final Callbacks mCallbacks;
73
74    /**
75     * The content of this view.
76     */
77    private final LinearLayout mActivityChooserContent;
78
79    /**
80     * Stores the background drawable to allow hiding and latter showing.
81     */
82    private final Drawable mActivityChooserContentBackground;
83
84    /**
85     * The expand activities action button;
86     */
87    private final FrameLayout mExpandActivityOverflowButton;
88
89    /**
90     * The image for the expand activities action button;
91     */
92    private final ImageView mExpandActivityOverflowButtonImage;
93
94    /**
95     * The default activities action button;
96     */
97    private final FrameLayout mDefaultActivityButton;
98
99    /**
100     * The image for the default activities action button;
101     */
102    private final ImageView mDefaultActivityButtonImage;
103
104    /**
105     * The maximal width of the list popup.
106     */
107    private final int mListPopupMaxWidth;
108
109    /**
110     * The ActionProvider hosting this view, if applicable.
111     */
112    ActionProvider mProvider;
113
114    /**
115     * Observer for the model data.
116     */
117    private final DataSetObserver mModelDataSetOberver = new DataSetObserver() {
118
119        @Override
120        public void onChanged() {
121            super.onChanged();
122            mAdapter.notifyDataSetChanged();
123        }
124        @Override
125        public void onInvalidated() {
126            super.onInvalidated();
127            mAdapter.notifyDataSetInvalidated();
128        }
129    };
130
131    private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
132        @Override
133        public void onGlobalLayout() {
134            if (isShowingPopup()) {
135                if (!isShown()) {
136                    getListPopupWindow().dismiss();
137                } else {
138                    getListPopupWindow().show();
139                    if (mProvider != null) {
140                        mProvider.subUiVisibilityChanged(true);
141                    }
142                }
143            }
144        }
145    };
146
147    /**
148     * Popup window for showing the activity overflow list.
149     */
150    private ListPopupWindow mListPopupWindow;
151
152    /**
153     * Listener for the dismissal of the popup/alert.
154     */
155    private PopupWindow.OnDismissListener mOnDismissListener;
156
157    /**
158     * Flag whether a default activity currently being selected.
159     */
160    private boolean mIsSelectingDefaultActivity;
161
162    /**
163     * The count of activities in the popup.
164     */
165    private int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT;
166
167    /**
168     * Flag whether this view is attached to a window.
169     */
170    private boolean mIsAttachedToWindow;
171
172    /**
173     * String resource for formatting content description of the default target.
174     */
175    private int mDefaultActionButtonContentDescription;
176
177    /**
178     * Create a new instance.
179     *
180     * @param context The application environment.
181     */
182    public ActivityChooserView(Context context) {
183        this(context, null);
184    }
185
186    /**
187     * Create a new instance.
188     *
189     * @param context The application environment.
190     * @param attrs A collection of attributes.
191     */
192    public ActivityChooserView(Context context, AttributeSet attrs) {
193        this(context, attrs, 0);
194    }
195
196    /**
197     * Create a new instance.
198     *
199     * @param context The application environment.
200     * @param attrs A collection of attributes.
201     * @param defStyle The default style to apply to this view.
202     */
203    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
204        super(context, attrs, defStyle);
205
206        TypedArray attributesArray = context.obtainStyledAttributes(attrs,
207                R.styleable.ActivityChooserView, defStyle, 0);
208
209        mInitialActivityCount = attributesArray.getInt(
210                R.styleable.ActivityChooserView_initialActivityCount,
211                ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT);
212
213        Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
214                R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable);
215
216        attributesArray.recycle();
217
218        LayoutInflater inflater = LayoutInflater.from(mContext);
219        inflater.inflate(R.layout.activity_chooser_view, this, true);
220
221        mCallbacks = new Callbacks();
222
223        mActivityChooserContent = (LinearLayout) findViewById(R.id.activity_chooser_view_content);
224        mActivityChooserContentBackground = mActivityChooserContent.getBackground();
225
226        mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button);
227        mDefaultActivityButton.setOnClickListener(mCallbacks);
228        mDefaultActivityButton.setOnLongClickListener(mCallbacks);
229        mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
230
231        mExpandActivityOverflowButton = (FrameLayout) findViewById(R.id.expand_activities_button);
232        mExpandActivityOverflowButton.setOnClickListener(mCallbacks);
233        mExpandActivityOverflowButton.setAccessibilityDelegate(new AccessibilityDelegate() {
234            @Override
235            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
236                super.onInitializeAccessibilityNodeInfo(host, info);
237                info.setCanOpenPopup(true);
238            }
239        });
240        mExpandActivityOverflowButtonImage =
241            (ImageView) mExpandActivityOverflowButton.findViewById(R.id.image);
242        mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
243
244        mAdapter = new ActivityChooserViewAdapter();
245        mAdapter.registerDataSetObserver(new DataSetObserver() {
246            @Override
247            public void onChanged() {
248                super.onChanged();
249                updateAppearance();
250            }
251        });
252
253        Resources resources = context.getResources();
254        mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2,
255              resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
256    }
257
258    /**
259     * {@inheritDoc}
260     */
261    public void setActivityChooserModel(ActivityChooserModel dataModel) {
262        mAdapter.setDataModel(dataModel);
263        if (isShowingPopup()) {
264            dismissPopup();
265            showPopup();
266        }
267    }
268
269    /**
270     * Sets the background for the button that expands the activity
271     * overflow list.
272     *
273     * <strong>Note:</strong> Clients would like to set this drawable
274     * as a clue about the action the chosen activity will perform. For
275     * example, if a share activity is to be chosen the drawable should
276     * give a clue that sharing is to be performed.
277     *
278     * @param drawable The drawable.
279     */
280    public void setExpandActivityOverflowButtonDrawable(Drawable drawable) {
281        mExpandActivityOverflowButtonImage.setImageDrawable(drawable);
282    }
283
284    /**
285     * Sets the content description for the button that expands the activity
286     * overflow list.
287     *
288     * description as a clue about the action performed by the button.
289     * For example, if a share activity is to be chosen the content
290     * description should be something like "Share with".
291     *
292     * @param resourceId The content description resource id.
293     */
294    public void setExpandActivityOverflowButtonContentDescription(int resourceId) {
295        CharSequence contentDescription = mContext.getString(resourceId);
296        mExpandActivityOverflowButtonImage.setContentDescription(contentDescription);
297    }
298
299    /**
300     * Set the provider hosting this view, if applicable.
301     * @hide Internal use only
302     */
303    public void setProvider(ActionProvider provider) {
304        mProvider = provider;
305    }
306
307    /**
308     * Shows the popup window with activities.
309     *
310     * @return True if the popup was shown, false if already showing.
311     */
312    public boolean showPopup() {
313        if (isShowingPopup() || !mIsAttachedToWindow) {
314            return false;
315        }
316        mIsSelectingDefaultActivity = false;
317        showPopupUnchecked(mInitialActivityCount);
318        return true;
319    }
320
321    /**
322     * Shows the popup no matter if it was already showing.
323     *
324     * @param maxActivityCount The max number of activities to display.
325     */
326    private void showPopupUnchecked(int maxActivityCount) {
327        if (mAdapter.getDataModel() == null) {
328            throw new IllegalStateException("No data model. Did you call #setDataModel?");
329        }
330
331        getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
332
333        final boolean defaultActivityButtonShown =
334            mDefaultActivityButton.getVisibility() == VISIBLE;
335
336        final int activityCount = mAdapter.getActivityCount();
337        final int maxActivityCountOffset = defaultActivityButtonShown ? 1 : 0;
338        if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED
339                && activityCount > maxActivityCount + maxActivityCountOffset) {
340            mAdapter.setShowFooterView(true);
341            mAdapter.setMaxActivityCount(maxActivityCount - 1);
342        } else {
343            mAdapter.setShowFooterView(false);
344            mAdapter.setMaxActivityCount(maxActivityCount);
345        }
346
347        ListPopupWindow popupWindow = getListPopupWindow();
348        if (!popupWindow.isShowing()) {
349            if (mIsSelectingDefaultActivity || !defaultActivityButtonShown) {
350                mAdapter.setShowDefaultActivity(true, defaultActivityButtonShown);
351            } else {
352                mAdapter.setShowDefaultActivity(false, false);
353            }
354            final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth);
355            popupWindow.setContentWidth(contentWidth);
356            popupWindow.show();
357            if (mProvider != null) {
358                mProvider.subUiVisibilityChanged(true);
359            }
360            popupWindow.getListView().setContentDescription(mContext.getString(
361                    R.string.activitychooserview_choose_application));
362        }
363    }
364
365    /**
366     * Dismisses the popup window with activities.
367     *
368     * @return True if dismissed, false if already dismissed.
369     */
370    public boolean dismissPopup() {
371        if (isShowingPopup()) {
372            getListPopupWindow().dismiss();
373            ViewTreeObserver viewTreeObserver = getViewTreeObserver();
374            if (viewTreeObserver.isAlive()) {
375                viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
376            }
377        }
378        return true;
379    }
380
381    /**
382     * Gets whether the popup window with activities is shown.
383     *
384     * @return True if the popup is shown.
385     */
386    public boolean isShowingPopup() {
387        return getListPopupWindow().isShowing();
388    }
389
390    @Override
391    protected void onAttachedToWindow() {
392        super.onAttachedToWindow();
393        ActivityChooserModel dataModel = mAdapter.getDataModel();
394        if (dataModel != null) {
395            dataModel.registerObserver(mModelDataSetOberver);
396        }
397        mIsAttachedToWindow = true;
398    }
399
400    @Override
401    protected void onDetachedFromWindow() {
402        super.onDetachedFromWindow();
403        ActivityChooserModel dataModel = mAdapter.getDataModel();
404        if (dataModel != null) {
405            dataModel.unregisterObserver(mModelDataSetOberver);
406        }
407        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
408        if (viewTreeObserver.isAlive()) {
409            viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
410        }
411        if (isShowingPopup()) {
412            dismissPopup();
413        }
414        mIsAttachedToWindow = false;
415    }
416
417    @Override
418    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
419        View child = mActivityChooserContent;
420        // If the default action is not visible we want to be as tall as the
421        // ActionBar so if this widget is used in the latter it will look as
422        // a normal action button.
423        if (mDefaultActivityButton.getVisibility() != VISIBLE) {
424            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
425                    MeasureSpec.EXACTLY);
426        }
427        measureChild(child, widthMeasureSpec, heightMeasureSpec);
428        setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
429    }
430
431    @Override
432    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
433        mActivityChooserContent.layout(0, 0, right - left, bottom - top);
434        if (!isShowingPopup()) {
435            dismissPopup();
436        }
437    }
438
439    public ActivityChooserModel getDataModel() {
440        return mAdapter.getDataModel();
441    }
442
443    /**
444     * Sets a listener to receive a callback when the popup is dismissed.
445     *
446     * @param listener The listener to be notified.
447     */
448    public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
449        mOnDismissListener = listener;
450    }
451
452    /**
453     * Sets the initial count of items shown in the activities popup
454     * i.e. the items before the popup is expanded. This is an upper
455     * bound since it is not guaranteed that such number of intent
456     * handlers exist.
457     *
458     * @param itemCount The initial popup item count.
459     */
460    public void setInitialActivityCount(int itemCount) {
461        mInitialActivityCount = itemCount;
462    }
463
464    /**
465     * Sets a content description of the default action button. This
466     * resource should be a string taking one formatting argument and
467     * will be used for formatting the content description of the button
468     * dynamically as the default target changes. For example, a resource
469     * pointing to the string "share with %1$s" will result in a content
470     * description "share with Bluetooth" for the Bluetooth activity.
471     *
472     * @param resourceId The resource id.
473     */
474    public void setDefaultActionButtonContentDescription(int resourceId) {
475        mDefaultActionButtonContentDescription = resourceId;
476    }
477
478    /**
479     * Gets the list popup window which is lazily initialized.
480     *
481     * @return The popup.
482     */
483    private ListPopupWindow getListPopupWindow() {
484        if (mListPopupWindow == null) {
485            mListPopupWindow = new ListPopupWindow(getContext());
486            mListPopupWindow.setAdapter(mAdapter);
487            mListPopupWindow.setAnchorView(ActivityChooserView.this);
488            mListPopupWindow.setModal(true);
489            mListPopupWindow.setOnItemClickListener(mCallbacks);
490            mListPopupWindow.setOnDismissListener(mCallbacks);
491        }
492        return mListPopupWindow;
493    }
494
495    /**
496     * Updates the buttons state.
497     */
498    private void updateAppearance() {
499        // Expand overflow button.
500        if (mAdapter.getCount() > 0) {
501            mExpandActivityOverflowButton.setEnabled(true);
502        } else {
503            mExpandActivityOverflowButton.setEnabled(false);
504        }
505        // Default activity button.
506        final int activityCount = mAdapter.getActivityCount();
507        final int historySize = mAdapter.getHistorySize();
508        if (activityCount==1 || activityCount > 1 && historySize > 0) {
509            mDefaultActivityButton.setVisibility(VISIBLE);
510            ResolveInfo activity = mAdapter.getDefaultActivity();
511            PackageManager packageManager = mContext.getPackageManager();
512            mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager));
513            if (mDefaultActionButtonContentDescription != 0) {
514                CharSequence label = activity.loadLabel(packageManager);
515                String contentDescription = mContext.getString(
516                        mDefaultActionButtonContentDescription, label);
517                mDefaultActivityButton.setContentDescription(contentDescription);
518            }
519        } else {
520            mDefaultActivityButton.setVisibility(View.GONE);
521        }
522        // Activity chooser content.
523        if (mDefaultActivityButton.getVisibility() == VISIBLE) {
524            mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
525        } else {
526            mActivityChooserContent.setBackgroundDrawable(null);
527        }
528    }
529
530    /**
531     * Interface implementation to avoid publishing them in the APIs.
532     */
533    private class Callbacks implements AdapterView.OnItemClickListener,
534            View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener {
535
536        // AdapterView#OnItemClickListener
537        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
538            ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
539            final int itemViewType = adapter.getItemViewType(position);
540            switch (itemViewType) {
541                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
542                    showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
543                } break;
544                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
545                    dismissPopup();
546                    if (mIsSelectingDefaultActivity) {
547                        // The item at position zero is the default already.
548                        if (position > 0) {
549                            mAdapter.getDataModel().setDefaultActivity(position);
550                        }
551                    } else {
552                        // If the default target is not shown in the list, the first
553                        // item in the model is default action => adjust index
554                        position = mAdapter.getShowDefaultActivity() ? position : position + 1;
555                        Intent launchIntent = mAdapter.getDataModel().chooseActivity(position);
556                        if (launchIntent != null) {
557                            launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
558                            mContext.startActivity(launchIntent);
559                        }
560                    }
561                } break;
562                default:
563                    throw new IllegalArgumentException();
564            }
565        }
566
567        // View.OnClickListener
568        public void onClick(View view) {
569            if (view == mDefaultActivityButton) {
570                dismissPopup();
571                ResolveInfo defaultActivity = mAdapter.getDefaultActivity();
572                final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
573                Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
574                if (launchIntent != null) {
575                    launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
576                    mContext.startActivity(launchIntent);
577                }
578            } else if (view == mExpandActivityOverflowButton) {
579                mIsSelectingDefaultActivity = false;
580                showPopupUnchecked(mInitialActivityCount);
581            } else {
582                throw new IllegalArgumentException();
583            }
584        }
585
586        // OnLongClickListener#onLongClick
587        @Override
588        public boolean onLongClick(View view) {
589            if (view == mDefaultActivityButton) {
590                if (mAdapter.getCount() > 0) {
591                    mIsSelectingDefaultActivity = true;
592                    showPopupUnchecked(mInitialActivityCount);
593                }
594            } else {
595                throw new IllegalArgumentException();
596            }
597            return true;
598        }
599
600        // PopUpWindow.OnDismissListener#onDismiss
601        public void onDismiss() {
602            notifyOnDismissListener();
603            if (mProvider != null) {
604                mProvider.subUiVisibilityChanged(false);
605            }
606        }
607
608        private void notifyOnDismissListener() {
609            if (mOnDismissListener != null) {
610                mOnDismissListener.onDismiss();
611            }
612        }
613    }
614
615    /**
616     * Adapter for backing the list of activities shown in the popup.
617     */
618    private class ActivityChooserViewAdapter extends BaseAdapter {
619
620        public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE;
621
622        public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
623
624        private static final int ITEM_VIEW_TYPE_ACTIVITY = 0;
625
626        private static final int ITEM_VIEW_TYPE_FOOTER = 1;
627
628        private static final int ITEM_VIEW_TYPE_COUNT = 3;
629
630        private ActivityChooserModel mDataModel;
631
632        private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
633
634        private boolean mShowDefaultActivity;
635
636        private boolean mHighlightDefaultActivity;
637
638        private boolean mShowFooterView;
639
640        public void setDataModel(ActivityChooserModel dataModel) {
641            ActivityChooserModel oldDataModel = mAdapter.getDataModel();
642            if (oldDataModel != null && isShown()) {
643                oldDataModel.unregisterObserver(mModelDataSetOberver);
644            }
645            mDataModel = dataModel;
646            if (dataModel != null && isShown()) {
647                dataModel.registerObserver(mModelDataSetOberver);
648            }
649            notifyDataSetChanged();
650        }
651
652        @Override
653        public int getItemViewType(int position) {
654            if (mShowFooterView && position == getCount() - 1) {
655                return ITEM_VIEW_TYPE_FOOTER;
656            } else {
657                return ITEM_VIEW_TYPE_ACTIVITY;
658            }
659        }
660
661        @Override
662        public int getViewTypeCount() {
663            return ITEM_VIEW_TYPE_COUNT;
664        }
665
666        public int getCount() {
667            int count = 0;
668            int activityCount = mDataModel.getActivityCount();
669            if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
670                activityCount--;
671            }
672            count = Math.min(activityCount, mMaxActivityCount);
673            if (mShowFooterView) {
674                count++;
675            }
676            return count;
677        }
678
679        public Object getItem(int position) {
680            final int itemViewType = getItemViewType(position);
681            switch (itemViewType) {
682                case ITEM_VIEW_TYPE_FOOTER:
683                    return null;
684                case ITEM_VIEW_TYPE_ACTIVITY:
685                    if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
686                        position++;
687                    }
688                    return mDataModel.getActivity(position);
689                default:
690                    throw new IllegalArgumentException();
691            }
692        }
693
694        public long getItemId(int position) {
695            return position;
696        }
697
698        public View getView(int position, View convertView, ViewGroup parent) {
699            final int itemViewType = getItemViewType(position);
700            switch (itemViewType) {
701                case ITEM_VIEW_TYPE_FOOTER:
702                    if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) {
703                        convertView = LayoutInflater.from(getContext()).inflate(
704                                R.layout.activity_chooser_view_list_item, parent, false);
705                        convertView.setId(ITEM_VIEW_TYPE_FOOTER);
706                        TextView titleView = (TextView) convertView.findViewById(R.id.title);
707                        titleView.setText(mContext.getString(
708                                R.string.activity_chooser_view_see_all));
709                    }
710                    return convertView;
711                case ITEM_VIEW_TYPE_ACTIVITY:
712                    if (convertView == null || convertView.getId() != R.id.list_item) {
713                        convertView = LayoutInflater.from(getContext()).inflate(
714                                R.layout.activity_chooser_view_list_item, parent, false);
715                    }
716                    PackageManager packageManager = mContext.getPackageManager();
717                    // Set the icon
718                    ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
719                    ResolveInfo activity = (ResolveInfo) getItem(position);
720                    iconView.setImageDrawable(activity.loadIcon(packageManager));
721                    // Set the title.
722                    TextView titleView = (TextView) convertView.findViewById(R.id.title);
723                    titleView.setText(activity.loadLabel(packageManager));
724                    // Highlight the default.
725                    if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
726                        convertView.setActivated(true);
727                    } else {
728                        convertView.setActivated(false);
729                    }
730                    return convertView;
731                default:
732                    throw new IllegalArgumentException();
733            }
734        }
735
736        public int measureContentWidth() {
737            // The user may have specified some of the target not to be shown but we
738            // want to measure all of them since after expansion they should fit.
739            final int oldMaxActivityCount = mMaxActivityCount;
740            mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
741
742            int contentWidth = 0;
743            View itemView = null;
744
745            final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
746            final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
747            final int count = getCount();
748
749            for (int i = 0; i < count; i++) {
750                itemView = getView(i, itemView, null);
751                itemView.measure(widthMeasureSpec, heightMeasureSpec);
752                contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth());
753            }
754
755            mMaxActivityCount = oldMaxActivityCount;
756
757            return contentWidth;
758        }
759
760        public void setMaxActivityCount(int maxActivityCount) {
761            if (mMaxActivityCount != maxActivityCount) {
762                mMaxActivityCount = maxActivityCount;
763                notifyDataSetChanged();
764            }
765        }
766
767        public ResolveInfo getDefaultActivity() {
768            return mDataModel.getDefaultActivity();
769        }
770
771        public void setShowFooterView(boolean showFooterView) {
772            if (mShowFooterView != showFooterView) {
773                mShowFooterView = showFooterView;
774                notifyDataSetChanged();
775            }
776        }
777
778        public int getActivityCount() {
779            return mDataModel.getActivityCount();
780        }
781
782        public int getHistorySize() {
783            return mDataModel.getHistorySize();
784        }
785
786        public int getMaxActivityCount() {
787            return mMaxActivityCount;
788        }
789
790        public ActivityChooserModel getDataModel() {
791            return mDataModel;
792        }
793
794        public void setShowDefaultActivity(boolean showDefaultActivity,
795                boolean highlightDefaultActivity) {
796            if (mShowDefaultActivity != showDefaultActivity
797                    || mHighlightDefaultActivity != highlightDefaultActivity) {
798                mShowDefaultActivity = showDefaultActivity;
799                mHighlightDefaultActivity = highlightDefaultActivity;
800                notifyDataSetChanged();
801            }
802        }
803
804        public boolean getShowDefaultActivity() {
805            return mShowDefaultActivity;
806        }
807    }
808}
809