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