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