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