Facebook.Unity/Canvas/CanvasFacebook.cs (591 lines of code) (raw):

/** * 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.Canvas { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; internal sealed class CanvasFacebook : FacebookBase, ICanvasFacebookImplementation { internal const string MethodAppRequests = "apprequests"; internal const string MethodFeed = "feed"; internal const string MethodPay = "pay"; internal const string CancelledResponse = "{\"cancelled\":true}"; internal const string FacebookConnectURL = "https://connect.facebook.net"; private const string AuthResponseKey = "authResponse"; private string appId; private string appLinkUrl; private ICanvasJSWrapper canvasJSWrapper; private HideUnityDelegate onHideUnityDelegate; public CanvasFacebook(): this(GetCanvasJSWrapper(), new CallbackManager()) { } public CanvasFacebook(ICanvasJSWrapper canvasJSWrapper, CallbackManager callbackManager) : base(callbackManager) { this.canvasJSWrapper = canvasJSWrapper; } private static ICanvasJSWrapper GetCanvasJSWrapper() { Assembly assembly = Assembly.Load("Facebook.Unity.Canvas"); Type type = assembly.GetType("Facebook.Unity.Canvas.CanvasJSWrapper"); ICanvasJSWrapper canvasJSWrapper = (ICanvasJSWrapper)Activator.CreateInstance(type); return canvasJSWrapper; } public override bool LimitEventUsage { get; set; } public override string SDKName { get { return "FBJSSDK"; } } public override string SDKVersion { get { return this.canvasJSWrapper.GetSDKVersion(); } } public override string SDKUserAgent { get { // We want to log whether we are running as webgl or in the web player. string webPlatform; switch (Constants.CurrentPlatform) { case FacebookUnityPlatform.WebGL: webPlatform = string.Format( CultureInfo.InvariantCulture, "FBUnity{0}", Constants.CurrentPlatform.ToString()); break; default: FacebookLogger.Warn("Currently running on uknown web platform"); webPlatform = "FBUnityWebUnknown"; break; } return string.Format( CultureInfo.InvariantCulture, "{0} {1}", base.SDKUserAgent, Utilities.GetUserAgent(webPlatform, FacebookSdkVersion.Build)); } } public void Init( string appId, bool cookie, bool logging, bool status, bool xfbml, string channelUrl, string authResponse, bool frictionlessRequests, string javascriptSDKLocale, bool loadDebugJSSDK, HideUnityDelegate hideUnityDelegate, InitDelegate onInitComplete) { base.Init(onInitComplete); this.canvasJSWrapper.InitScreenPosition(); this.appId = appId; this.onHideUnityDelegate = hideUnityDelegate; MethodArguments parameters = new MethodArguments(); parameters.AddString("appId", appId); parameters.AddPrimative("cookie", cookie); parameters.AddPrimative("logging", logging); parameters.AddPrimative("status", status); parameters.AddPrimative("xfbml", xfbml); parameters.AddString("channelUrl", channelUrl); parameters.AddString("authResponse", authResponse); parameters.AddPrimative("frictionlessRequests", frictionlessRequests); parameters.AddString("version", FB.GraphApiVersion); // use 1/0 for booleans, otherwise you'll get strings "True"/"False" this.canvasJSWrapper.Init( FacebookConnectURL, javascriptSDKLocale, loadDebugJSSDK ? 1 : 0, parameters.ToJsonString(), status ? 1 : 0); } public override void LogInWithPublishPermissions( IEnumerable<string> permissions, FacebookDelegate<ILoginResult> callback) { this.canvasJSWrapper.DisableFullScreen(); this.canvasJSWrapper.Login(permissions, CallbackManager.AddFacebookDelegate(callback)); } public override void LogInWithReadPermissions( IEnumerable<string> permissions, FacebookDelegate<ILoginResult> callback) { this.canvasJSWrapper.DisableFullScreen(); this.canvasJSWrapper.Login(permissions, CallbackManager.AddFacebookDelegate(callback)); } public override void LogOut() { base.LogOut(); this.canvasJSWrapper.Logout(); } public override void AppRequest( string message, OGActionType? actionType, string objectId, IEnumerable<string> to, IEnumerable<object> filters, IEnumerable<string> excludeIds, int? maxRecipients, string data, string title, FacebookDelegate<IAppRequestResult> callback) { this.ValidateAppRequestArgs( message, actionType, objectId, to, filters, excludeIds, maxRecipients, data, title, callback); MethodArguments args = new MethodArguments(); args.AddString("message", message); args.AddCommaSeparatedList("to", to); args.AddString("action_type", actionType != null ? actionType.ToString() : null); args.AddString("object_id", objectId); args.AddList("filters", filters); args.AddList("exclude_ids", excludeIds); args.AddNullablePrimitive("max_recipients", maxRecipients); args.AddString("data", data); args.AddString("title", title); var call = new CanvasUIMethodCall<IAppRequestResult>(this, MethodAppRequests, Constants.OnAppRequestsCompleteMethodName); call.Callback = callback; call.Call(args); } public override void ActivateApp(string appId) { this.canvasJSWrapper.ActivateApp(); } public override void ShareLink( Uri contentURL, string contentTitle, string contentDescription, Uri photoURL, FacebookDelegate<IShareResult> callback) { MethodArguments args = new MethodArguments(); args.AddUri("link", contentURL); args.AddString("name", contentTitle); args.AddString("description", contentDescription); args.AddUri("picture", photoURL); var call = new CanvasUIMethodCall<IShareResult>(this, MethodFeed, Constants.OnShareCompleteMethodName); call.Callback = callback; call.Call(args); } public override void FeedShare( string toId, Uri link, string linkName, string linkCaption, string linkDescription, Uri picture, string mediaSource, FacebookDelegate<IShareResult> callback) { MethodArguments args = new MethodArguments(); args.AddString("to", toId); args.AddUri("link", link); args.AddString("name", linkName); args.AddString("caption", linkCaption); args.AddString("description", linkDescription); args.AddUri("picture", picture); args.AddString("source", mediaSource); var call = new CanvasUIMethodCall<IShareResult>(this, MethodFeed, Constants.OnShareCompleteMethodName); call.Callback = callback; call.Call(args); } public void Pay( string product, string action, int quantity, int? quantityMin, int? quantityMax, string requestId, string pricepointId, string testCurrency, FacebookDelegate<IPayResult> callback) { this.PayImpl( product, /*productId*/ null, action, quantity, quantityMin, quantityMax, requestId, pricepointId, testCurrency, /*developerPayload*/ null, callback); } public void PayWithProductId( string productId, string action, int quantity, int? quantityMin, int? quantityMax, string requestId, string pricepointId, string testCurrency, FacebookDelegate<IPayResult> callback) { this.PayImpl( /*product*/ null, productId, action, quantity, quantityMin, quantityMax, requestId, pricepointId, testCurrency, /*developerPayload*/ null, callback); } public void PayWithProductId( string productId, string action, string developerPayload, string testCurrency, FacebookDelegate<IPayResult> callback) { this.PayImpl( /*product*/ null, productId, action, /*quantity*/ 1, /*quantityMin*/ null, /*quantityMax*/ null, /*requestId*/ null, /*pricepointId*/ null, testCurrency, developerPayload, callback); } public override void GetAppLink(FacebookDelegate<IAppLinkResult> callback) { var result = new Dictionary<string, object>() { { "url", this.appLinkUrl } }; callback(new AppLinkResult(new ResultContainer(result))); this.appLinkUrl = string.Empty; } public override void AppEventsLogEvent( string logEvent, float? valueToSum, Dictionary<string, object> parameters) { this.canvasJSWrapper.LogAppEvent( logEvent, valueToSum, MiniJSON.Json.Serialize(parameters)); } public override void AppEventsLogPurchase( float purchaseAmount, string currency, Dictionary<string, object> parameters) { this.canvasJSWrapper.LogPurchase( purchaseAmount, currency, MiniJSON.Json.Serialize(parameters)); } public override void OnLoginComplete(ResultContainer result) { CanvasFacebook.FormatAuthResponse( result, (formattedResponse) => { this.OnAuthResponse(new LoginResult(formattedResponse)); }); } public override void OnGetAppLinkComplete(ResultContainer message) { // We should never get here on canvas. We store the app link on this object // so should never hit this method. throw new NotImplementedException(); } // used only to refresh the access token public void OnFacebookAuthResponseChange(string responseJsonData) { this.OnFacebookAuthResponseChange(new ResultContainer(responseJsonData)); } public void OnFacebookAuthResponseChange(ResultContainer resultContainer) { CanvasFacebook.FormatAuthResponse( resultContainer, (formattedResponse) => { var result = new LoginResult(formattedResponse); AccessToken.CurrentAccessToken = result.AccessToken; }); } public void OnPayComplete(string responseJsonData) { this.OnPayComplete(new ResultContainer(responseJsonData)); } public void OnPayComplete(ResultContainer resultContainer) { var result = new PayResult(resultContainer); CallbackManager.OnFacebookResponse(result); } public override void OnAppRequestsComplete(ResultContainer resultContainer) { var result = new AppRequestResult(resultContainer); CallbackManager.OnFacebookResponse(result); } public override void OnShareLinkComplete(ResultContainer resultContainer) { var result = new ShareResult(resultContainer); CallbackManager.OnFacebookResponse(result); } public void OnUrlResponse(string url) { this.appLinkUrl = url; } public void OnHideUnity(bool isGameShown) { if (this.onHideUnityDelegate != null) { this.onHideUnityDelegate(isGameShown); } } private static void FormatAuthResponse(ResultContainer result, Utilities.Callback<ResultContainer> callback) { if (result.ResultDictionary == null) { callback(result); return; } IDictionary<string, object> authResponse; if (result.ResultDictionary.TryGetValue(CanvasFacebook.AuthResponseKey, out authResponse)) { result.ResultDictionary.Remove(CanvasFacebook.AuthResponseKey); foreach (var item in authResponse) { result.ResultDictionary[item.Key] = item.Value; } } // The JS SDK doesn't always store the permissions so request them before returning the results if (result.ResultDictionary.ContainsKey(LoginResult.AccessTokenKey) && !result.ResultDictionary.ContainsKey(LoginResult.PermissionsKey)) { var parameters = new Dictionary<string, string>() { { "fields", "permissions" }, { Constants.AccessTokenKey, (string)result.ResultDictionary[LoginResult.AccessTokenKey] }, }; FacebookDelegate<IGraphResult> apiCallback = (IGraphResult r) => { IDictionary<string, object> permissionsJson; if (r.ResultDictionary != null && r.ResultDictionary.TryGetValue("permissions", out permissionsJson)) { IList<string> permissions = new List<string>(); IList<object> data; if (permissionsJson.TryGetValue("data", out data)) { foreach (var permission in data) { var permissionDictionary = permission as IDictionary<string, object>; if (permissionDictionary != null) { string status; if (permissionDictionary.TryGetValue("status", out status) && status.Equals("granted", StringComparison.InvariantCultureIgnoreCase)) { string permissionName; if (permissionDictionary.TryGetValue("permission", out permissionName)) { permissions.Add(permissionName); } else { FacebookLogger.Warn("Didn't find permission name"); } } else { FacebookLogger.Warn("Didn't find status in permissions result"); } } else { FacebookLogger.Warn("Failed to case permission dictionary"); } } } else { FacebookLogger.Warn("Failed to extract data from permissions"); } result.ResultDictionary[LoginResult.PermissionsKey] = permissions.ToCommaSeparateList(); } else { FacebookLogger.Warn("Failed to load permissions for access token"); } callback(result); }; FB.API( "me", HttpMethod.GET, apiCallback, parameters); } else { callback(result); } } private void PayImpl( string product, string productId, string action, int quantity, int? quantityMin, int? quantityMax, string requestId, string pricepointId, string testCurrency, string developerPayload, FacebookDelegate<IPayResult> callback) { MethodArguments args = new MethodArguments(); args.AddString("product", product); args.AddString("product_id", productId); args.AddString("action", action); args.AddPrimative("quantity", quantity); args.AddNullablePrimitive("quantity_min", quantityMin); args.AddNullablePrimitive("quantity_max", quantityMax); args.AddString("request_id", requestId); args.AddString("pricepoint_id", pricepointId); args.AddString("test_currency", testCurrency); args.AddString("developer_payload", developerPayload); var call = new CanvasUIMethodCall<IPayResult>(this, MethodPay, Constants.OnPayCompleteMethodName); call.Callback = callback; call.Call(args); } private class CanvasUIMethodCall<T> : MethodCall<T> where T : IResult { private CanvasFacebook canvasImpl; private string callbackMethod; public CanvasUIMethodCall(CanvasFacebook canvasImpl, string methodName, string callbackMethod) : base(canvasImpl, methodName) { this.canvasImpl = canvasImpl; this.callbackMethod = callbackMethod; } public override void Call(MethodArguments args) { this.UI(this.MethodName, args, this.Callback); } private void UI( string method, MethodArguments args, FacebookDelegate<T> callback = null) { this.canvasImpl.canvasJSWrapper.DisableFullScreen(); var clonedArgs = new MethodArguments(args); clonedArgs.AddString("app_id", this.canvasImpl.appId); clonedArgs.AddString("method", method); var uniqueId = this.canvasImpl.CallbackManager.AddFacebookDelegate(callback); this.canvasImpl.canvasJSWrapper.Ui(clonedArgs.ToJsonString (), uniqueId, this.callbackMethod); } } public override void GetCatalog(FacebookDelegate<ICatalogResult> callback) { throw new NotImplementedException(); } public override void GetPurchases(FacebookDelegate<IPurchasesResult> callback) { throw new NotImplementedException(); } public override void Purchase(string productID, FacebookDelegate<IPurchaseResult> callback, string developerPayload = "") { throw new NotImplementedException(); } public override void ConsumePurchase(string productToken, FacebookDelegate<IConsumePurchaseResult> callback) { throw new NotImplementedException(); } public override Profile CurrentProfile() { throw new NotImplementedException(); } public override void CurrentProfile(FacebookDelegate<IProfileResult> callback) { throw new NotSupportedException(); } public override void LoadInterstitialAd(string placementID, FacebookDelegate<IInterstitialAdResult> callback) { throw new NotImplementedException(); } public override void ShowInterstitialAd(string placementID, FacebookDelegate<IInterstitialAdResult> callback) { throw new NotImplementedException(); } public override void LoadRewardedVideo(string placementID, FacebookDelegate<IRewardedVideoResult> callback) { throw new NotImplementedException(); } public override void ShowRewardedVideo(string placementID, FacebookDelegate<IRewardedVideoResult> callback) { throw new NotImplementedException(); } public override void OpenFriendFinderDialog(FacebookDelegate<IGamingServicesFriendFinderResult> callback) { throw new NotImplementedException(); } public override void GetFriendFinderInvitations(FacebookDelegate<IFriendFinderInvitationResult> callback) { throw new NotImplementedException(); } public override void DeleteFriendFinderInvitation(string invitationId, FacebookDelegate<IFriendFinderInvitationResult> callback) { throw new NotImplementedException(); } public override void ScheduleAppToUserNotification(string title, string body, Uri media, int timeInterval, string payload, FacebookDelegate<IScheduleAppToUserNotificationResult> callback) { throw new NotImplementedException(); } public override void PostSessionScore(int score, FacebookDelegate<ISessionScoreResult> callback) { throw new NotImplementedException(); } public override void PostTournamentScore(int score, FacebookDelegate<ITournamentScoreResult> callback) { throw new NotImplementedException(); } public override void GetTournament(FacebookDelegate<ITournamentResult> callback) { throw new NotImplementedException(); } public override void ShareTournament(int score, Dictionary<string, string> data, FacebookDelegate<ITournamentScoreResult> callback) { throw new NotImplementedException(); } public override void CreateTournament(int initialScore, string title, string imageBase64DataUrl, string sortOrder, string scoreFormat, Dictionary<string, string> data, FacebookDelegate<ITournamentResult> callback) { throw new NotImplementedException(); } public override void UploadImageToMediaLibrary(string caption, Uri imageUri, bool shouldLaunchMediaDialog, FacebookDelegate<IMediaUploadResult> callback) { throw new NotImplementedException(); } public override void UploadVideoToMediaLibrary(string caption, Uri videoUri, bool shouldLaunchMediaDialog, FacebookDelegate<IMediaUploadResult> callback) { throw new NotImplementedException(); } } }