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