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