/* * 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 android.support.v4.app; import android.app.Activity; import android.content.ComponentName; 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.os.Build; import android.support.v4.content.IntentCompat; import android.support.v4.view.MenuItemCompat; import android.text.Html; import android.text.Spanned; import android.util.Log; import android.view.Menu; import android.view.MenuItem; 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 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 = "android.support.v4.app.EXTRA_CALLING_PACKAGE"; /** * Intent extra that stores the {@link ComponentName} of the calling activity for * an ACTION_SEND intent. */ public static final String EXTRA_CALLING_ACTIVITY = "android.support.v4.app.EXTRA_CALLING_ACTIVITY"; /** * Compatibility shims for sharing operations */ interface ShareCompatImpl { void configureMenuItem(MenuItem item, IntentBuilder shareIntent); String escapeHtml(CharSequence text); } static class ShareCompatImplBase implements ShareCompatImpl { public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) { item.setIntent(shareIntent.createChooserIntent()); } public String escapeHtml(CharSequence text) { StringBuilder out = new StringBuilder(); withinStyle(out, text, 0, text.length()); return out.toString(); } 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("<"); } else if (c == '>') { out.append(">"); } else if (c == '&') { out.append("&"); } else if (c > 0x7E || c < ' ') { out.append("" + ((int) c) + ";"); } else if (c == ' ') { while (i + 1 < end && text.charAt(i + 1) == ' ') { out.append(" "); i++; } out.append(' '); } else { out.append(c); } } } } static class ShareCompatImplICS extends ShareCompatImplBase { public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) { ShareCompatICS.configureMenuItem(item, shareIntent.getActivity(), shareIntent.getIntent()); if (shouldAddChooserIntent(item)) { item.setIntent(shareIntent.createChooserIntent()); } } boolean shouldAddChooserIntent(MenuItem item) { return !item.hasSubMenu(); } } static class ShareCompatImplJB extends ShareCompatImplICS { public String escapeHtml(CharSequence html) { return ShareCompatJB.escapeHtml(html); } @Override boolean shouldAddChooserIntent(MenuItem item) { return false; } } private static ShareCompatImpl IMPL; static { if (Build.VERSION.SDK_INT >= 16) { IMPL = new ShareCompatImplJB(); } else if (Build.VERSION.SDK_INT >= 14) { IMPL = new ShareCompatImplICS(); } else { IMPL = new ShareCompatImplBase(); } } /** * 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.
* * @param calledActivity Current activity that was launched to share content * @return Name of the calling package */ public static String getCallingPackage(Activity calledActivity) { String result = calledActivity.getCallingPackage(); if (result == null) { result = calledActivity.getIntent().getStringExtra(EXTRA_CALLING_PACKAGE); } 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. * *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.
* * @param calledActivity Current activity that was launched to share content * @return ComponentName of the calling activity */ public static ComponentName getCallingActivity(Activity calledActivity) { ComponentName result = calledActivity.getCallingActivity(); if (result == null) { result = calledActivity.getIntent().getParcelableExtra(EXTRA_CALLING_ACTIVITY); } return result; } /** * Configure a {@link MenuItem} to act as a sharing action. * *If the app is running on API level 14 or higher (Android 4.0/Ice Cream Sandwich) * 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 MenuItemCompat#setShowAsAction(MenuItem, int)} to request that behavior * in addition to calling this method.
* *If the app is running on an older platform version this method will configure * a standard activity chooser dialog for the menu item.
* *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:
*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
*/
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;
}
// Check if we need to change the action.
boolean needsSendMultiple = mStreams != null && mStreams.size() > 1;
boolean isSendMultiple = mIntent.getAction().equals(Intent.ACTION_SEND_MULTIPLE);
if (!needsSendMultiple && isSendMultiple) {
// Change back to a single send action; place the first stream into the
// intent for single sharing.
mIntent.setAction(Intent.ACTION_SEND);
if (mStreams != null && !mStreams.isEmpty()) {
mIntent.putExtra(Intent.EXTRA_STREAM, mStreams.get(0));
} else {
mIntent.removeExtra(Intent.EXTRA_STREAM);
}
mStreams = null;
}
if (needsSendMultiple && !isSendMultiple) {
// Change to a multiple send action; place the relevant ArrayList into the
// intent for multiple sharing.
mIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
if (mStreams != null && !mStreams.isEmpty()) {
mIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mStreams);
} else {
mIntent.removeExtra(Intent.EXTRA_STREAM);
}
}
return mIntent;
}
Activity getActivity() {
return mActivity;
}
private void combineArrayExtra(String extra, ArrayList Note that under most circumstances you should use
* {@link ShareCompat#configureMenuItem(MenuItem, IntentBuilder)
* ShareCompat.configureMenuItem()} to add a Share item to the menu while
* presenting a detail view of the content to be shared instead
* of invoking this directly. This replaces all currently set stream URIs and will produce a single-stream
* ACTION_SEND intent. 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. 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. 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. 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. 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. 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. 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. 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.