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