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