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