public final class

ShareCompat

extends java.lang.Object

 java.lang.Object

↳androidx.core.app.ShareCompat

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.9.0-alpha04'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.9.0-alpha04

Artifact androidx.core:core:1.9.0-alpha04 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.app.ShareCompat android.support.v4.app.ShareCompat

Overview

Extra helper functionality for sharing data between activities. ShareCompat provides functionality to extend the / protocol and support retrieving more info about the activity that invoked a social sharing action. ShareCompat.IntentBuilder provides helper functions for constructing a sharing intent that always includes data about the calling activity and app. This lets the called activity provide attribution for the app that shared content. Constructing an intent this way can be done in a method-chaining style. To obtain an IntentBuilder with info about your calling activity, use the static method ShareCompat.IntentBuilder.from(Activity). ShareCompat.IntentReader provides helper functions for parsing the defined extras within an or intent used to launch an activity. You can also obtain a Drawable for the caller's application icon and the application's localized label (the app's human-readable name). Social apps that enable sharing content are encouraged to use this information to call out the app that the content was shared from.

Summary

Fields
public static final java.lang.StringEXTRA_CALLING_ACTIVITY

Intent extra that stores the of the calling activity for an ACTION_SEND intent.

public static final java.lang.StringEXTRA_CALLING_ACTIVITY_INTEROP

Intent extra that stores the of the calling activity for an ACTION_SEND intent.

public static final java.lang.StringEXTRA_CALLING_PACKAGE

Intent extra that stores the name of the calling package for an ACTION_SEND intent.

public static final java.lang.StringEXTRA_CALLING_PACKAGE_INTEROP

Intent extra that stores the name of the calling package for an ACTION_SEND intent.

Methods
public static voidconfigureMenuItem(Menu menu, int menuItemId, ShareCompat.IntentBuilder shareIntent)

Configure a menu item to act as a sharing action.

public static voidconfigureMenuItem(MenuItem item, ShareCompat.IntentBuilder shareIntent)

Configure a MenuItem to act as a sharing action.

public static ComponentNamegetCallingActivity(Activity calledActivity)

Retrieve the ComponentName of the activity that launched calledActivity from a share intent.

public static java.lang.StringgetCallingPackage(Activity calledActivity)

Retrieve the name of the package that launched calledActivity from a share intent.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final java.lang.String EXTRA_CALLING_PACKAGE

Intent extra that stores the name of the calling package for an ACTION_SEND intent. When an activity is started using startActivityForResult this is redundant info. (It is also provided by .) Instead of using this constant directly, consider using ShareCompat.getCallingPackage(Activity) or ShareCompat.IntentReader.getCallingPackage().

public static final java.lang.String EXTRA_CALLING_PACKAGE_INTEROP

Intent extra that stores the name of the calling package for an ACTION_SEND intent. When an activity is started using startActivityForResult this is redundant info. (It is also provided by .) Note that this is only for interoperability between pre-1.0 AndroidX and AndroidX 1.1+ worlds. You are strongly encouraged to use ShareCompat.getCallingPackage(Activity) or ShareCompat.IntentReader.getCallingPackage().

public static final java.lang.String EXTRA_CALLING_ACTIVITY

Intent extra that stores the of the calling activity for an ACTION_SEND intent. Instead of using this constant directly, consider using ShareCompat.getCallingPackage(Activity) or ShareCompat.IntentReader.getCallingPackage().

public static final java.lang.String EXTRA_CALLING_ACTIVITY_INTEROP

Intent extra that stores the of the calling activity for an ACTION_SEND intent. Note that this is only for interoperability between pre-1.0 AndroidX and AndroidX 1.1+ worlds. You are strongly encouraged to use ShareCompat.getCallingPackage(Activity) or ShareCompat.IntentReader.getCallingPackage().

Methods

public static java.lang.String getCallingPackage(Activity calledActivity)

Retrieve the name of the package that launched calledActivity from a share intent. Apps that provide social sharing functionality can use this to provide attribution for the app that shared the content.

Note: This data may have been provided voluntarily by the calling application. As such it should not be trusted for accuracy in the context of security or verification.

Parameters:

calledActivity: Current activity that was launched to share content

Returns:

Name of the calling package

public static ComponentName getCallingActivity(Activity calledActivity)

Retrieve the ComponentName of the activity that launched calledActivity from a share intent. Apps that provide social sharing functionality can use this to provide attribution for the app that shared the content.

Note: This data may have been provided voluntarily by the calling application. As such it should not be trusted for accuracy in the context of security or verification.

Parameters:

calledActivity: Current activity that was launched to share content

Returns:

ComponentName of the calling activity

public static void configureMenuItem(MenuItem item, ShareCompat.IntentBuilder shareIntent)

Deprecated: Use the system sharesheet. See https://developer.android.com/training/sharing/send

Configure a MenuItem to act as a sharing action.

This method will configure a ShareActionProvider to provide a more robust UI for selecting the target of the share. History will be tracked for each calling activity in a file named with the prefix ".sharecompat_" in the application's private data directory. If the application wishes to set this MenuItem to show as an action in the Action Bar it should use MenuItem to request that behavior in addition to calling this method.

During the calling activity's lifecycle, if data within the share intent must change the app should change that state in one of several ways:

  • Call ActivityCompat.invalidateOptionsMenu(Activity). If the app uses the Action Bar its menu will be recreated and rebuilt. If not, the activity will receive a call to the next time the user presses the menu key to open the options menu panel. The activity can then call configureMenuItem again with a new or altered IntentBuilder to reconfigure the share menu item.
  • Keep a reference to the MenuItem object for the share item once it has been created and call configureMenuItem to update the associated sharing intent as needed.

Parameters:

item: MenuItem to configure for sharing
shareIntent: IntentBuilder with data about the content to share

public static void configureMenuItem(Menu menu, int menuItemId, ShareCompat.IntentBuilder shareIntent)

Deprecated: Use the system sharesheet. See https://developer.android.com/training/sharing/send

Configure a menu item to act as a sharing action.

Parameters:

menu: Menu containing the item to use for sharing
menuItemId: ID of the share item within menu
shareIntent: IntentBuilder with data about the content to share

See also: ShareCompat.configureMenuItem(MenuItem, ShareCompat.IntentBuilder)

Source

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.core.app;

import static android.os.Build.VERSION.SDK_INT;

import static androidx.core.util.Preconditions.checkNotNull;

import android.app.Activity;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.view.ActionProvider;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ShareActionProvider;

import androidx.annotation.DoNotInline;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.core.content.IntentCompat;

import java.util.ArrayList;

/**
 * Extra helper functionality for sharing data between activities.
 *
 * ShareCompat provides functionality to extend the {@link Intent#ACTION_SEND}/
 * {@link Intent#ACTION_SEND_MULTIPLE} protocol and support retrieving more info
 * about the activity that invoked a social sharing action.
 *
 * {@link IntentBuilder} provides helper functions for constructing a sharing
 * intent that always includes data about the calling activity and app.
 * This lets the called activity provide attribution for the app that shared
 * content. Constructing an intent this way can be done in a method-chaining style.
 * To obtain an IntentBuilder with info about your calling activity, use the static
 * method {@link IntentBuilder#from(Activity)}.
 *
 * {@link IntentReader} provides helper functions for parsing the defined extras
 * within an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE} intent
 * used to launch an activity. You can also obtain a Drawable for the caller's
 * application icon and the application's localized label (the app's human-readable name).
 * Social apps that enable sharing content are encouraged to use this information
 * to call out the app that the content was shared from.
 */
public final class ShareCompat {
    /**
     * Intent extra that stores the name of the calling package for an ACTION_SEND intent.
     * When an activity is started using startActivityForResult this is redundant info.
     * (It is also provided by {@link Activity#getCallingPackage()}.)
     *
     * Instead of using this constant directly, consider using {@link #getCallingPackage(Activity)}
     * or {@link IntentReader#getCallingPackage()}.
     */
    public static final String EXTRA_CALLING_PACKAGE =
            "androidx.core.app.EXTRA_CALLING_PACKAGE";

    /**
     * Intent extra that stores the name of the calling package for an ACTION_SEND intent.
     * When an activity is started using startActivityForResult this is redundant info.
     * (It is also provided by {@link Activity#getCallingPackage()}.)
     *
     * Note that this is only for interoperability between pre-1.0 AndroidX and AndroidX 1.1+
     * worlds. You are strongly encouraged to use {@link #getCallingPackage(Activity)}
     * or {@link IntentReader#getCallingPackage()}.
     */
    public static final String EXTRA_CALLING_PACKAGE_INTEROP =
            "android.support.v4.app.EXTRA_CALLING_PACKAGE";

    /**
     * Intent extra that stores the {@link ComponentName} of the calling activity for
     * an ACTION_SEND intent.
     *
     * Instead of using this constant directly, consider using {@link #getCallingPackage(Activity)}
     * or {@link IntentReader#getCallingPackage()}.
     */
    public static final String EXTRA_CALLING_ACTIVITY =
            "androidx.core.app.EXTRA_CALLING_ACTIVITY";

    /**
     * Intent extra that stores the {@link ComponentName} of the calling activity for
     * an ACTION_SEND intent.
     *
     * Note that this is only for interoperability between pre-1.0 AndroidX and AndroidX 1.1+
     * worlds. You are strongly encouraged to use {@link #getCallingPackage(Activity)}
     * or {@link IntentReader#getCallingPackage()}.
     */
    public static final String EXTRA_CALLING_ACTIVITY_INTEROP =
            "android.support.v4.app.EXTRA_CALLING_ACTIVITY";

    private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";

    private ShareCompat() {}

    /**
     * Retrieve the name of the package that launched calledActivity from a share intent.
     * Apps that provide social sharing functionality can use this to provide attribution
     * for the app that shared the content.
     *
     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
     * application. As such it should not be trusted for accuracy in the context of
     * security or verification.</p>
     *
     * @param calledActivity Current activity that was launched to share content
     * @return Name of the calling package
     */
    @Nullable
    public static String getCallingPackage(@NonNull Activity calledActivity) {
        Intent intent = calledActivity.getIntent();
        String result = calledActivity.getCallingPackage();
        if (result == null && intent != null) {
            result = getCallingPackage(intent);
        }
        return result;
    }

    /**
     * Retrieve the name of the package that launched intent from a share intent.
     * Apps that provide social sharing functionality can use this to provide attribution
     * for the app that shared the content.
     *
     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
     * application. As such it should not be trusted for accuracy in the context of
     * security or verification.</p>
     *
     * @param intent Intent that was launched to share content
     * @return Name of the calling package
     */
    @SuppressWarnings("WeakerAccess")
    @Nullable
    static String getCallingPackage(@NonNull Intent intent) {
        String result = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
        if (result == null) {
            result = intent.getStringExtra(EXTRA_CALLING_PACKAGE_INTEROP);
        }
        return result;
    }

    /**
     * Retrieve the ComponentName of the activity that launched calledActivity from a share intent.
     * Apps that provide social sharing functionality can use this to provide attribution
     * for the app that shared the content.
     *
     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
     * application. As such it should not be trusted for accuracy in the context of
     * security or verification.</p>
     *
     * @param calledActivity Current activity that was launched to share content
     * @return ComponentName of the calling activity
     */
    @Nullable
    public static ComponentName getCallingActivity(@NonNull Activity calledActivity) {
        Intent intent = calledActivity.getIntent();
        ComponentName result = calledActivity.getCallingActivity();
        if (result == null) {
            result = getCallingActivity(intent);
        }
        return result;
    }

    /**
     * Retrieve the ComponentName of the activity that launched intent from a share intent.
     * Apps that provide social sharing functionality can use this to provide attribution
     * for the app that shared the content.
     *
     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
     * application. As such it should not be trusted for accuracy in the context of
     * security or verification.</p>
     *
     * @param intent Intent that was launched to share content
     * @return ComponentName of the calling activity
     */
    @SuppressWarnings({"WeakerAccess", "deprecation"})
    @Nullable
    static ComponentName getCallingActivity(@NonNull Intent intent) {
        ComponentName result = intent.getParcelableExtra(EXTRA_CALLING_ACTIVITY);
        if (result == null) {
            result = intent.getParcelableExtra(EXTRA_CALLING_ACTIVITY_INTEROP);
        }
        return result;
    }

    /**
     * Configure a {@link MenuItem} to act as a sharing action.
     *
     * <p>This method will configure a ShareActionProvider to provide a more robust UI
     * for selecting the target of the share. History will be tracked for each calling
     * activity in a file named with the prefix ".sharecompat_" in the application's
     * private data directory. If the application wishes to set this MenuItem to show
     * as an action in the Action Bar it should use {@link MenuItem#setShowAsAction(int)} to request
     * that behavior in addition to calling this method.</p>
     *
     * <p>During the calling activity's lifecycle, if data within the share intent must
     * change the app should change that state in one of several ways:</p>
     * <ul>
     * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app uses the
     * Action Bar its menu will be recreated and rebuilt.
     * If not, the activity will receive a call to {@link Activity#onPrepareOptionsMenu(Menu)}
     * the next time the user presses the menu key to open the options menu panel. The activity
     * can then call configureMenuItem again with a new or altered IntentBuilder to reconfigure
     * the share menu item.</li>
     * <li>Keep a reference to the MenuItem object for the share item once it has been created
     * and call configureMenuItem to update the associated sharing intent as needed.</li>
     * </ul>
     *
     * @param item MenuItem to configure for sharing
     * @param shareIntent IntentBuilder with data about the content to share
     *
     * @deprecated Use the system sharesheet. See https://developer.android.com/training/sharing/send
     */
    @Deprecated
    public static void configureMenuItem(@NonNull MenuItem item,
            @NonNull IntentBuilder shareIntent) {
        ActionProvider itemProvider = item.getActionProvider();
        ShareActionProvider provider;
        if (!(itemProvider instanceof ShareActionProvider)) {
            provider = new ShareActionProvider(shareIntent.getContext());
        } else {
            provider = (ShareActionProvider) itemProvider;
        }
        provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX
                + shareIntent.getContext().getClass().getName());
        provider.setShareIntent(shareIntent.getIntent());
        item.setActionProvider(provider);

        if (SDK_INT < 16) {
            if (!item.hasSubMenu()) {
                item.setIntent(shareIntent.createChooserIntent());
            }
        }
    }

    /**
     * Configure a menu item to act as a sharing action.
     *
     * @param menu Menu containing the item to use for sharing
     * @param menuItemId ID of the share item within menu
     * @param shareIntent IntentBuilder with data about the content to share
     * @see #configureMenuItem(MenuItem, IntentBuilder)
     *
     * @deprecated Use the system sharesheet. See https://developer.android.com/training/sharing/send
     */
    @SuppressWarnings("deprecation")
    @Deprecated
    public static void configureMenuItem(@NonNull Menu menu, @IdRes int menuItemId,
            @NonNull IntentBuilder shareIntent) {
        MenuItem item = menu.findItem(menuItemId);
        if (item == null) {
            throw new IllegalArgumentException("Could not find menu item with id " + menuItemId
                    + " in the supplied menu");
        }
        configureMenuItem(item, shareIntent);
    }

    /**
     * IntentBuilder is a helper for constructing {@link Intent#ACTION_SEND} and
     * {@link Intent#ACTION_SEND_MULTIPLE} sharing intents and starting activities
     * to share content. The ComponentName and package name of the calling activity
     * will be included.
     */
    public static class IntentBuilder {
        private final @NonNull Context mContext;
        private final @NonNull Intent mIntent;

        private @Nullable CharSequence mChooserTitle;
        private @Nullable ArrayList<String> mToAddresses;
        private @Nullable ArrayList<String> mCcAddresses;
        private @Nullable ArrayList<String> mBccAddresses;
        private @Nullable ArrayList<Uri> mStreams;

        /**
         * Create a new IntentBuilder for launching a sharing action from launchingActivity.
         *
         * @param launchingActivity Activity that the share will be launched from
         * @return a new IntentBuilder instance
         * @deprecated Use the constructor of IntentBuilder
         */
        @NonNull
        @Deprecated
        public static IntentBuilder from(@NonNull Activity launchingActivity) {
            return new IntentBuilder(launchingActivity);
        }

        /**
         * Create a new IntentBuilder for launching a sharing action from launchingContext.
         *
         * <p>Note that builders that are not constructed with an {@link Activity} context
         * (or a wrapped activity context) will not set the
         * {@link #EXTRA_CALLING_ACTIVITY calling activity} on the returned intent.</p>
         *
         * @param launchingContext Context that the share will be launched from
         */
        public IntentBuilder(@NonNull Context launchingContext) {
            mContext = checkNotNull(launchingContext);
            mIntent = new Intent().setAction(Intent.ACTION_SEND);
            mIntent.putExtra(EXTRA_CALLING_PACKAGE, launchingContext.getPackageName());
            mIntent.putExtra(EXTRA_CALLING_PACKAGE_INTEROP, launchingContext.getPackageName());
            mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);

            Activity activity = null;
            Context context = launchingContext;
            while (context instanceof ContextWrapper) {
                if (context instanceof Activity) {
                    activity = (Activity) context;
                    break;
                }
                context = ((ContextWrapper) context).getBaseContext();
            }

            if (activity != null) {
                ComponentName componentName = activity.getComponentName();
                mIntent.putExtra(EXTRA_CALLING_ACTIVITY, componentName);
                mIntent.putExtra(EXTRA_CALLING_ACTIVITY_INTEROP, componentName);
            }
        }

        /**
         * Retrieve the Intent as configured so far by the IntentBuilder. This Intent
         * is suitable for use in a ShareActionProvider or chooser dialog.
         *
         * <p>To create an intent that will launch the activity chooser so that the user
         * may select a target for the share, see {@link #createChooserIntent()}.
         *
         * @return The current Intent being configured by this builder
         */
        @NonNull
        public Intent getIntent() {
            if (mToAddresses != null) {
                combineArrayExtra(Intent.EXTRA_EMAIL, mToAddresses);
                mToAddresses = null;
            }
            if (mCcAddresses != null) {
                combineArrayExtra(Intent.EXTRA_CC, mCcAddresses);
                mCcAddresses = null;
            }
            if (mBccAddresses != null) {
                combineArrayExtra(Intent.EXTRA_BCC, mBccAddresses);
                mBccAddresses = null;
            }

            boolean needsSendMultiple = mStreams != null && mStreams.size() > 1;

            if (!needsSendMultiple) {
                mIntent.setAction(Intent.ACTION_SEND);
                if (mStreams != null && !mStreams.isEmpty()) {
                    mIntent.putExtra(Intent.EXTRA_STREAM, mStreams.get(0));
                    if (SDK_INT >= 16) {
                        Api16Impl.migrateExtraStreamToClipData(mIntent, mStreams);
                    }
                } else {
                    mIntent.removeExtra(Intent.EXTRA_STREAM);
                    if (SDK_INT >= 16) {
                        Api16Impl.removeClipData(mIntent);
                    }
                }
            } else {
                mIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
                mIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mStreams);
                if (SDK_INT >= 16) {
                    Api16Impl.migrateExtraStreamToClipData(mIntent, mStreams);
                }
            }

            return mIntent;
        }

        @NonNull
        Context getContext() {
            return mContext;
        }

        private void combineArrayExtra(String extra, ArrayList<String> add) {
            String[] currentAddresses = mIntent.getStringArrayExtra(extra);
            int currentLength = currentAddresses != null ? currentAddresses.length : 0;
            String[] finalAddresses = new String[currentLength + add.size()];
            add.toArray(finalAddresses);
            if (currentAddresses != null) {
                System.arraycopy(currentAddresses, 0, finalAddresses, add.size(), currentLength);
            }
            mIntent.putExtra(extra, finalAddresses);
        }

        private void combineArrayExtra(@Nullable String extra, @NonNull String[] add) {
            // Add any items still pending
            Intent intent = getIntent();
            String[] old = intent.getStringArrayExtra(extra);
            int oldLength = old != null ? old.length : 0;
            String[] result = new String[oldLength + add.length];
            if (old != null) System.arraycopy(old, 0, result, 0, oldLength);
            System.arraycopy(add, 0, result, oldLength, add.length);
            intent.putExtra(extra, result);
        }

        /**
         * Create an Intent that will launch the standard Android activity chooser,
         * allowing the user to pick what activity/app on the system should handle
         * the share.
         *
         * @return A chooser Intent for the currently configured sharing action
         */
        @NonNull
        public Intent createChooserIntent() {
            return Intent.createChooser(getIntent(), mChooserTitle);
        }

        /**
         * Start a chooser activity for the current share intent.
         */
        public void startChooser() {
            mContext.startActivity(createChooserIntent());
        }

        /**
         * Set the title that will be used for the activity chooser for this share.
         *
         * @param title Title string
         * @return This IntentBuilder for method chaining
         */
        @NonNull
        public IntentBuilder setChooserTitle(@Nullable CharSequence title) {
            mChooserTitle = title;
            return this;
        }

        /**
         * Set the title that will be used for the activity chooser for this share.
         *
         * @param resId Resource ID of the title string to use
         * @return This IntentBuilder for method chaining
         */
        @NonNull
        public IntentBuilder setChooserTitle(@StringRes int resId) {
            return setChooserTitle(mContext.getText(resId));
        }

        /**
         * Set the type of data being shared
         *
         * @param mimeType mimetype of the shared data
         * @return This IntentBuilder for method chaining
         * @see Intent#setType(String)
         */
        @NonNull
        public IntentBuilder setType(@Nullable String mimeType) {
            mIntent.setType(mimeType);
            return this;
        }

        /**
         * Set the literal text data to be sent as part of the share.
         * This may be a styled CharSequence.
         *
         * @param text Text to share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_TEXT
         */
        @NonNull
        public IntentBuilder setText(@Nullable CharSequence text) {
            mIntent.putExtra(Intent.EXTRA_TEXT, text);
            return this;
        }

        /**
         * Set an HTML string to be sent as part of the share.
         * If {@link Intent#EXTRA_TEXT EXTRA_TEXT} has not already been supplied,
         * a styled version of the supplied HTML text will be added as EXTRA_TEXT as
         * parsed by {@link android.text.Html#fromHtml(String) Html.fromHtml}.
         *
         * @param htmlText A string containing HTML markup as a richer version of the text
         *                 provided by EXTRA_TEXT.
         * @return This IntentBuilder for method chaining
         * @see #setText(CharSequence)
         */
        @NonNull
        public IntentBuilder setHtmlText(@Nullable String htmlText) {
            mIntent.putExtra(IntentCompat.EXTRA_HTML_TEXT, htmlText);
            if (!mIntent.hasExtra(Intent.EXTRA_TEXT)) {
                // Supply a default if EXTRA_TEXT isn't set
                setText(Html.fromHtml(htmlText));
            }
            return this;
        }

        /**
         * Set a stream URI to the data that should be shared.
         *
         * <p>This replaces all currently set stream URIs and will produce a single-stream
         * ACTION_SEND intent.</p>
         *
         * @param streamUri URI of the stream to share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_STREAM
         */
        @NonNull
        public IntentBuilder setStream(@Nullable Uri streamUri) {
            mStreams = null;
            if (streamUri != null) {
                addStream(streamUri);
            }
            return this;
        }

        /**
         * Add a stream URI to the data that should be shared. If this is not the first
         * stream URI added the final intent constructed will become an ACTION_SEND_MULTIPLE
         * intent. Not all apps will handle both ACTION_SEND and ACTION_SEND_MULTIPLE.
         *
         * @param streamUri URI of the stream to share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_STREAM
         * @see Intent#ACTION_SEND
         * @see Intent#ACTION_SEND_MULTIPLE
         */
        @NonNull
        public IntentBuilder addStream(@NonNull Uri streamUri) {
            if (mStreams == null) {
                mStreams = new ArrayList<>();
            }
            mStreams.add(streamUri);
            return this;
        }

        /**
         * Set an array of email addresses as recipients of this share.
         * This replaces all current "to" recipients that have been set so far.
         *
         * @param addresses Email addresses to send to
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_EMAIL
         */
        @NonNull
        public IntentBuilder setEmailTo(@Nullable String[] addresses) {
            if (mToAddresses != null) {
                mToAddresses = null;
            }
            mIntent.putExtra(Intent.EXTRA_EMAIL, addresses);
            return this;
        }

        /**
         * Add an email address to be used in the "to" field of the final Intent.
         *
         * @param address Email address to send to
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_EMAIL
         */
        @NonNull
        public IntentBuilder addEmailTo(@NonNull String address) {
            if (mToAddresses == null) {
                mToAddresses = new ArrayList<>();
            }
            mToAddresses.add(address);
            return this;
        }

        /**
         * Add an array of email addresses to be used in the "to" field of the final Intent.
         *
         * @param addresses Email addresses to send to
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_EMAIL
         */
        @NonNull
        public IntentBuilder addEmailTo(@NonNull String[] addresses) {
            combineArrayExtra(Intent.EXTRA_EMAIL, addresses);
            return this;
        }

        /**
         * Set an array of email addresses to CC on this share.
         * This replaces all current "CC" recipients that have been set so far.
         *
         * @param addresses Email addresses to CC on the share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_CC
         */
        @NonNull
        public IntentBuilder setEmailCc(@Nullable String[] addresses) {
            mIntent.putExtra(Intent.EXTRA_CC, addresses);
            return this;
        }

        /**
         * Add an email address to be used in the "cc" field of the final Intent.
         *
         * @param address Email address to CC
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_CC
         */
        @NonNull
        public IntentBuilder addEmailCc(@NonNull String address) {
            if (mCcAddresses == null) {
                mCcAddresses = new ArrayList<>();
            }
            mCcAddresses.add(address);
            return this;
        }

        /**
         * Add an array of email addresses to be used in the "cc" field of the final Intent.
         *
         * @param addresses Email addresses to CC
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_CC
         */
        @NonNull
        public IntentBuilder addEmailCc(@NonNull String[] addresses) {
            combineArrayExtra(Intent.EXTRA_CC, addresses);
            return this;
        }

        /**
         * Set an array of email addresses to BCC on this share.
         * This replaces all current "BCC" recipients that have been set so far.
         *
         * @param addresses Email addresses to BCC on the share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_BCC
         */
        @NonNull
        public IntentBuilder setEmailBcc(@Nullable String[] addresses) {
            mIntent.putExtra(Intent.EXTRA_BCC, addresses);
            return this;
        }

        /**
         * Add an email address to be used in the "bcc" field of the final Intent.
         *
         * @param address Email address to BCC
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_BCC
         */
        @NonNull
        public IntentBuilder addEmailBcc(@NonNull String address) {
            if (mBccAddresses == null) {
                mBccAddresses = new ArrayList<>();
            }
            mBccAddresses.add(address);
            return this;
        }

        /**
         * Add an array of email addresses to be used in the "bcc" field of the final Intent.
         *
         * @param addresses Email addresses to BCC
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_BCC
         */
        @NonNull
        public IntentBuilder addEmailBcc(@NonNull String[] addresses) {
            combineArrayExtra(Intent.EXTRA_BCC, addresses);
            return this;
        }

        /**
         * Set a subject heading for this share; useful for sharing via email.
         *
         * @param subject Subject heading for this share
         * @return This IntentBuilder for method chaining
         * @see Intent#EXTRA_SUBJECT
         */
        @NonNull
        public IntentBuilder setSubject(@Nullable String subject) {
            mIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
            return this;
        }
    }

    /**
     * IntentReader is a helper for reading the data contained within a sharing (ACTION_SEND)
     * Intent. It provides methods to parse standard elements included with a share
     * in addition to extra metadata about the app that shared the content.
     *
     * <p>Social sharing apps are encouraged to provide attribution for the app that shared
     * the content. IntentReader offers access to the application label, calling activity info,
     * and application icon of the app that shared the content. This data may have been provided
     * voluntarily by the calling app and should always be displayed to the user before submission
     * for manual verification. The user should be offered the option to omit this information
     * from shared posts if desired.</p>
     *
     * <p>Activities that intend to receive sharing intents should configure an intent-filter
     * to accept {@link Intent#ACTION_SEND} intents ("android.intent.action.SEND") and optionally
     * accept {@link Intent#ACTION_SEND_MULTIPLE} ("android.intent.action.SEND_MULTIPLE") if
     * the activity is equipped to handle multiple data streams.</p>
     */
    public static class IntentReader {
        private static final String TAG = "IntentReader";

        private final @NonNull Context mContext;
        private final @NonNull Intent mIntent;
        private final @Nullable String mCallingPackage;
        private final @Nullable ComponentName mCallingActivity;

        private @Nullable ArrayList<Uri> mStreams;

        /**
         * Get an IntentReader for parsing and interpreting the sharing intent
         * used to start the given activity.
         *
         * @param activity Activity that was started to share content
         * @return IntentReader for parsing sharing data
         * @deprecated Use the constructor of IntentReader instead
         */
        @NonNull
        @Deprecated
        public static IntentReader from(@NonNull Activity activity) {
            return new IntentReader(activity);
        }

        /**
         * Create an IntentReader for parsing and interpreting the sharing intent
         * used to start the given activity.
         *
         * @param activity Activity that was started to share content
         */
        public IntentReader(@NonNull Activity activity) {
            this(checkNotNull(activity), activity.getIntent());
        }


        /**
         * Create an IntentReader for parsing and interpreting the given sharing intent.
         *
         * @param context Context that was started to share content
         * @param intent Intent that was used to start the context
         */
        public IntentReader(@NonNull Context context, @NonNull Intent intent) {
            mContext = checkNotNull(context);
            mIntent = checkNotNull(intent);
            mCallingPackage = ShareCompat.getCallingPackage(intent);
            mCallingActivity = ShareCompat.getCallingActivity(intent);
        }

        /**
         * Returns true if the activity this reader was obtained for was
         * started with an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE}
         * sharing Intent.
         *
         * @return true if the activity was started with an ACTION_SEND
         *         or ACTION_SEND_MULTIPLE Intent
         */
        public boolean isShareIntent() {
            final String action = mIntent.getAction();
            return Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action);
        }

        /**
         * Returns true if the activity this reader was obtained for was started with an
         * {@link Intent#ACTION_SEND} intent and contains a single shared item.
         * The shared content should be obtained using either the {@link #getText()}
         * or {@link #getStream()} methods depending on the type of content shared.
         *
         * @return true if the activity was started with an ACTION_SEND intent
         */
        public boolean isSingleShare() {
            return Intent.ACTION_SEND.equals(mIntent.getAction());
        }

        /**
         * Returns true if the activity this reader was obtained for was started with an
         * {@link Intent#ACTION_SEND_MULTIPLE} intent. The Intent may contain more than
         * one stream item.
         *
         * @return true if the activity was started with an ACTION_SEND_MULTIPLE intent
         */
        public boolean isMultipleShare() {
            return Intent.ACTION_SEND_MULTIPLE.equals(mIntent.getAction());
        }

        /**
         * Get the mimetype of the data shared to this activity.
         *
         * @return mimetype of the shared data
         * @see Intent#getType()
         */
        @Nullable
        public String getType() {
            return mIntent.getType();
        }

        /**
         * Get the literal text shared with the target activity.
         *
         * @return Literal shared text or null if none was supplied
         * @see Intent#EXTRA_TEXT
         */
        @Nullable
        public CharSequence getText() {
            return mIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
        }

        /**
         * Get the styled HTML text shared with the target activity.
         * If no HTML text was supplied but {@link Intent#EXTRA_TEXT} contained
         * styled text, it will be converted to HTML if possible and returned.
         * If the text provided by {@link Intent#EXTRA_TEXT} was not styled text,
         * it will be escaped by {@link android.text.Html#escapeHtml(CharSequence)}
         * and returned. If no text was provided at all, this method will return null.
         *
         * @return Styled text provided by the sender as HTML.
         */
        @Nullable
        public String getHtmlText() {
            String result = mIntent.getStringExtra(IntentCompat.EXTRA_HTML_TEXT);
            if (result == null) {
                CharSequence text = getText();
                if (text instanceof Spanned) {
                    result = Html.toHtml((Spanned) text);
                } else if (text != null) {
                    if (SDK_INT >= 16) {
                        result = Api16Impl.escapeHtml(text);
                    } else {
                        StringBuilder out = new StringBuilder();
                        withinStyle(out, text, 0, text.length());
                        result = out.toString();
                    }
                }
            }
            return result;
        }

        @SuppressWarnings("SameParameterValue")
        private static void withinStyle(StringBuilder out, CharSequence text, int start, int end) {
            for (int i = start; i < end; i++) {
                char c = text.charAt(i);

                if (c == '<') {
                    out.append("&lt;");
                } else if (c == '>') {
                    out.append("&gt;");
                } else if (c == '&') {
                    out.append("&amp;");
                } else if (c > 0x7E || c < ' ') {
                    out.append("&#").append((int) c).append(";");
                } else if (c == ' ') {
                    while (i + 1 < end && text.charAt(i + 1) == ' ') {
                        out.append("&nbsp;");
                        i++;
                    }

                    out.append(' ');
                } else {
                    out.append(c);
                }
            }
        }

        /**
         * Get a URI referring to a data stream shared with the target activity.
         *
         * <p>This call will fail if the share intent contains multiple stream items.
         * If {@link #isMultipleShare()} returns true the application should use
         * {@link #getStream(int)} and {@link #getStreamCount()} to retrieve the
         * included stream items.</p>
         *
         * @return A URI referring to a data stream to be shared or null if one was not supplied
         * @see Intent#EXTRA_STREAM
         */
        @SuppressWarnings("deprecation")
        @Nullable
        public Uri getStream() {
            return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
        }

        /**
         * Get the URI of a stream item shared with the target activity.
         * Index should be in the range [0-getStreamCount()).
         *
         * @param index Index of text item to retrieve
         * @return Requested stream item URI
         * @see Intent#EXTRA_STREAM
         * @see Intent#ACTION_SEND_MULTIPLE
         */
        @SuppressWarnings("deprecation")
        @Nullable
        public Uri getStream(int index) {
            if (mStreams == null && isMultipleShare()) {
                mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
            }
            if (mStreams != null) {
                return mStreams.get(index);
            }
            if (index == 0) {
                return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
            }
            throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount()
                    + " index requested: " + index);
        }

        /**
         * Return the number of stream items shared. The return value will be 0 or 1 if
         * this was an {@link Intent#ACTION_SEND} intent, or 0 or more if it was an
         * {@link Intent#ACTION_SEND_MULTIPLE} intent.
         *
         * @return Count of text items contained within the Intent
         */
        @SuppressWarnings("deprecation")
        public int getStreamCount() {
            if (mStreams == null && isMultipleShare()) {
                mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
            }
            if (mStreams != null) {
                return mStreams.size();
            }
            return mIntent.hasExtra(Intent.EXTRA_STREAM) ? 1 : 0;
        }

        /**
         * Get an array of Strings, each an email address to share to.
         *
         * @return An array of email addresses or null if none were supplied.
         * @see Intent#EXTRA_EMAIL
         */
        @Nullable
        public String[] getEmailTo() {
            return mIntent.getStringArrayExtra(Intent.EXTRA_EMAIL);
        }

        /**
         * Get an array of Strings, each an email address to CC on this share.
         *
         * @return An array of email addresses or null if none were supplied.
         * @see Intent#EXTRA_CC
         */
        @Nullable
        public String[] getEmailCc() {
            return mIntent.getStringArrayExtra(Intent.EXTRA_CC);
        }

        /**
         * Get an array of Strings, each an email address to BCC on this share.
         *
         * @return An array of email addresses or null if none were supplied.
         * @see Intent#EXTRA_BCC
         */
        @Nullable
        public String[] getEmailBcc() {
            return mIntent.getStringArrayExtra(Intent.EXTRA_BCC);
        }

        /**
         * Get a subject heading for this share; useful when sharing via email.
         *
         * @return The subject heading for this share or null if one was not supplied.
         * @see Intent#EXTRA_SUBJECT
         */
        @Nullable
        public String getSubject() {
            return mIntent.getStringExtra(Intent.EXTRA_SUBJECT);
        }

        /**
         * Get the name of the package that invoked this sharing intent. If the activity
         * was not started for a result, IntentBuilder will read this from extra metadata placed
         * in the Intent by ShareBuilder.
         *
         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
         * application. As such it should not be trusted for accuracy in the context of
         * security or verification.</p>
         *
         * @return Name of the package that started this activity or null if unknown
         * @see Activity#getCallingPackage()
         * @see ShareCompat#EXTRA_CALLING_PACKAGE
         * @see ShareCompat#EXTRA_CALLING_PACKAGE_INTEROP
         */
        @Nullable
        public String getCallingPackage() {
            return mCallingPackage;
        }

        /**
         * Get the {@link ComponentName} of the Activity that invoked this sharing intent.
         * If the target sharing activity was not started for a result, IntentBuilder will read
         * this from extra metadata placed in the intent by ShareBuilder.
         *
         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
         * application. As such it should not be trusted for accuracy in the context of
         * security or verification.</p>
         *
         * @return ComponentName of the calling Activity or null if unknown
         * @see Activity#getCallingActivity()
         * @see ShareCompat#EXTRA_CALLING_ACTIVITY
         * @see ShareCompat#EXTRA_CALLING_ACTIVITY_INTEROP
         */
        @Nullable
        public ComponentName getCallingActivity() {
            return mCallingActivity;
        }

        /**
         * Get the icon of the calling activity as a Drawable if data about
         * the calling activity is available.
         *
         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
         * application. As such it should not be trusted for accuracy in the context of
         * security or verification.</p>
         *
         * @return The calling Activity's icon or null if unknown
         */
        @Nullable
        public Drawable getCallingActivityIcon() {
            if (mCallingActivity == null) return null;

            PackageManager pm = mContext.getPackageManager();
            try {
                return pm.getActivityIcon(mCallingActivity);
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Could not retrieve icon for calling activity", e);
            }
            return null;
        }

        /**
         * Get the icon of the calling application as a Drawable if data
         * about the calling package is available.
         *
         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
         * application. As such it should not be trusted for accuracy in the context of
         * security or verification.</p>
         *
         * @return The calling application's icon or null if unknown
         */
        @Nullable
        public Drawable getCallingApplicationIcon() {
            if (mCallingPackage == null) return null;

            PackageManager pm = mContext.getPackageManager();
            try {
                return pm.getApplicationIcon(mCallingPackage);
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Could not retrieve icon for calling application", e);
            }
            return null;
        }

        /**
         * Get the human-readable label (title) of the calling application if
         * data about the calling package is available.
         *
         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
         * application. As such it should not be trusted for accuracy in the context of
         * security or verification.</p>
         *
         * @return The calling application's label or null if unknown
         */
        @SuppressWarnings("deprecation")
        @Nullable
        public CharSequence getCallingApplicationLabel() {
            if (mCallingPackage == null) return null;

            PackageManager pm = mContext.getPackageManager();
            try {
                return pm.getApplicationLabel(pm.getApplicationInfo(mCallingPackage, 0));
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Could not retrieve label for calling application", e);
            }
            return null;
        }
    }

    @RequiresApi(16)
    private static class Api16Impl {
        private Api16Impl() {
            // This class is not instantiable.
        }

        @DoNotInline
        static void migrateExtraStreamToClipData(@NonNull Intent intent,
                @NonNull ArrayList<Uri> streams) {
            CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
            String htmlText = intent.getStringExtra(IntentCompat.EXTRA_HTML_TEXT);

            ClipData clipData = new ClipData(
                    null, new String[] { intent.getType() },
                    new ClipData.Item(text, htmlText, null, streams.get(0)));

            for (int i = 1, end = streams.size(); i < end; i++) {
                Uri uri = streams.get(i);
                clipData.addItem(new ClipData.Item(uri));
            }

            intent.setClipData(clipData);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        @DoNotInline
        static void removeClipData(@NonNull Intent intent) {
            intent.setClipData(null);
            intent.setFlags(intent.getFlags() & ~Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        @DoNotInline
        static String escapeHtml(CharSequence text) {
            return Html.escapeHtml(text);
        }
    }
}