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