/** * 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. */ namespace Facebook.Unity { using System; using System.Collections; using System.Collections.Generic; using Facebook.Unity.Canvas; using Facebook.Unity.Editor; using Facebook.Unity.Mobile; using Facebook.Unity.Mobile.Android; using Facebook.Unity.Mobile.IOS; using UnityEngine; /// <summary> /// Static class for exposing the facebook integration /// </summary> public sealed class FB : ScriptableObject { private static InitDelegate onInitComplete; private static HideUnityDelegate onHideUnity; private static FacebookGameObject facebook; private static string authResponse; private static bool isInitCalled = false; private static string appId; private static bool cookie; private static bool logging; private static bool status; private static bool xfbml; private static bool frictionlessRequests; private static string facebookDomain = "facebook.com"; /// <summary> /// Gets the app identifier. AppId might be different from FBSettings.AppId /// if using the programmatic version of FB.Init() /// </summary> /// <value>The app identifier.</value> public static string AppId { get { return appId; } } /// <summary> /// Gets a value indicating a user logged in. /// </summary> /// <value><c>true</c> if is logged in; otherwise, <c>false</c>.</value> public static bool IsLoggedIn { get { return (facebook != null) && FacebookImpl.LoggedIn; } } /// <summary> /// Gets a value indicating is the SDK is initialized. /// </summary> /// <value><c>true</c> if is initialized; otherwise, <c>false</c>.</value> public static bool IsInitialized { get { return (facebook != null) && facebook.Initialized; } } // If the player has set the limitEventUsage flag to YES, your app will continue // to send this data to Facebook, but Facebook will not use the data to serve // targeted ads. Facebook may continue to use the information for other purposes, // including frequency capping, conversion events, estimating the number of unique // users, security and fraud detection, and debugging. public static bool LimitAppEventUsage { get { return (facebook != null) && facebook.Facebook.LimitEventUsage; } set { if (facebook != null) { facebook.Facebook.LimitEventUsage = value; } } } internal static IFacebook FacebookImpl { get { if (facebook == null) { throw new NullReferenceException("Facebook object is not yet loaded. Did you call FB.Init()?"); } return facebook.Facebook; } } internal static string FacebookDomain { get { return FB.facebookDomain; } set { FB.facebookDomain = value; } } /// <summary> /// This is the preferred way to call FB.Init(). It will take the facebook app id specified in your "Facebook" /// => "Edit Settings" menu when it is called. /// </summary> /// <param name="onInitComplete"> /// Delegate is called when FB.Init() finished initializing everything. By passing in a delegate you can find /// out when you can safely call the other methods. /// </param> /// <param name="onHideUnity">A delegate to invoke when unity is hidden.</param> /// <param name="authResponse">Auth response.</param> public static void Init(InitDelegate onInitComplete = null, HideUnityDelegate onHideUnity = null, string authResponse = null) { Init( FacebookSettings.AppId, FacebookSettings.Cookie, FacebookSettings.Logging, FacebookSettings.Status, FacebookSettings.Xfbml, FacebookSettings.FrictionlessRequests, authResponse, onHideUnity, onInitComplete); } /// <summary> /// If you need a more programmatic way to set the facebook app id and other setting call this function. /// Useful for a build pipeline that requires no human input. /// </summary> /// <param name="appId">App identifier.</param> /// <param name="cookie">If set to <c>true</c> cookie.</param> /// <param name="logging">If set to <c>true</c> logging.</param> /// <param name="status">If set to <c>true</c> status.</param> /// <param name="xfbml">If set to <c>true</c> xfbml.</param> /// <param name="frictionlessRequests">If set to <c>true</c> frictionless requests.</param> /// <param name="authResponse">Auth response.</param> /// <param name="onHideUnity"> /// A delegate to invoke when unity is hidden. /// </param> /// <param name="onInitComplete"> /// Delegate is called when FB.Init() finished initializing everything. By passing in a delegate you can find /// out when you can safely call the other methods. /// </param> public static void Init( string appId, bool cookie = true, bool logging = true, bool status = true, bool xfbml = false, bool frictionlessRequests = true, string authResponse = null, HideUnityDelegate onHideUnity = null, InitDelegate onInitComplete = null) { if (string.IsNullOrEmpty(appId)) { throw new ArgumentException("appId cannot be null or empty!"); } FB.appId = appId; FB.cookie = cookie; FB.logging = logging; FB.status = status; FB.xfbml = xfbml; FB.frictionlessRequests = frictionlessRequests; FB.authResponse = authResponse; FB.onInitComplete = onInitComplete; FB.onHideUnity = onHideUnity; if (!isInitCalled) { FB.LogVersion(); #if UNITY_EDITOR ComponentFactory.GetComponent<EditorFacebookLoader>(); #elif UNITY_WEBPLAYER || UNITY_WEBGL ComponentFactory.GetComponent<CanvasFacebookLoader>(); #elif UNITY_IOS ComponentFactory.GetComponent<IOSFacebookLoader>(); #elif UNITY_ANDROID ComponentFactory.GetComponent<AndroidFacebookLoader>(); #else throw new NotImplementedException("Facebook API does not yet support this platform"); #endif isInitCalled = true; return; } FacebookLogger.Warn("FB.Init() has already been called. You only need to call this once and only once."); // Init again if possible just in case something bad actually happened. if (FacebookImpl != null) { OnDllLoaded(); } } /// <summary> /// Logs the user in with the requested publish permissions. /// </summary> /// <param name="permissions">A list of requested permissions.</param> /// <param name="callback">Callback to be called when request completes.</param> public static void LogInWithPublishPermissions( IEnumerable<string> permissions = null, FacebookDelegate<ILoginResult> callback = null) { FacebookImpl.LogInWithPublishPermissions(permissions, callback); } /// <summary> /// Logs the user in with the requested read permissions /// </summary> /// <param name="permissions">A list of requested permissions.</param> /// <param name="callback">Callback to be called when request completes.</param> public static void LogInWithReadPermissions( IEnumerable<string> permissions = null, FacebookDelegate<ILoginResult> callback = null) { FacebookImpl.LogInWithReadPermissions(permissions, callback); } /// <summary> /// Logs out the current user. /// </summary> public static void LogOut() { FacebookImpl.LogOut(); } /// <summary> /// Apps the request. /// </summary> /// <param name="message">The request string the recipient will see, maximum length 60 characters.</param> /// <param name="actionType">Request action type for structured request.</param> /// <param name="objectId"> /// Open Graph object ID for structured request. /// Note the type of object should belong to this app. /// </param> /// <param name="to">A list of Facebook IDs to which to send the request.</param> /// <param name="data"> /// Additional data stored with the request on Facebook, /// and handed back to the app when it reads the request back out. /// Maximum length 255 characters.</param> /// <param name="title">The title for the platform multi-friend selector dialog. Max length 50 characters..</param> /// <param name="callback">A callback for when the request completes.</param> public static void AppRequest( string message, OGActionType actionType, string objectId, IEnumerable<string> to, string data = "", string title = "", FacebookDelegate<IAppRequestResult> callback = null) { FacebookImpl.AppRequest(message, actionType, objectId, to, null, null, null, data, title, callback); } /// <summary> /// Apps the request. /// </summary> /// <param name="message">The request string the recipient will see, maximum length 60 characters.</param> /// <param name="actionType">Request action type for structured request.</param> /// <param name="objectId"> /// Open Graph object ID for structured request. /// Note the type of object should belong to this app. /// </param> /// <param name="filters"> /// The configuration of the platform multi-friend selector. /// It should be a List of filter strings. /// </param> /// <param name="excludeIds"> /// A list of Facebook IDs to exclude from the platform multi-friend selector dialog. /// This list is currently not supported for mobile devices /// </param> /// <param name="maxRecipients"> /// Platform-dependent The maximum number of recipients the sender should be able to /// choose in the platform multi-friend selector dialog. /// Only guaranteed to work in Unity Web Player app. /// </param> /// <param name="data"> /// Additional data stored with the request on Facebook, and handed /// back to the app when it reads the request back out. /// Maximum length 255 characters. /// </param> /// <param name="title"> /// The title for the platform multi-friend selector dialog. Max length 50 characters. /// </param> /// <param name="callback">A callback for when the request completes.</param> public static void AppRequest( string message, OGActionType actionType, string objectId, IEnumerable<object> filters = null, IEnumerable<string> excludeIds = null, int? maxRecipients = null, string data = "", string title = "", FacebookDelegate<IAppRequestResult> callback = null) { FacebookImpl.AppRequest(message, actionType, objectId, null, filters, excludeIds, maxRecipients, data, title, callback); } /// <summary> /// Apps the request. /// </summary> /// <param name="message">The request string the recipient will see, maximum length 60 characters.</param> /// <param name="to">A list of Facebook IDs to which to send the request.</param> /// <param name="filters"> /// The configuration of the platform multi-friend selector. /// It should be a List of filter strings. /// </param> /// <param name="excludeIds"> /// A list of Facebook IDs to exclude from the platform multi-friend selector dialog. /// This list is currently not supported for mobile devices /// </param> /// <param name="maxRecipients"> /// Platform-dependent The maximum number of recipients the sender should be able to /// choose in the platform multi-friend selector dialog. /// Only guaranteed to work in Unity Web Player app. /// </param> /// <param name="data"> /// Additional data stored with the request on Facebook, and handed /// back to the app when it reads the request back out. /// Maximum length 255 characters. /// </param> /// <param name="title"> /// The title for the platform multi-friend selector dialog. Max length 50 characters. /// </param> /// <param name="callback">A callback for when the request completes.</param> public static void AppRequest( string message, IEnumerable<string> to = null, IEnumerable<object> filters = null, IEnumerable<string> excludeIds = null, int? maxRecipients = null, string data = "", string title = "", FacebookDelegate<IAppRequestResult> callback = null) { FacebookImpl.AppRequest(message, null, null, to, filters, excludeIds, maxRecipients, data, title, callback); } /// <summary> /// Opens a share dialog for sharing a link. /// </summary> /// <param name="contentURL">The URL or the link to share.</param> /// <param name="contentTitle">The title to display for this link..</param> /// <param name="contentDescription"> /// The description of the link. If not specified, this field is automatically populated by /// information scraped from the link, typically the title of the page. /// </param> /// <param name="photoURL">The URL of a picture to attach to this content.</param> /// <param name="callback">A callback for when the request completes.</param> public static void ShareLink( Uri contentURL = null, string contentTitle = "", string contentDescription = "", Uri photoURL = null, FacebookDelegate<IShareResult> callback = null) { FacebookImpl.ShareLink( contentURL, contentTitle, contentDescription, photoURL, callback); } /// <summary> /// Legacy feed share. Only use this dialog if you need the legacy parameters otherwiese use /// <see cref="FB.ShareLink(System.String, System.String, System.String, System.String, Facebook.FacebookDelegate"/>. /// </summary> /// <param name="toId"> /// The ID of the profile that this story will be published to. /// If this is unspecified, it defaults to the value of from. /// The ID must be a friend who also uses your app. /// </param> /// <param name="link">The link attached to this post.</param> /// <param name="linkName">The name of the link attachment.</param> /// <param name="linkCaption"> /// The caption of the link (appears beneath the link name). /// If not specified, this field is automatically populated /// with the URL of the link. /// </param> /// <param name="linkDescription"> /// The description of the link (appears beneath the link caption). /// If not specified, this field is automatically populated by information /// scraped from the link, typically the title of the page. /// </param> /// <param name="picture"> /// The URL of a picture attached to this post. /// The picture must be at least 200px by 200px. /// See our documentation on sharing best practices for more information on sizes. /// </param> /// <param name="mediaSource"> /// The URL of a media file (either SWF or MP3) attached to this post. /// If SWF, you must also specify picture to provide a thumbnail for the video. /// </param> /// <param name="callback">The callback to use upon completion.</param> public static void FeedShare( string toId = "", Uri link = null, string linkName = "", string linkCaption = "", string linkDescription = "", Uri picture = null, string mediaSource = "", FacebookDelegate<IShareResult> callback = null) { FacebookImpl.FeedShare( toId, link, linkName, linkCaption, linkDescription, picture, mediaSource, callback); } /// <summary> /// Makes a call to the Facebook Graph API. /// </summary> /// <param name="query"> /// The Graph API endpoint to call. /// You may prefix this with a version string to call a particular version of the API. /// </param> /// <param name="method">The HTTP method to use in the call.</param> /// <param name="callback">The callback to use upon completion.</param> /// <param name="formData">The key/value pairs to be passed to the endpoint as arguments.</param> public static void API( string query, HttpMethod method, FacebookDelegate<IGraphResult> callback = null, IDictionary<string, string> formData = null) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException("query", "The query param cannot be null or empty"); } FacebookImpl.API(query, method, formData, callback); } /// <summary> /// Makes a call to the Facebook Graph API. /// </summary> /// <param name="query"> /// The Graph API endpoint to call. /// You may prefix this with a version string to call a particular version of the API. /// </param> /// <param name="method">The HTTP method to use in the call.</param> /// <param name="callback">The callback to use upon completion.</param> /// <param name="formData">Form data for the request.</param> public static void API( string query, HttpMethod method, FacebookDelegate<IGraphResult> callback, WWWForm formData) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException("query", "The query param cannot be null or empty"); } FacebookImpl.API(query, method, formData, callback); } /// <summary> /// Sends an app activation event to Facebook when your app is activated. /// </summary> public static void ActivateApp() { FacebookImpl.ActivateApp(AppId); } /// <summary> /// Gets the deep link if available. /// </summary> /// <param name="callback">The callback to use upon completion.</param> public static void GetAppLink( FacebookDelegate<IAppLinkResult> callback) { if (callback == null) { // No point in fetching the data if there is no callback return; } FacebookImpl.GetAppLink(callback); } /// <summary> /// Opens a dialog to create a new game group /// </summary> /// <param name="name">The name of the group you wish to create.</param> /// <param name="description">A short description of the group's purpose.</param> /// <param name="privacy"> /// The privacy of the group. /// OPEN groups' content is visible to anyone /// CLOSED groups can be found by anyone but their content is only visible to members /// SECRET groups can only be found by their members. /// </param> /// <param name="callback">The callback to use upon completion.</param> public static void GameGroupCreate( string name, string description, string privacy = "CLOSED", FacebookDelegate<IGroupCreateResult> callback = null) { FacebookImpl.GameGroupCreate(name, description, privacy, callback); } /// <summary> /// Opens a dialog to join a game group. /// </summary> /// <param name="id">The group ID of the group to which you'd like to add the user.</param> /// <param name="callback">The callback to use upon completion.</param> public static void GameGroupJoin( string id, FacebookDelegate<IGroupJoinResult> callback = null) { FacebookImpl.GameGroupJoin(id, callback); } /// <summary> /// Logs an app event. /// </summary> /// <param name="logEvent">The name of the event to log.</param> /// <param name="valueToSum">A number representing some value to be summed when reported.</param> /// <param name="parameters">Any parameters needed to describe the event.</param> public static void LogAppEvent( string logEvent, float? valueToSum = null, Dictionary<string, object> parameters = null) { FacebookImpl.AppEventsLogEvent(logEvent, valueToSum, parameters); } /// <summary> /// Logs the purchase. /// </summary> /// <param name="logPurchase">The amount of currency the user spent.</param> /// <param name="currency">The 3-letter ISO currency code.</param> /// <param name="parameters"> /// Any parameters needed to describe the event. /// Elements included in this dictionary can't be null. /// </param> public static void LogPurchase( float logPurchase, string currency = null, Dictionary<string, object> parameters = null) { if (string.IsNullOrEmpty(currency)) { currency = "USD"; } FacebookImpl.AppEventsLogPurchase(logPurchase, currency, parameters); } private static void OnDllLoaded() { FB.LogVersion(); FacebookImpl.Init( appId, cookie, logging, status, xfbml, FacebookSettings.ChannelUrl, authResponse, frictionlessRequests, onHideUnity, onInitComplete); } private static void LogVersion() { // If we have initlized we can also get the underlying sdk version if (facebook != null) { FacebookLogger.Info(string.Format( "Using Unity SDK v{0} with {1}", FacebookSdkVersion.Build, FB.FacebookImpl.SDKUserAgent)); } else { FacebookLogger.Info(string.Format("Using Unity SDK v{0}", FacebookSdkVersion.Build)); } } public sealed class Canvas { private static ICanvasFacebook CanvasFacebookImpl { get { ICanvasFacebook impl = FacebookImpl as ICanvasFacebook; if (impl == null) { throw new InvalidOperationException("Attempt to call Canvas interface on non canvas platform"); } return impl; } } /// <summary> /// Pay the specified product, action, quantity, quantityMin, quantityMax, requestId, pricepointId, /// testCurrency and callback. /// </summary> /// <param name="product">The URL of your og:product object that the user is looking to purchase.</param> /// <param name="action">Should always be purchaseitem.</param> /// <param name="quantity"> /// The amount of this item the user is looking to purchase - typically used when implementing a virtual currency purchase. /// </param> /// <param name="quantityMin"> /// The minimum quantity of the item the user is able to purchase. /// This parameter is important when handling price jumping to maximize the efficiency of the transaction. /// </param> /// <param name="quantityMax"> /// The maximum quantity of the item the user is able to purchase. /// This parameter is important when handling price jumping to maximize the efficiency of the transaction. /// </param> /// <param name="requestId"> /// The developer defined unique identifier for this transaction, which becomes /// attached to the payment within the Graph API. /// </param> /// <param name="pricepointId"> /// Used to shortcut a mobile payer directly to the /// mobile purchase flow at a given price point. /// </param> /// <param name="testCurrency"> /// This parameter can be used during debugging and testing your implementation to force the dialog to /// use a specific currency rather than the current user's preferred currency. This allows you to /// rapidly prototype your payment experience for different currencies without having to repeatedly /// change your personal currency preference settings. This parameter is only available for admins, /// developers and testers associated with the app, in order to minimize the security risk of a /// malicious JavaScript injection. Provide the 3 letter currency code of the intended forced currency. /// </param> /// <param name="callback">The callback to use upon completion.</param> public static void Pay( string product, string action = "purchaseitem", int quantity = 1, int? quantityMin = null, int? quantityMax = null, string requestId = null, string pricepointId = null, string testCurrency = null, FacebookDelegate<IPayResult> callback = null) { CanvasFacebookImpl.Pay( product, action, quantity, quantityMin, quantityMax, requestId, pricepointId, testCurrency, callback); } } /// <summary> /// A class containing the settings specific to the supported mobile platforms. /// </summary> public sealed class Mobile { /// <summary> /// Gets or sets the share dialog mode. /// </summary> /// <value>The share dialog mode.</value> public static ShareDialogMode ShareDialogMode { get { return Mobile.MobileFacebookImpl.ShareDialogMode; } set { Mobile.MobileFacebookImpl.ShareDialogMode = value; } } private static IMobileFacebook MobileFacebookImpl { get { IMobileFacebook impl = FacebookImpl as IMobileFacebook; if (impl == null) { throw new InvalidOperationException("Attempt to call Mobile interface on non mobile platform"); } return impl; } } /// <summary> /// Show the app invite dialog. /// </summary> /// <param name="appLinkUrl"> /// App Link for what should be opened when the recipient clicks on the /// install/play button on the app invite page. /// </param> /// <param name="previewImageUrl">A url to an image to be used in the invite.</param> /// <param name="callback">A callback for when the dialog completes</para> public static void AppInvite( Uri appLinkUrl, Uri previewImageUrl = null, FacebookDelegate<IAppInviteResult> callback = null) { MobileFacebookImpl.AppInvite(appLinkUrl, previewImageUrl, callback); } /// <summary> /// Fetchs the deferred app link data. /// </summary> /// <param name="callback">A callback for when the call is complete.</param> public static void FetchDeferredAppLinkData( FacebookDelegate<IAppLinkResult> callback = null) { if (callback == null) { // No point in fetching the data if there is no callback return; } Mobile.MobileFacebookImpl.FetchDeferredAppLink(callback); } } public sealed class Android { /// <summary> /// Gets the key hash. /// </summary> /// <value>The key hash.</value> public static string KeyHash { get { var androidFacebook = FacebookImpl as AndroidFacebook; return (androidFacebook != null) ? androidFacebook.KeyHash : string.Empty; } } } internal abstract class CompiledFacebookLoader : MonoBehaviour { protected abstract FacebookGameObject FBGameObject { get; } public void Start() { FB.facebook = this.FBGameObject; FB.OnDllLoaded(); MonoBehaviour.Destroy(this); } } } }