Home | History | Annotate | Download | only in widget
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    378     public boolean isShowingPopup() {
    379         return getListPopupWindow().isShowing();
    380     }
    381 
    382     @Override
    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
    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
    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
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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 > 0 && 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
    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
    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
    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
    593         public void onDismiss() {
    594             notifyOnDismissListener();
    595             if (mProvider != null) {
    596                 mProvider.subUiVisibilityChanged(false);
    597             }
    598         }
    599 
    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 
    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
    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
    654         public int getViewTypeCount() {
    655             return ITEM_VIEW_TYPE_COUNT;
    656         }
    657 
    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 
    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 
    686         public long getItemId(int position) {
    687             return position;
    688         }
    689 
    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 
    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 
    752         public void setMaxActivityCount(int maxActivityCount) {
    753             if (mMaxActivityCount != maxActivityCount) {
    754                 mMaxActivityCount = maxActivityCount;
    755                 notifyDataSetChanged();
    756             }
    757         }
    758 
    759         public ResolveInfo getDefaultActivity() {
    760             return mDataModel.getDefaultActivity();
    761         }
    762 
    763         public void setShowFooterView(boolean showFooterView) {
    764             if (mShowFooterView != showFooterView) {
    765                 mShowFooterView = showFooterView;
    766                 notifyDataSetChanged();
    767             }
    768         }
    769 
    770         public int getActivityCount() {
    771             return mDataModel.getActivityCount();
    772         }
    773 
    774         public int getHistorySize() {
    775             return mDataModel.getHistorySize();
    776         }
    777 
    778         public int getMaxActivityCount() {
    779             return mMaxActivityCount;
    780         }
    781 
    782         public ActivityChooserModel getDataModel() {
    783             return mDataModel;
    784         }
    785 
    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 
    796         public boolean getShowDefaultActivity() {
    797             return mShowDefaultActivity;
    798         }
    799     }
    800 }
    801