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