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.support.v4.app; 18 19 import static android.os.Build.VERSION.SDK_INT; 20 21 import android.app.Activity; 22 import android.content.ComponentName; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.graphics.drawable.Drawable; 27 import android.net.Uri; 28 import android.support.annotation.StringRes; 29 import android.support.v4.content.IntentCompat; 30 import android.text.Html; 31 import android.text.Spanned; 32 import android.util.Log; 33 import android.view.ActionProvider; 34 import android.view.Menu; 35 import android.view.MenuItem; 36 import android.widget.ShareActionProvider; 37 38 import java.util.ArrayList; 39 40 /** 41 * Extra helper functionality for sharing data between activities. 42 * 43 * ShareCompat provides functionality to extend the {@link Intent#ACTION_SEND}/ 44 * {@link Intent#ACTION_SEND_MULTIPLE} protocol and support retrieving more info 45 * about the activity that invoked a social sharing action. 46 * 47 * {@link IntentBuilder} provides helper functions for constructing a sharing 48 * intent that always includes data about the calling activity and app. 49 * This lets the called activity provide attribution for the app that shared 50 * content. Constructing an intent this way can be done in a method-chaining style. 51 * To obtain an IntentBuilder with info about your calling activity, use the static 52 * method {@link IntentBuilder#from(Activity)}. 53 * 54 * {@link IntentReader} provides helper functions for parsing the defined extras 55 * within an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE} intent 56 * used to launch an activity. You can also obtain a Drawable for the caller's 57 * application icon and the application's localized label (the app's human-readable name). 58 * Social apps that enable sharing content are encouraged to use this information 59 * to call out the app that the content was shared from. 60 */ 61 public final class ShareCompat { 62 /** 63 * Intent extra that stores the name of the calling package for an ACTION_SEND intent. 64 * When an activity is started using startActivityForResult this is redundant info. 65 * (It is also provided by {@link Activity#getCallingPackage()}.) 66 * 67 * Instead of using this constant directly, consider using {@link #getCallingPackage(Activity)} 68 * or {@link IntentReader#getCallingPackage()}. 69 */ 70 public static final String EXTRA_CALLING_PACKAGE = 71 "android.support.v4.app.EXTRA_CALLING_PACKAGE"; 72 73 /** 74 * Intent extra that stores the {@link ComponentName} of the calling activity for 75 * an ACTION_SEND intent. 76 */ 77 public static final String EXTRA_CALLING_ACTIVITY = 78 "android.support.v4.app.EXTRA_CALLING_ACTIVITY"; 79 80 private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_"; 81 ShareCompat()82 private ShareCompat() {} 83 84 /** 85 * Retrieve the name of the package that launched calledActivity from a share intent. 86 * Apps that provide social sharing functionality can use this to provide attribution 87 * for the app that shared the content. 88 * 89 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 90 * application. As such it should not be trusted for accuracy in the context of 91 * security or verification.</p> 92 * 93 * @param calledActivity Current activity that was launched to share content 94 * @return Name of the calling package 95 */ getCallingPackage(Activity calledActivity)96 public static String getCallingPackage(Activity calledActivity) { 97 String result = calledActivity.getCallingPackage(); 98 if (result == null) { 99 result = calledActivity.getIntent().getStringExtra(EXTRA_CALLING_PACKAGE); 100 } 101 return result; 102 } 103 104 /** 105 * Retrieve the ComponentName of the activity that launched calledActivity from a share intent. 106 * Apps that provide social sharing functionality can use this to provide attribution 107 * for the app that shared the content. 108 * 109 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 110 * application. As such it should not be trusted for accuracy in the context of 111 * security or verification.</p> 112 * 113 * @param calledActivity Current activity that was launched to share content 114 * @return ComponentName of the calling activity 115 */ getCallingActivity(Activity calledActivity)116 public static ComponentName getCallingActivity(Activity calledActivity) { 117 ComponentName result = calledActivity.getCallingActivity(); 118 if (result == null) { 119 result = calledActivity.getIntent().getParcelableExtra(EXTRA_CALLING_ACTIVITY); 120 } 121 return result; 122 } 123 124 /** 125 * Configure a {@link MenuItem} to act as a sharing action. 126 * 127 * <p>This method will configure a ShareActionProvider to provide a more robust UI 128 * for selecting the target of the share. History will be tracked for each calling 129 * activity in a file named with the prefix ".sharecompat_" in the application's 130 * private data directory. If the application wishes to set this MenuItem to show 131 * as an action in the Action Bar it should use {@link MenuItem#setShowAsAction(int)} to request 132 * that behavior in addition to calling this method.</p> 133 * 134 * <p>During the calling activity's lifecycle, if data within the share intent must 135 * change the app should change that state in one of several ways:</p> 136 * <ul> 137 * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app uses the 138 * Action Bar its menu will be recreated and rebuilt. 139 * If not, the activity will receive a call to {@link Activity#onPrepareOptionsMenu(Menu)} 140 * the next time the user presses the menu key to open the options menu panel. The activity 141 * can then call configureMenuItem again with a new or altered IntentBuilder to reconfigure 142 * the share menu item.</li> 143 * <li>Keep a reference to the MenuItem object for the share item once it has been created 144 * and call configureMenuItem to update the associated sharing intent as needed.</li> 145 * </ul> 146 * 147 * @param item MenuItem to configure for sharing 148 * @param shareIntent IntentBuilder with data about the content to share 149 */ configureMenuItem(MenuItem item, IntentBuilder shareIntent)150 public static void configureMenuItem(MenuItem item, IntentBuilder shareIntent) { 151 ActionProvider itemProvider = item.getActionProvider(); 152 ShareActionProvider provider; 153 if (!(itemProvider instanceof ShareActionProvider)) { 154 provider = new ShareActionProvider(shareIntent.getActivity()); 155 } else { 156 provider = (ShareActionProvider) itemProvider; 157 } 158 provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX 159 + shareIntent.getActivity().getClass().getName()); 160 provider.setShareIntent(shareIntent.getIntent()); 161 item.setActionProvider(provider); 162 163 if (SDK_INT < 16) { 164 if (!item.hasSubMenu()) { 165 item.setIntent(shareIntent.createChooserIntent()); 166 } 167 } 168 } 169 170 /** 171 * Configure a menu item to act as a sharing action. 172 * 173 * @param menu Menu containing the item to use for sharing 174 * @param menuItemId ID of the share item within menu 175 * @param shareIntent IntentBuilder with data about the content to share 176 * @see #configureMenuItem(MenuItem, IntentBuilder) 177 */ configureMenuItem(Menu menu, int menuItemId, IntentBuilder shareIntent)178 public static void configureMenuItem(Menu menu, int menuItemId, IntentBuilder shareIntent) { 179 MenuItem item = menu.findItem(menuItemId); 180 if (item == null) { 181 throw new IllegalArgumentException("Could not find menu item with id " + menuItemId 182 + " in the supplied menu"); 183 } 184 configureMenuItem(item, shareIntent); 185 } 186 187 /** 188 * IntentBuilder is a helper for constructing {@link Intent#ACTION_SEND} and 189 * {@link Intent#ACTION_SEND_MULTIPLE} sharing intents and starting activities 190 * to share content. The ComponentName and package name of the calling activity 191 * will be included. 192 */ 193 public static class IntentBuilder { 194 private Activity mActivity; 195 private Intent mIntent; 196 private CharSequence mChooserTitle; 197 private ArrayList<String> mToAddresses; 198 private ArrayList<String> mCcAddresses; 199 private ArrayList<String> mBccAddresses; 200 201 private ArrayList<Uri> mStreams; 202 203 /** 204 * Create a new IntentBuilder for launching a sharing action from launchingActivity. 205 * 206 * @param launchingActivity Activity that the share will be launched from 207 * @return a new IntentBuilder instance 208 */ from(Activity launchingActivity)209 public static IntentBuilder from(Activity launchingActivity) { 210 return new IntentBuilder(launchingActivity); 211 } 212 IntentBuilder(Activity launchingActivity)213 private IntentBuilder(Activity launchingActivity) { 214 mActivity = launchingActivity; 215 mIntent = new Intent().setAction(Intent.ACTION_SEND); 216 mIntent.putExtra(EXTRA_CALLING_PACKAGE, launchingActivity.getPackageName()); 217 mIntent.putExtra(EXTRA_CALLING_ACTIVITY, launchingActivity.getComponentName()); 218 mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 219 } 220 221 /** 222 * Retrieve the Intent as configured so far by the IntentBuilder. This Intent 223 * is suitable for use in a ShareActionProvider or chooser dialog. 224 * 225 * <p>To create an intent that will launch the activity chooser so that the user 226 * may select a target for the share, see {@link #createChooserIntent()}. 227 * 228 * @return The current Intent being configured by this builder 229 */ getIntent()230 public Intent getIntent() { 231 if (mToAddresses != null) { 232 combineArrayExtra(Intent.EXTRA_EMAIL, mToAddresses); 233 mToAddresses = null; 234 } 235 if (mCcAddresses != null) { 236 combineArrayExtra(Intent.EXTRA_CC, mCcAddresses); 237 mCcAddresses = null; 238 } 239 if (mBccAddresses != null) { 240 combineArrayExtra(Intent.EXTRA_BCC, mBccAddresses); 241 mBccAddresses = null; 242 } 243 244 // Check if we need to change the action. 245 boolean needsSendMultiple = mStreams != null && mStreams.size() > 1; 246 boolean isSendMultiple = mIntent.getAction().equals(Intent.ACTION_SEND_MULTIPLE); 247 248 if (!needsSendMultiple && isSendMultiple) { 249 // Change back to a single send action; place the first stream into the 250 // intent for single sharing. 251 mIntent.setAction(Intent.ACTION_SEND); 252 if (mStreams != null && !mStreams.isEmpty()) { 253 mIntent.putExtra(Intent.EXTRA_STREAM, mStreams.get(0)); 254 } else { 255 mIntent.removeExtra(Intent.EXTRA_STREAM); 256 } 257 mStreams = null; 258 } 259 260 if (needsSendMultiple && !isSendMultiple) { 261 // Change to a multiple send action; place the relevant ArrayList into the 262 // intent for multiple sharing. 263 mIntent.setAction(Intent.ACTION_SEND_MULTIPLE); 264 if (mStreams != null && !mStreams.isEmpty()) { 265 mIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mStreams); 266 } else { 267 mIntent.removeExtra(Intent.EXTRA_STREAM); 268 } 269 } 270 271 return mIntent; 272 } 273 getActivity()274 Activity getActivity() { 275 return mActivity; 276 } 277 combineArrayExtra(String extra, ArrayList<String> add)278 private void combineArrayExtra(String extra, ArrayList<String> add) { 279 String[] currentAddresses = mIntent.getStringArrayExtra(extra); 280 int currentLength = currentAddresses != null ? currentAddresses.length : 0; 281 String[] finalAddresses = new String[currentLength + add.size()]; 282 add.toArray(finalAddresses); 283 if (currentAddresses != null) { 284 System.arraycopy(currentAddresses, 0, finalAddresses, add.size(), currentLength); 285 } 286 mIntent.putExtra(extra, finalAddresses); 287 } 288 combineArrayExtra(String extra, String[] add)289 private void combineArrayExtra(String extra, String[] add) { 290 // Add any items still pending 291 Intent intent = getIntent(); 292 String[] old = intent.getStringArrayExtra(extra); 293 int oldLength = old != null ? old.length : 0; 294 String[] result = new String[oldLength + add.length]; 295 if (old != null) System.arraycopy(old, 0, result, 0, oldLength); 296 System.arraycopy(add, 0, result, oldLength, add.length); 297 intent.putExtra(extra, result); 298 } 299 300 /** 301 * Create an Intent that will launch the standard Android activity chooser, 302 * allowing the user to pick what activity/app on the system should handle 303 * the share. 304 * 305 * @return A chooser Intent for the currently configured sharing action 306 */ createChooserIntent()307 public Intent createChooserIntent() { 308 return Intent.createChooser(getIntent(), mChooserTitle); 309 } 310 311 /** 312 * Start a chooser activity for the current share intent. 313 * 314 * <p>Note that under most circumstances you should use 315 * {@link ShareCompat#configureMenuItem(MenuItem, IntentBuilder) 316 * ShareCompat.configureMenuItem()} to add a Share item to the menu while 317 * presenting a detail view of the content to be shared instead 318 * of invoking this directly.</p> 319 */ startChooser()320 public void startChooser() { 321 mActivity.startActivity(createChooserIntent()); 322 } 323 324 /** 325 * Set the title that will be used for the activity chooser for this share. 326 * 327 * @param title Title string 328 * @return This IntentBuilder for method chaining 329 */ setChooserTitle(CharSequence title)330 public IntentBuilder setChooserTitle(CharSequence title) { 331 mChooserTitle = title; 332 return this; 333 } 334 335 /** 336 * Set the title that will be used for the activity chooser for this share. 337 * 338 * @param resId Resource ID of the title string to use 339 * @return This IntentBuilder for method chaining 340 */ setChooserTitle(@tringRes int resId)341 public IntentBuilder setChooserTitle(@StringRes int resId) { 342 return setChooserTitle(mActivity.getText(resId)); 343 } 344 345 /** 346 * Set the type of data being shared 347 * 348 * @param mimeType mimetype of the shared data 349 * @return This IntentBuilder for method chaining 350 * @see Intent#setType(String) 351 */ setType(String mimeType)352 public IntentBuilder setType(String mimeType) { 353 mIntent.setType(mimeType); 354 return this; 355 } 356 357 /** 358 * Set the literal text data to be sent as part of the share. 359 * This may be a styled CharSequence. 360 * 361 * @param text Text to share 362 * @return This IntentBuilder for method chaining 363 * @see Intent#EXTRA_TEXT 364 */ setText(CharSequence text)365 public IntentBuilder setText(CharSequence text) { 366 mIntent.putExtra(Intent.EXTRA_TEXT, text); 367 return this; 368 } 369 370 /** 371 * Set an HTML string to be sent as part of the share. 372 * If {@link Intent#EXTRA_TEXT EXTRA_TEXT} has not already been supplied, 373 * a styled version of the supplied HTML text will be added as EXTRA_TEXT as 374 * parsed by {@link android.text.Html#fromHtml(String) Html.fromHtml}. 375 * 376 * @param htmlText A string containing HTML markup as a richer version of the text 377 * provided by EXTRA_TEXT. 378 * @return This IntentBuilder for method chaining 379 * @see #setText(CharSequence) 380 */ setHtmlText(String htmlText)381 public IntentBuilder setHtmlText(String htmlText) { 382 mIntent.putExtra(IntentCompat.EXTRA_HTML_TEXT, htmlText); 383 if (!mIntent.hasExtra(Intent.EXTRA_TEXT)) { 384 // Supply a default if EXTRA_TEXT isn't set 385 setText(Html.fromHtml(htmlText)); 386 } 387 return this; 388 } 389 390 /** 391 * Set a stream URI to the data that should be shared. 392 * 393 * <p>This replaces all currently set stream URIs and will produce a single-stream 394 * ACTION_SEND intent.</p> 395 * 396 * @param streamUri URI of the stream to share 397 * @return This IntentBuilder for method chaining 398 * @see Intent#EXTRA_STREAM 399 */ setStream(Uri streamUri)400 public IntentBuilder setStream(Uri streamUri) { 401 if (!mIntent.getAction().equals(Intent.ACTION_SEND)) { 402 mIntent.setAction(Intent.ACTION_SEND); 403 } 404 mStreams = null; 405 mIntent.putExtra(Intent.EXTRA_STREAM, streamUri); 406 return this; 407 } 408 409 /** 410 * Add a stream URI to the data that should be shared. If this is not the first 411 * stream URI added the final intent constructed will become an ACTION_SEND_MULTIPLE 412 * intent. Not all apps will handle both ACTION_SEND and ACTION_SEND_MULTIPLE. 413 * 414 * @param streamUri URI of the stream to share 415 * @return This IntentBuilder for method chaining 416 * @see Intent#EXTRA_STREAM 417 * @see Intent#ACTION_SEND 418 * @see Intent#ACTION_SEND_MULTIPLE 419 */ addStream(Uri streamUri)420 public IntentBuilder addStream(Uri streamUri) { 421 Uri currentStream = mIntent.getParcelableExtra(Intent.EXTRA_STREAM); 422 if (mStreams == null && currentStream == null) { 423 return setStream(streamUri); 424 } 425 if (mStreams == null) { 426 mStreams = new ArrayList<Uri>(); 427 } 428 if (currentStream != null) { 429 mIntent.removeExtra(Intent.EXTRA_STREAM); 430 mStreams.add(currentStream); 431 } 432 mStreams.add(streamUri); 433 return this; 434 } 435 436 /** 437 * Set an array of email addresses as recipients of this share. 438 * This replaces all current "to" recipients that have been set so far. 439 * 440 * @param addresses Email addresses to send to 441 * @return This IntentBuilder for method chaining 442 * @see Intent#EXTRA_EMAIL 443 */ setEmailTo(String[] addresses)444 public IntentBuilder setEmailTo(String[] addresses) { 445 if (mToAddresses != null) { 446 mToAddresses = null; 447 } 448 mIntent.putExtra(Intent.EXTRA_EMAIL, addresses); 449 return this; 450 } 451 452 /** 453 * Add an email address to be used in the "to" field of the final Intent. 454 * 455 * @param address Email address to send to 456 * @return This IntentBuilder for method chaining 457 * @see Intent#EXTRA_EMAIL 458 */ addEmailTo(String address)459 public IntentBuilder addEmailTo(String address) { 460 if (mToAddresses == null) { 461 mToAddresses = new ArrayList<String>(); 462 } 463 mToAddresses.add(address); 464 return this; 465 } 466 467 /** 468 * Add an array of email addresses to be used in the "to" field of the final Intent. 469 * 470 * @param addresses Email addresses to send to 471 * @return This IntentBuilder for method chaining 472 * @see Intent#EXTRA_EMAIL 473 */ addEmailTo(String[] addresses)474 public IntentBuilder addEmailTo(String[] addresses) { 475 combineArrayExtra(Intent.EXTRA_EMAIL, addresses); 476 return this; 477 } 478 479 /** 480 * Set an array of email addresses to CC on this share. 481 * This replaces all current "CC" recipients that have been set so far. 482 * 483 * @param addresses Email addresses to CC on the share 484 * @return This IntentBuilder for method chaining 485 * @see Intent#EXTRA_CC 486 */ setEmailCc(String[] addresses)487 public IntentBuilder setEmailCc(String[] addresses) { 488 mIntent.putExtra(Intent.EXTRA_CC, addresses); 489 return this; 490 } 491 492 /** 493 * Add an email address to be used in the "cc" field of the final Intent. 494 * 495 * @param address Email address to CC 496 * @return This IntentBuilder for method chaining 497 * @see Intent#EXTRA_CC 498 */ addEmailCc(String address)499 public IntentBuilder addEmailCc(String address) { 500 if (mCcAddresses == null) { 501 mCcAddresses = new ArrayList<String>(); 502 } 503 mCcAddresses.add(address); 504 return this; 505 } 506 507 /** 508 * Add an array of email addresses to be used in the "cc" field of the final Intent. 509 * 510 * @param addresses Email addresses to CC 511 * @return This IntentBuilder for method chaining 512 * @see Intent#EXTRA_CC 513 */ addEmailCc(String[] addresses)514 public IntentBuilder addEmailCc(String[] addresses) { 515 combineArrayExtra(Intent.EXTRA_CC, addresses); 516 return this; 517 } 518 519 /** 520 * Set an array of email addresses to BCC on this share. 521 * This replaces all current "BCC" recipients that have been set so far. 522 * 523 * @param addresses Email addresses to BCC on the share 524 * @return This IntentBuilder for method chaining 525 * @see Intent#EXTRA_BCC 526 */ setEmailBcc(String[] addresses)527 public IntentBuilder setEmailBcc(String[] addresses) { 528 mIntent.putExtra(Intent.EXTRA_BCC, addresses); 529 return this; 530 } 531 532 /** 533 * Add an email address to be used in the "bcc" field of the final Intent. 534 * 535 * @param address Email address to BCC 536 * @return This IntentBuilder for method chaining 537 * @see Intent#EXTRA_BCC 538 */ addEmailBcc(String address)539 public IntentBuilder addEmailBcc(String address) { 540 if (mBccAddresses == null) { 541 mBccAddresses = new ArrayList<String>(); 542 } 543 mBccAddresses.add(address); 544 return this; 545 } 546 547 /** 548 * Add an array of email addresses to be used in the "bcc" field of the final Intent. 549 * 550 * @param addresses Email addresses to BCC 551 * @return This IntentBuilder for method chaining 552 * @see Intent#EXTRA_BCC 553 */ addEmailBcc(String[] addresses)554 public IntentBuilder addEmailBcc(String[] addresses) { 555 combineArrayExtra(Intent.EXTRA_BCC, addresses); 556 return this; 557 } 558 559 /** 560 * Set a subject heading for this share; useful for sharing via email. 561 * 562 * @param subject Subject heading for this share 563 * @return This IntentBuilder for method chaining 564 * @see Intent#EXTRA_SUBJECT 565 */ setSubject(String subject)566 public IntentBuilder setSubject(String subject) { 567 mIntent.putExtra(Intent.EXTRA_SUBJECT, subject); 568 return this; 569 } 570 } 571 572 /** 573 * IntentReader is a helper for reading the data contained within a sharing (ACTION_SEND) 574 * Intent. It provides methods to parse standard elements included with a share 575 * in addition to extra metadata about the app that shared the content. 576 * 577 * <p>Social sharing apps are encouraged to provide attribution for the app that shared 578 * the content. IntentReader offers access to the application label, calling activity info, 579 * and application icon of the app that shared the content. This data may have been provided 580 * voluntarily by the calling app and should always be displayed to the user before submission 581 * for manual verification. The user should be offered the option to omit this information 582 * from shared posts if desired.</p> 583 * 584 * <p>Activities that intend to receive sharing intents should configure an intent-filter 585 * to accept {@link Intent#ACTION_SEND} intents ("android.intent.action.SEND") and optionally 586 * accept {@link Intent#ACTION_SEND_MULTIPLE} ("android.intent.action.SEND_MULTIPLE") if 587 * the activity is equipped to handle multiple data streams.</p> 588 */ 589 public static class IntentReader { 590 private static final String TAG = "IntentReader"; 591 592 private Activity mActivity; 593 private Intent mIntent; 594 private String mCallingPackage; 595 private ComponentName mCallingActivity; 596 597 private ArrayList<Uri> mStreams; 598 599 /** 600 * Get an IntentReader for parsing and interpreting the sharing intent 601 * used to start the given activity. 602 * 603 * @param activity Activity that was started to share content 604 * @return IntentReader for parsing sharing data 605 */ from(Activity activity)606 public static IntentReader from(Activity activity) { 607 return new IntentReader(activity); 608 } 609 IntentReader(Activity activity)610 private IntentReader(Activity activity) { 611 mActivity = activity; 612 mIntent = activity.getIntent(); 613 mCallingPackage = ShareCompat.getCallingPackage(activity); 614 mCallingActivity = ShareCompat.getCallingActivity(activity); 615 } 616 617 /** 618 * Returns true if the activity this reader was obtained for was 619 * started with an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE} 620 * sharing Intent. 621 * 622 * @return true if the activity was started with an ACTION_SEND 623 * or ACTION_SEND_MULTIPLE Intent 624 */ isShareIntent()625 public boolean isShareIntent() { 626 final String action = mIntent.getAction(); 627 return Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action); 628 } 629 630 /** 631 * Returns true if the activity this reader was obtained for was started with an 632 * {@link Intent#ACTION_SEND} intent and contains a single shared item. 633 * The shared content should be obtained using either the {@link #getText()} 634 * or {@link #getStream()} methods depending on the type of content shared. 635 * 636 * @return true if the activity was started with an ACTION_SEND intent 637 */ isSingleShare()638 public boolean isSingleShare() { 639 return Intent.ACTION_SEND.equals(mIntent.getAction()); 640 } 641 642 /** 643 * Returns true if the activity this reader was obtained for was started with an 644 * {@link Intent#ACTION_SEND_MULTIPLE} intent. The Intent may contain more than 645 * one stream item. 646 * 647 * @return true if the activity was started with an ACTION_SEND_MULTIPLE intent 648 */ isMultipleShare()649 public boolean isMultipleShare() { 650 return Intent.ACTION_SEND_MULTIPLE.equals(mIntent.getAction()); 651 } 652 653 /** 654 * Get the mimetype of the data shared to this activity. 655 * 656 * @return mimetype of the shared data 657 * @see Intent#getType() 658 */ getType()659 public String getType() { 660 return mIntent.getType(); 661 } 662 663 /** 664 * Get the literal text shared with the target activity. 665 * 666 * @return Literal shared text or null if none was supplied 667 * @see Intent#EXTRA_TEXT 668 */ getText()669 public CharSequence getText() { 670 return mIntent.getCharSequenceExtra(Intent.EXTRA_TEXT); 671 } 672 673 /** 674 * Get the styled HTML text shared with the target activity. 675 * If no HTML text was supplied but {@link Intent#EXTRA_TEXT} contained 676 * styled text, it will be converted to HTML if possible and returned. 677 * If the text provided by {@link Intent#EXTRA_TEXT} was not styled text, 678 * it will be escaped by {@link android.text.Html#escapeHtml(CharSequence)} 679 * and returned. If no text was provided at all, this method will return null. 680 * 681 * @return Styled text provided by the sender as HTML. 682 */ getHtmlText()683 public String getHtmlText() { 684 String result = mIntent.getStringExtra(IntentCompat.EXTRA_HTML_TEXT); 685 if (result == null) { 686 CharSequence text = getText(); 687 if (text instanceof Spanned) { 688 result = Html.toHtml((Spanned) text); 689 } else if (text != null) { 690 if (SDK_INT >= 16) { 691 result = Html.escapeHtml(text); 692 } else { 693 StringBuilder out = new StringBuilder(); 694 withinStyle(out, text, 0, text.length()); 695 result = out.toString(); 696 } 697 } 698 } 699 return result; 700 } 701 withinStyle(StringBuilder out, CharSequence text, int start, int end)702 private static void withinStyle(StringBuilder out, CharSequence text, 703 int start, int end) { 704 for (int i = start; i < end; i++) { 705 char c = text.charAt(i); 706 707 if (c == '<') { 708 out.append("<"); 709 } else if (c == '>') { 710 out.append(">"); 711 } else if (c == '&') { 712 out.append("&"); 713 } else if (c > 0x7E || c < ' ') { 714 out.append("&#" + ((int) c) + ";"); 715 } else if (c == ' ') { 716 while (i + 1 < end && text.charAt(i + 1) == ' ') { 717 out.append(" "); 718 i++; 719 } 720 721 out.append(' '); 722 } else { 723 out.append(c); 724 } 725 } 726 } 727 728 /** 729 * Get a URI referring to a data stream shared with the target activity. 730 * 731 * <p>This call will fail if the share intent contains multiple stream items. 732 * If {@link #isMultipleShare()} returns true the application should use 733 * {@link #getStream(int)} and {@link #getStreamCount()} to retrieve the 734 * included stream items.</p> 735 * 736 * @return A URI referring to a data stream to be shared or null if one was not supplied 737 * @see Intent#EXTRA_STREAM 738 */ getStream()739 public Uri getStream() { 740 return mIntent.getParcelableExtra(Intent.EXTRA_STREAM); 741 } 742 743 /** 744 * Get the URI of a stream item shared with the target activity. 745 * Index should be in the range [0-getStreamCount()). 746 * 747 * @param index Index of text item to retrieve 748 * @return Requested stream item URI 749 * @see Intent#EXTRA_STREAM 750 * @see Intent#ACTION_SEND_MULTIPLE 751 */ getStream(int index)752 public Uri getStream(int index) { 753 if (mStreams == null && isMultipleShare()) { 754 mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); 755 } 756 if (mStreams != null) { 757 return mStreams.get(index); 758 } 759 if (index == 0) { 760 return mIntent.getParcelableExtra(Intent.EXTRA_STREAM); 761 } 762 throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount() 763 + " index requested: " + index); 764 } 765 766 /** 767 * Return the number of stream items shared. The return value will be 0 or 1 if 768 * this was an {@link Intent#ACTION_SEND} intent, or 0 or more if it was an 769 * {@link Intent#ACTION_SEND_MULTIPLE} intent. 770 * 771 * @return Count of text items contained within the Intent 772 */ getStreamCount()773 public int getStreamCount() { 774 if (mStreams == null && isMultipleShare()) { 775 mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); 776 } 777 if (mStreams != null) { 778 return mStreams.size(); 779 } 780 return mIntent.hasExtra(Intent.EXTRA_STREAM) ? 1 : 0; 781 } 782 783 /** 784 * Get an array of Strings, each an email address to share to. 785 * 786 * @return An array of email addresses or null if none were supplied. 787 * @see Intent#EXTRA_EMAIL 788 */ getEmailTo()789 public String[] getEmailTo() { 790 return mIntent.getStringArrayExtra(Intent.EXTRA_EMAIL); 791 } 792 793 /** 794 * Get an array of Strings, each an email address to CC on this share. 795 * 796 * @return An array of email addresses or null if none were supplied. 797 * @see Intent#EXTRA_CC 798 */ getEmailCc()799 public String[] getEmailCc() { 800 return mIntent.getStringArrayExtra(Intent.EXTRA_CC); 801 } 802 803 /** 804 * Get an array of Strings, each an email address to BCC on this share. 805 * 806 * @return An array of email addresses or null if none were supplied. 807 * @see Intent#EXTRA_BCC 808 */ getEmailBcc()809 public String[] getEmailBcc() { 810 return mIntent.getStringArrayExtra(Intent.EXTRA_BCC); 811 } 812 813 /** 814 * Get a subject heading for this share; useful when sharing via email. 815 * 816 * @return The subject heading for this share or null if one was not supplied. 817 * @see Intent#EXTRA_SUBJECT 818 */ getSubject()819 public String getSubject() { 820 return mIntent.getStringExtra(Intent.EXTRA_SUBJECT); 821 } 822 823 /** 824 * Get the name of the package that invoked this sharing intent. If the activity 825 * was not started for a result, IntentBuilder will read this from extra metadata placed 826 * in the Intent by ShareBuilder. 827 * 828 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 829 * application. As such it should not be trusted for accuracy in the context of 830 * security or verification.</p> 831 * 832 * @return Name of the package that started this activity or null if unknown 833 * @see Activity#getCallingPackage() 834 * @see ShareCompat#EXTRA_CALLING_PACKAGE 835 */ getCallingPackage()836 public String getCallingPackage() { 837 return mCallingPackage; 838 } 839 840 /** 841 * Get the {@link ComponentName} of the Activity that invoked this sharing intent. 842 * If the target sharing activity was not started for a result, IntentBuilder will read 843 * this from extra metadata placed in the intent by ShareBuilder. 844 * 845 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 846 * application. As such it should not be trusted for accuracy in the context of 847 * security or verification.</p> 848 * 849 * @return ComponentName of the calling Activity or null if unknown 850 * @see Activity#getCallingActivity() 851 * @see ShareCompat#EXTRA_CALLING_ACTIVITY 852 */ getCallingActivity()853 public ComponentName getCallingActivity() { 854 return mCallingActivity; 855 } 856 857 /** 858 * Get the icon of the calling activity as a Drawable if data about 859 * the calling activity is available. 860 * 861 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 862 * application. As such it should not be trusted for accuracy in the context of 863 * security or verification.</p> 864 * 865 * @return The calling Activity's icon or null if unknown 866 */ getCallingActivityIcon()867 public Drawable getCallingActivityIcon() { 868 if (mCallingActivity == null) return null; 869 870 PackageManager pm = mActivity.getPackageManager(); 871 try { 872 return pm.getActivityIcon(mCallingActivity); 873 } catch (NameNotFoundException e) { 874 Log.e(TAG, "Could not retrieve icon for calling activity", e); 875 } 876 return null; 877 } 878 879 /** 880 * Get the icon of the calling application as a Drawable if data 881 * about the calling package is available. 882 * 883 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 884 * application. As such it should not be trusted for accuracy in the context of 885 * security or verification.</p> 886 * 887 * @return The calling application's icon or null if unknown 888 */ getCallingApplicationIcon()889 public Drawable getCallingApplicationIcon() { 890 if (mCallingPackage == null) return null; 891 892 PackageManager pm = mActivity.getPackageManager(); 893 try { 894 return pm.getApplicationIcon(mCallingPackage); 895 } catch (NameNotFoundException e) { 896 Log.e(TAG, "Could not retrieve icon for calling application", e); 897 } 898 return null; 899 } 900 901 /** 902 * Get the human-readable label (title) of the calling application if 903 * data about the calling package is available. 904 * 905 * <p><em>Note:</em> This data may have been provided voluntarily by the calling 906 * application. As such it should not be trusted for accuracy in the context of 907 * security or verification.</p> 908 * 909 * @return The calling application's label or null if unknown 910 */ getCallingApplicationLabel()911 public CharSequence getCallingApplicationLabel() { 912 if (mCallingPackage == null) return null; 913 914 PackageManager pm = mActivity.getPackageManager(); 915 try { 916 return pm.getApplicationLabel(pm.getApplicationInfo(mCallingPackage, 0)); 917 } catch (NameNotFoundException e) { 918 Log.e(TAG, "Could not retrieve label for calling application", e); 919 } 920 return null; 921 } 922 } 923 } 924