/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.facebook.share.widget; import android.app.Activity; import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import com.facebook.FacebookCallback; import com.facebook.FacebookException; import com.facebook.appevents.AppEventsLogger; import com.facebook.internal.AnalyticsEvents; import com.facebook.internal.AppCall; import com.facebook.internal.CallbackManagerImpl; import com.facebook.internal.DialogFeature; import com.facebook.internal.DialogPresenter; import com.facebook.internal.FacebookDialogBase; import com.facebook.internal.Utility; import com.facebook.share.Sharer; import com.facebook.share.internal.LegacyNativeDialogParameters; import com.facebook.share.internal.NativeDialogParameters; import com.facebook.share.internal.OpenGraphActionDialogFeature; import com.facebook.share.internal.ShareContentValidation; import com.facebook.share.internal.ShareDialogFeature; import com.facebook.share.internal.ShareInternalUtility; import com.facebook.share.internal.WebDialogParameters; import com.facebook.share.model.*; import java.util.ArrayList; import java.util.List; /** * Provides functionality to share content via the Facebook Share Dialog */ public final class ShareDialog extends FacebookDialogBase<ShareContent, Sharer.Result> implements Sharer { /** * The mode for the share dialog. */ public enum Mode { /** * The mode is determined automatically. */ AUTOMATIC, /** * The native dialog is used. */ NATIVE, /** * The web dialog is used. */ WEB, /** * The feed dialog is used. */ FEED } private static final String FEED_DIALOG = "feed"; private static final String WEB_SHARE_DIALOG = "share"; private static final String WEB_OG_SHARE_DIALOG = "share_open_graph"; private static final int DEFAULT_REQUEST_CODE = CallbackManagerImpl.RequestCodeOffset.Share.toRequestCode(); private boolean shouldFailOnDataError = false; // Keep track of Mode overrides for logging purposes. private boolean isAutomaticMode = true; /** * Helper to show the provided {@link com.facebook.share.model.ShareContent} using the provided * Activity. No callback will be invoked. * * @param activity Activity to use to share the provided content * @param shareContent Content to share */ public static void show( final Activity activity, final ShareContent shareContent) { new ShareDialog(activity).show(shareContent); } /** * Helper to show the provided {@link com.facebook.share.model.ShareContent} using the provided * Fragment. No callback will be invoked. * * @param fragment Fragment to use to share the provided content * @param shareContent Content to share */ public static void show( final Fragment fragment, final ShareContent shareContent) { new ShareDialog(fragment).show(shareContent); } /** * Indicates whether it is possible to show the dialog for * {@link com.facebook.share.model.ShareContent} of the specified type. * * @param contentType Class of the intended {@link com.facebook.share.model.ShareContent} to * share. * @return True if the specified content type can be shown via the dialog */ public static boolean canShow(Class<? extends ShareContent> contentType) { return canShowWebTypeCheck(contentType) || canShowNative(contentType); } private static boolean canShowNative(Class<? extends ShareContent> contentType) { DialogFeature feature = getFeature(contentType); return feature != null && DialogPresenter.canPresentNativeDialogWithFeature(feature); } private static boolean canShowWebTypeCheck(Class<? extends ShareContent> contentType) { // If we don't have an instance of a ShareContent, then all we can do is check whether // this is a ShareLinkContent, which can be shared if configured properly. // The instance method version of this check is more accurate and should be used on // ShareDialog instances. return ShareLinkContent.class.isAssignableFrom(contentType) || ShareOpenGraphContent.class.isAssignableFrom(contentType); } /** * Constructs a new ShareDialog. * @param activity Activity to use to share the provided content. */ public ShareDialog(Activity activity) { super(activity, DEFAULT_REQUEST_CODE); ShareInternalUtility.registerStaticShareCallback(DEFAULT_REQUEST_CODE); } /** * Constructs a new ShareDialog. * @param fragment Fragment to use to share the provided content. */ public ShareDialog(Fragment fragment) { super(fragment, DEFAULT_REQUEST_CODE); ShareInternalUtility.registerStaticShareCallback(DEFAULT_REQUEST_CODE); } // for ShareDialog use only ShareDialog(Activity activity, int requestCode) { super(activity, requestCode); ShareInternalUtility.registerStaticShareCallback(requestCode); } // for ShareDialog use only ShareDialog(Fragment fragment, int requestCode) { super(fragment, requestCode); ShareInternalUtility.registerStaticShareCallback(requestCode); } @Override protected void registerCallbackImpl( final CallbackManagerImpl callbackManager, final FacebookCallback<Result> callback) { ShareInternalUtility.registerSharerCallback( getRequestCode(), callbackManager, callback); } @Override public boolean getShouldFailOnDataError() { return this.shouldFailOnDataError; } @Override public void setShouldFailOnDataError(boolean shouldFailOnDataError) { this.shouldFailOnDataError = shouldFailOnDataError; } /** * Call this to check if the Share Dialog can be shown in a specific mode. * * @param mode Mode of the Share Dialog * @return True if the dialog can be shown in the passed in Mode */ public boolean canShow(ShareContent content, Mode mode) { return canShowImpl(content, (mode == Mode.AUTOMATIC) ? BASE_AUTOMATIC_MODE : mode); } /** * Call this to show the Share Dialog in a specific mode * @param mode Mode of the Share Dialog */ public void show(ShareContent content, Mode mode) { isAutomaticMode = (mode == Mode.AUTOMATIC); showImpl(content, isAutomaticMode ? BASE_AUTOMATIC_MODE : mode); } @Override protected AppCall createBaseAppCall() { return new AppCall(getRequestCode()); } @Override protected List<ModeHandler> getOrderedModeHandlers() { ArrayList<ModeHandler> handlers = new ArrayList<>(); handlers.add(new NativeHandler()); handlers.add(new FeedHandler()); // Feed takes precedence for link-shares for Mode.AUTOMATIC handlers.add(new WebShareHandler()); return handlers; } private class NativeHandler extends ModeHandler { @Override public Object getMode() { return Mode.NATIVE; } @Override public boolean canShow(final ShareContent content) { return content != null && ShareDialog.canShowNative(content.getClass()); } @Override public AppCall createAppCall(final ShareContent content) { logDialogShare(getActivityContext(), content, Mode.NATIVE); ShareContentValidation.validateForNativeShare(content); final AppCall appCall = createBaseAppCall(); final boolean shouldFailOnDataError = getShouldFailOnDataError(); DialogPresenter.setupAppCallForNativeDialog( appCall, new DialogPresenter.ParameterProvider() { @Override public Bundle getParameters() { return NativeDialogParameters.create( appCall.getCallId(), content, shouldFailOnDataError); } @Override public Bundle getLegacyParameters() { return LegacyNativeDialogParameters.create( appCall.getCallId(), content, shouldFailOnDataError); } }, getFeature(content.getClass())); return appCall; } } private class WebShareHandler extends ModeHandler { @Override public Object getMode() { return Mode.WEB; } @Override public boolean canShow(final ShareContent content) { return (content != null) && ShareDialog.canShowWebTypeCheck(content.getClass()); } @Override public AppCall createAppCall(final ShareContent content) { logDialogShare(getActivityContext(), content, Mode.WEB); final AppCall appCall = createBaseAppCall(); ShareContentValidation.validateForWebShare(content); Bundle params; if (content instanceof ShareLinkContent) { params = WebDialogParameters.create((ShareLinkContent)content); } else { params = WebDialogParameters.create((ShareOpenGraphContent)content); } DialogPresenter.setupAppCallForWebDialog( appCall, getActionName(content), params); return appCall; } private String getActionName(ShareContent shareContent) { if (shareContent instanceof ShareLinkContent) { return WEB_SHARE_DIALOG; } else if (shareContent instanceof ShareOpenGraphContent) { return WEB_OG_SHARE_DIALOG; } return null; } } private class FeedHandler extends ModeHandler { @Override public Object getMode() { return Mode.FEED; } @Override public boolean canShow(final ShareContent content) { return (content instanceof ShareLinkContent); } @Override public AppCall createAppCall(final ShareContent content) { logDialogShare(getActivityContext(), content, Mode.FEED); final ShareLinkContent linkContent = (ShareLinkContent)content; final AppCall appCall = createBaseAppCall(); ShareContentValidation.validateForWebShare(linkContent); DialogPresenter.setupAppCallForWebDialog( appCall, FEED_DIALOG, WebDialogParameters.createForFeed(linkContent)); return appCall; } } private static DialogFeature getFeature( Class<? extends ShareContent> contentType) { if (ShareLinkContent.class.isAssignableFrom(contentType)) { return ShareDialogFeature.SHARE_DIALOG; } else if (SharePhotoContent.class.isAssignableFrom(contentType)) { return ShareDialogFeature.PHOTOS; } else if (ShareVideoContent.class.isAssignableFrom(contentType)) { return ShareDialogFeature.VIDEO; } else if (ShareOpenGraphContent.class.isAssignableFrom(contentType)) { return OpenGraphActionDialogFeature.OG_ACTION_DIALOG; } return null; } private void logDialogShare(Context context, ShareContent content, Mode mode) { String displayType; if (isAutomaticMode) { mode = Mode.AUTOMATIC; } switch (mode) { case AUTOMATIC: displayType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_AUTOMATIC; break; case WEB: displayType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_WEB; break; case NATIVE: displayType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_NATIVE; break; default: displayType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW_UNKNOWN; break; } String contentType; DialogFeature dialogFeature = getFeature(content.getClass()); if (dialogFeature == ShareDialogFeature.SHARE_DIALOG) { contentType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_STATUS; } else if (dialogFeature == ShareDialogFeature.PHOTOS) { contentType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_PHOTO; } else if (dialogFeature == ShareDialogFeature.VIDEO) { contentType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_VIDEO; } else if (dialogFeature == OpenGraphActionDialogFeature.OG_ACTION_DIALOG) { contentType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_OPENGRAPH; } else { contentType = AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_UNKNOWN; } AppEventsLogger logger = AppEventsLogger.newLogger(context); Bundle parameters = new Bundle(); parameters.putString( AnalyticsEvents.PARAMETER_SHARE_DIALOG_SHOW, displayType ); parameters.putString( AnalyticsEvents.PARAMETER_SHARE_DIALOG_CONTENT_TYPE, contentType ); logger.logSdkEvent(AnalyticsEvents.EVENT_SHARE_DIALOG_SHOW, null, parameters); } }