diff --git a/.gitignore b/.gitignore index d660cc80c..55924626a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,10 +19,6 @@ UnityPackageManager/ /Temp /Library ## https://stackoverflow.com/a/57728803/3745724 -#Ignore all .meta file -*.meta -#But not source file with postfix. which is everything but a folder -!*.*.meta # Firebase /unity-samples/Assets/Plugins/Android/*.aar @@ -87,3 +83,5 @@ unity-samples/iOS/Roll-A-Ball-Ios/fastlane/report.xml # Visual Studio .vs/ obj/ +.vscode/ + diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 000000000..5a0b87a39 --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 71fbc09525bca413cafd9ae08a29d175 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins.meta b/Assets/Plugins.meta new file mode 100644 index 000000000..397bbe3dc --- /dev/null +++ b/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2245c82083ae24e51b7730ccad41cef9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android.meta b/Assets/Plugins/Android.meta new file mode 100644 index 000000000..a2153c028 --- /dev/null +++ b/Assets/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b188b8d8c25040e59346b214285c462 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/appboy-ui.aar b/Assets/Plugins/Android/appboy-ui.aar index 930119b27..757ef6701 100644 Binary files a/Assets/Plugins/Android/appboy-ui.aar and b/Assets/Plugins/Android/appboy-ui.aar differ diff --git a/Assets/Plugins/Android/appboy-unity.aar b/Assets/Plugins/Android/appboy-unity.aar index 2285f2c23..65a35e78b 100644 Binary files a/Assets/Plugins/Android/appboy-unity.aar and b/Assets/Plugins/Android/appboy-unity.aar differ diff --git a/Assets/Plugins/Android/appboy.aar b/Assets/Plugins/Android/appboy.aar index ec2d12f7e..4027c43e4 100644 Binary files a/Assets/Plugins/Android/appboy.aar and b/Assets/Plugins/Android/appboy.aar differ diff --git a/Assets/Plugins/Appboy.meta b/Assets/Plugins/Appboy.meta new file mode 100644 index 000000000..bdbd9363d --- /dev/null +++ b/Assets/Plugins/Appboy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7946e19958b6c4b36938e0ff6292a975 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/AppboyBinding.cs b/Assets/Plugins/Appboy/AppboyBinding.cs index 64291187c..6a8cfdd64 100755 --- a/Assets/Plugins/Appboy/AppboyBinding.cs +++ b/Assets/Plugins/Appboy/AppboyBinding.cs @@ -1,23 +1,82 @@ // When developing, you can place #define UNITY_ANDROID or #define UNITY_IOS above this line // in order to get correct syntax highlighting in the region you are working on. +using Appboy.Internal; using Appboy.Models; -using UnityEngine; -using System.Collections.Generic; -using System.Runtime.InteropServices; using Appboy.Utilities; using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; +using UnityEngine; /// /// These methods can be called by Unity applications using iOS or Android in order to report -/// events and set user attributes. Please see the Appboy Android JavaDocs for more -/// detailed guidance on usage (note that only a subset of the functions in the JavaDocs -/// are available in the Unity API): -/// http://appboy.github.io/appboy-android-sdk/javadocs/com/appboy/IAppboy.html -/// http://appboy.github.io/appboy-android-sdk/javadocs/index.html +/// events, set user attributes, and control messaging /// - namespace Appboy { + + public delegate void PushPromptResponseReceived(bool granted); + public delegate void PushTokenReceivedFromSystem(string token); + + /// + /// Types of messages that Braze can be configured to send to a GameObject method at runtime. + /// + /// Use to set up a listener. + /// + public enum BrazeUnityMessageType { + /// + /// Sent when Braze receives a response from the user after displaying a push prompt via + /// , or if Braze is automatically configured + /// to register for push. Currently only sent for iOS. + /// + PUSH_PERMISSIONS_PROMPT_RESPONSE = 0, + + /// + /// Sent when Braze receives a push token from the OS. Currently only sent for iOS. + /// + /// On iOS, only sent if Braze is configured to automatically integrate push. + /// + PUSH_TOKEN_RECEIVED_FROM_SYSTEM = 1, + + /// + /// Sent when the user receives a push notification. + /// + /// On iOS, only sent if Braze is configured to automatically integrate push. On iOS, we recommend + /// configuring this value before application:didFinishLaunchingWithOptions: returns to ensure your + /// callback is set before iOS push delegates are called. + /// + PUSH_RECEIVED = 2, + + /// + /// Sent when the user opens a push notification. + /// + /// On iOS, only sent if Braze is configured to automatically integrate push. On iOS, we recommend + /// configuring this value before application:didFinishLaunchingWithOptions: returns to ensure your + /// callback is set before iOS push delegates are called. + /// + PUSH_OPENED = 3, + + /// + /// Sent when the user has swiped away a push notification. Currently only sent for Android. + /// + PUSH_DELETED = 4, + + /// + /// Sent when the SDK has a new In-App Message. + /// + IN_APP_MESSAGE = 5, + + /// + /// Sent when the SDK has an update for the News Feed. + /// + NEWS_FEED_UPDATED = 6, + + /// + /// Sent when the SDK has an update for Content Cards. + /// + CONTENT_CARDS_UPDATED = 7 + } + public class AppboyBinding : MonoBehaviour { // Overloads // These will call the associated binding method for the current live platform @@ -32,7 +91,7 @@ public static void IncrementCustomUserAttribute(string key) { #if UNITY_IOS void Start() { - Debug.Log("Starting Appboy binding for iOS clients."); + Debug.Log("Starting Braze binding for iOS clients."); } [System.Runtime.InteropServices.DllImport("__Internal")] @@ -185,9 +244,18 @@ void Start() { [System.Runtime.InteropServices.DllImport("__Internal")] private static extern void _registerAppboyPushMessages(string registrationTokenBase64); + [System.Runtime.InteropServices.DllImport("__Internal")] + private static extern void _promptUserForPushPermissions(bool provisional); + [System.Runtime.InteropServices.DllImport("__Internal")] private static extern void _addAlias(string label, string alias); + [System.Runtime.InteropServices.DllImport("__Internal")] + private static extern void _configureListener(int messageType, string gameobject, string method); + + [System.Runtime.InteropServices.DllImport("__Internal")] + private static extern void _configureInternalListener(int messageType); + public static void LogCustomEvent(string eventName) { _logCustomEvent(eventName, null); } @@ -206,6 +274,11 @@ public static void LogPurchase(string productId, string currencyCode, decimal pr _logPurchase(productId, currencyCode, price.ToString(), quantity, propertiesString); } + /// + /// When you first start using Braze on a device, the user is considered "anonymous". You can use this + /// method to optionally identify a user with a unique ID. + /// + /// public static void ChangeUser(string userId) { _changeUser(userId); } @@ -238,10 +311,18 @@ public static void SetUserHomeCity(string city) { _setUserHomeCity(city); } + /// + /// Configures the user's opt-in status for email within Braze. + /// + /// public static void SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType emailNotificationSubscriptionType) { _setUserEmailNotificationSubscriptionType((int)emailNotificationSubscriptionType); } + /// + /// Configures the user's opt-in status for push within Braze. + /// + /// public static void SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType pushNotificationSubscriptionType) { _setUserPushNotificationSubscriptionType((int)pushNotificationSubscriptionType); } @@ -395,18 +476,56 @@ public static void RequestLocationInitialization() { } /// - /// Registers a device token with Braze. See - /// https://docs.unity3d.com/2018.4/Documentation/ScriptReference/iOS.NotificationServices.RegisterForNotifications.html + /// Registers a device token (the term for push token on iOS) with Braze. /// /// - /// https://docs.unity3d.com/2018.4/Documentation/ScriptReference/iOS.NotificationServices-deviceToken.html + /// The device token /// - /// public static void RegisterAppboyPushMessages(byte[] registrationDeviceToken) { string registrationTokenBase64 = Convert.ToBase64String(registrationDeviceToken); _registerAppboyPushMessages(registrationTokenBase64); } + /// + /// Prompts the user for push permissions and registers the user to receive push notifications. + /// + /// To subscribe to the result, set a Game Object to listen for + /// events using + /// , or pass in a delegate instance of . + /// + /// + /// If set to true, on iOS 12 and above, the user will be provisionally (silently) authorized + /// to receive quiet push. + /// Otherwise, the user will be shown the native push prompt. + /// + /// + /// An optional delegate instance to receive the user's response to the prompt. + /// + public static void PromptUserForPushPermissions(bool provisional, PushPromptResponseReceived reponseDelegate = null) { + if (reponseDelegate != null) { + _configureInternalListener((int)BrazeUnityMessageType.PUSH_PERMISSIONS_PROMPT_RESPONSE); + BrazeInternalGameObject.setPushPromptResponseReceivedDelegate(reponseDelegate); + } + _promptUserForPushPermissions(provisional); + } + + /// + /// Configures Braze to send push tokens from the OS to the provided delegate. Braze will only listen for push tokens + /// from the OS if automatic push registration is enabled. + /// + /// Tokens passed to Braze via will also cause the delegate to be called. + /// + /// + /// A delegate instance to receive push tokens. + /// + public static void SetPushTokenReceivedFromSystemDelegate(PushTokenReceivedFromSystem tokenDelegate) { + if (tokenDelegate == null) { + return; + } + BrazeInternalGameObject.setPushTokenReceivedFromSystemDelegate(tokenDelegate); + _configureInternalListener((int)BrazeUnityMessageType.PUSH_TOKEN_RECEIVED_FROM_SYSTEM); + } + /// /// Requests a refresh of Braze Geofences for the specified GPS coordinate. /// @@ -429,13 +548,35 @@ public static void AddAlias(string alias, string label) { _addAlias(alias, label); } + /// + /// Used to configure Braze to send messages to GameObjects based + /// on lifecycle events. See for + /// available message types. + /// + /// Any previous configured game object callback will be overwritten. + /// There can only be one active callback per message type. + /// + /// + /// The type of message to send to the target GameObject. + /// + /// + /// The target GameObject. + /// + /// + /// The script method to call on the GameObject. + /// + public static void ConfigureListener(BrazeUnityMessageType messageType, string gameobject, string method) { + _configureListener((int)messageType, gameobject, method); + } + #elif UNITY_ANDROID private static AndroidJavaObject appboyUnityActivity; private static AndroidJavaObject inAppMessageUtils; private static AndroidJavaObject appboyLocationService; + private static AndroidJavaObject unityConfigurationProvider; void Start() { - Debug.Log("Starting Appboy binding for Android clients."); + Debug.Log("Starting Braze binding for Android clients."); } #region Properties @@ -476,6 +617,15 @@ public static AndroidJavaObject AppboyLocationService { } } + public static AndroidJavaObject UnityConfigurationProvider { + get { + if (unityConfigurationProvider == null) { + unityConfigurationProvider = new AndroidJavaObject("com.appboy.unity.configuration.UnityConfigurationProvider", AppboyUnityActivity); + } + return unityConfigurationProvider; + } + } + #endregion private static AndroidJavaObject GetCurrentUser() { @@ -533,6 +683,11 @@ public static void LogPurchase(string productId, string currencyCode, decimal pr Appboy.Call("logPurchase", productId, currencyCode, javaPrice, quantity, appboyProperties); } + /// + /// When you first start using Braze on a device, the user is considered "anonymous". You can use this + /// method to optionally identify a user with a unique ID. + /// + /// public static void ChangeUser(string userId) { Appboy.Call("changeUser", userId); } @@ -651,6 +806,10 @@ public static void SetUserHomeCity(string city) { GetCurrentUser().Call("setHomeCity", city); } + /// + /// Configures the user's opt-in status for email within Braze. + /// + /// public static void SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType emailNotificationSubscriptionType) { using (var notificationTypeClass = new AndroidJavaClass("com.appboy.enums.NotificationSubscriptionType")) { switch (emailNotificationSubscriptionType) { @@ -670,6 +829,10 @@ public static void SetUserEmailNotificationSubscriptionType(AppboyNotificationSu } } + /// + /// Configures the user's opt-in status for push within Braze. + /// + /// public static void SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType pushNotificationSubscriptionType) { using (var notificationTypeClass = new AndroidJavaClass("com.appboy.enums.NotificationSubscriptionType")) { switch (pushNotificationSubscriptionType) { @@ -796,10 +959,30 @@ public static void RemoveFromCustomUserAttributeArray(string key, string value) GetCurrentUser().Call("removeFromCustomAttributeArray", key, value); } + /// + /// Registers a push token with Braze. + /// + /// + /// The push token + /// public static void RegisterAppboyPushMessages(string registrationId) { Appboy.Call("registerAppboyPushMessages", new object[] { registrationId }); } + /// + /// No-op on Android. + /// + /// + /// + public static void PromptUserForPushPermissions(bool provisional, PushPromptResponseReceived reponseDelegate = null) {} + + /// + /// No-op on Android. + /// + /// + /// + public static void SetPushTokenReceivedFromSystemDelegate(PushTokenReceivedFromSystem tokenDelegate) {} + public static void LogInAppMessageClicked(string inAppMessageJSONString) { var inAppMessage = InAppMessageUtils.CallStatic("inAppMessageFromString", appboyUnityActivity, inAppMessageJSONString); InAppMessageUtils.CallStatic("logInAppMessageClick", inAppMessage); @@ -905,96 +1088,61 @@ public static void AddAlias(string alias, string label) { GetCurrentUser().Call("addAlias", alias, label); } + /// + /// Used to configure Braze to send messages to GameObjects based + /// on lifecycle events. See for + /// available message types. + /// + /// + /// The type of message to send to the target GameObject. + /// + /// + /// The target GameObject. + /// + /// + /// The script method to call on the GameObject. + /// + public static void ConfigureListener(BrazeUnityMessageType messageType, string gameobject, string method) { + UnityConfigurationProvider.Call("configureListener", (int)messageType, gameobject, method); + } + #else // Empty implementations of the API, in case the application is being compiled for a platform other than iOS or Android. void Start() { - Debug.Log("Starting no-op Appboy binding for non iOS/Android clients."); - } - - public static void LogCustomEvent(string eventName) { - } - - public static void LogCustomEvent(string eventName, Dictionary properties) { - } - - public static void LogPurchase(string productId, string currencyCode, decimal price, int quantity) { - } - - public static void LogPurchase(string productId, string currencyCode, decimal price, int quantity, Dictionary properties) { - } - - public static void ChangeUser(string userId) { - } - - public static void SetUserFirstName(string firstName) { - } - - public static void SetUserLastName(string lastName) { - } - - public static void SetUserEmail(string email) { - } - - public static void SetUserBio(string bio) { - } - - public static void SetUserGender(Gender gender) { - } - - public static void SetUserDateOfBirth(int year, int month, int day) { - } - - public static void SetUserCountry(string country) { - } - - public static void SetUserHomeCity(string city) { - } - - public static void SetUserIsSubscribedToEmails(bool isSubscribedToEmails) { - } - - public static void SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType emailNotificationSubscriptionType) { - } - - public static void SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType pushNotificationSubscriptionType) { - } - - public static void SetUserPhoneNumber(string phoneNumber) { - } - - public static void SetUserAvatarImageURL(string imageURL) { - } - - public static void SetCustomUserAttribute(string key, bool value) { - } - - public static void SetCustomUserAttribute(string key, int value) { - } - - public static void SetCustomUserAttribute(string key, float value) { - } - - public static void SetCustomUserAttribute(string key, string value) { - } - - public static void SetCustomUserAttributeToNow(string key) { - } - - public static void SetCustomUserAttributeToSecondsFromEpoch(string key, long secondsFromEpoch) { - } - - public static void UnsetCustomUserAttribute(string key) { - } - - public static void IncrementCustomUserAttribute(string key, int incrementValue) { - } - - public static void SetCustomUserAttributeArray(string key, List array, int size) { - } - - public static void AddToCustomUserAttributeArray(string key, string value) { - } + Debug.Log("Starting no-op Braze binding for non iOS/Android clients."); + } + + public static void LogCustomEvent(string eventName) {} + public static void LogCustomEvent(string eventName, Dictionary properties) {} + public static void LogPurchase(string productId, string currencyCode, decimal price, int quantity) {} + public static void LogPurchase(string productId, string currencyCode, decimal price, int quantity, Dictionary properties) {} + + public static void ChangeUser(string userId) {} + + public static void SetUserFirstName(string firstName) {} + public static void SetUserLastName(string lastName) {} + public static void SetUserEmail(string email) {} + public static void SetUserGender(Gender gender) {} + public static void SetUserDateOfBirth(int year, int month, int day) {} + public static void SetUserCountry(string country) {} + public static void SetUserHomeCity(string city) {} + public static void SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType emailNotificationSubscriptionType) {} + public static void SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType pushNotificationSubscriptionType) {} + public static void SetUserPhoneNumber(string phoneNumber) {} + public static void SetUserAvatarImageURL(string imageURL) {} + + public static void SetCustomUserAttribute(string key, bool value) {} + public static void SetCustomUserAttribute(string key, int value) {} + public static void SetCustomUserAttribute(string key, float value) {} + public static void SetCustomUserAttribute(string key, string value) {} + public static void SetCustomUserAttributeToNow(string key) {} + public static void SetCustomUserAttributeToSecondsFromEpoch(string key, long secondsFromEpoch) {} + public static void UnsetCustomUserAttribute(string key) {} + public static void IncrementCustomUserAttribute(string key, int incrementValue) {} + public static void SetCustomUserAttributeArray(string key, List array, int size) {} + public static void AddToCustomUserAttributeArray(string key, string value) {} + public static void RemoveFromCustomUserAttributeArray(string key, string value) {} public static void setUserFacebookData(string facebookId, string firstName, string lastName, string email, string bio, string cityName, Gender? gender, int? numberOfFriends, string birthday) {} @@ -1002,75 +1150,43 @@ public static void setUserFacebookData(string facebookId, string firstName, stri public static void setUserTwitterData(int? twitterUserId, string twitterHandle, string name, string description, int? followerCount, int? followingCount, int? tweetCount, string profileImageUrl) {} - public static void RemoveFromCustomUserAttributeArray(string key, string value) { - } - - public static void RegisterAppboyPushMessages(string registrationId) { - } - - public static void LogInAppMessageClicked(string inAppMessageJSONString) { - } - - public static void LogInAppMessageImpression(string inAppMessageJSONString) { - } - - public static void LogInAppMessageButtonClicked(string inAppMessageJSONString, int buttonID) { - } - - public static void RequestFeedRefresh() { - } - - public static void RequestFeedRefreshFromCache() { - } - - public static void LogFeedDisplayed() { - } - - public static void RequestContentCardsRefresh() { - } - - public static void RequestContentCardsRefreshFromCache() { - } + public static void RegisterAppboyPushMessages(string registrationId) {} + public static void PromptUserForPushPermissions(bool provisional, PushPromptResponseReceived reponseDelegate = null) {} + public static void SetPushTokenReceivedFromSystemDelegate(PushTokenReceivedFromSystem tokenDelegate) {} - public static void LogContentCardsDisplayed() { - } + public static void LogInAppMessageClicked(string inAppMessageJSONString) {} + public static void LogInAppMessageImpression(string inAppMessageJSONString) {} + public static void LogInAppMessageButtonClicked(string inAppMessageJSONString, int buttonID) {} - public static void LogContentCardClicked(string contentCardString) { - } + public static void RequestFeedRefresh() {} + public static void RequestFeedRefreshFromCache() {} + public static void LogFeedDisplayed() {} - public static void LogContentCardImpression(string contentCardString) { - } + public static void RequestContentCardsRefresh() {} + public static void RequestContentCardsRefreshFromCache() {} + public static void LogContentCardsDisplayed() {} + public static void LogContentCardClicked(string contentCardString) {} + public static void LogContentCardImpression(string contentCardString) { } + public static void LogContentCardDismissed(string contentCardString) { } - public static void LogContentCardDismissed(string contentCardString) { - } - - public static void WipeData() { - } - - public static void EnableSDK() { - } - - public static void DisableSDK() { - } + public static void WipeData() {} + public static void EnableSDK() {} + public static void DisableSDK() {} public static string GetInstallTrackingId() { return null; } - public static void SetAttributionData(string network, string campaign, string adgroup, string creative) { - } + public static void SetAttributionData(string network, string campaign, string adgroup, string creative) {} - public static void RequestLocationInitialization() { - } + public static void RequestLocationInitialization() {} + public static void RequestGeofences(decimal latitude, decimal longitude) {} - public static void RequestGeofences(decimal latitude, decimal longitude) { - } + public static void RequestImmediateDataFlush() {} - public static void RequestImmediateDataFlush() { - } + public static void AddAlias(string alias, string label) {} - public static void AddAlias(string alias, string label) { - } + public static void ConfigureListener(BrazeUnityMessageType messageType, string gameobject, string method) {} #endif } } diff --git a/Assets/Plugins/Appboy/Editor.meta b/Assets/Plugins/Appboy/Editor.meta new file mode 100644 index 000000000..0faeb635e --- /dev/null +++ b/Assets/Plugins/Appboy/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed3f31f832ecb4cd98c6f8dbd5ea07f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/Editor/AppboyConfig.cs b/Assets/Plugins/Appboy/Editor/AppboyConfig.cs index 884281497..043d035c9 100644 --- a/Assets/Plugins/Appboy/Editor/AppboyConfig.cs +++ b/Assets/Plugins/Appboy/Editor/AppboyConfig.cs @@ -52,6 +52,10 @@ public class AppboyConfig : ScriptableObject { [SerializeField] private bool iOSDisableAutomaticPushRegistration = false; [SerializeField] + private bool iOSDisableProvisionalAuth = false; + [SerializeField] + private bool iOSDisableAutomaticPushCapability = false; + [SerializeField] private bool iOSPushIsBackgroundEnabled = false; [SerializeField] private string iOSPushReceivedGameObjectName = string.Empty; @@ -173,6 +177,16 @@ public static bool IOSDisableAutomaticPushRegistration { set { SetProperty(ref Instance.iOSDisableAutomaticPushRegistration, value); } } + public static bool IOSDisableProvisionalAuth { + get { return Instance.iOSDisableProvisionalAuth; } + set { SetProperty(ref Instance.iOSDisableProvisionalAuth, value); } + } + + public static bool IOSDisableAutomaticPushCapability { + get { return Instance.iOSDisableAutomaticPushCapability; } + set { SetProperty(ref Instance.iOSDisableAutomaticPushCapability, value); } + } + public static string IOSPushReceivedGameObjectName { get { return Instance.iOSPushReceivedGameObjectName; } set { SetProperty(ref Instance.iOSPushReceivedGameObjectName, value); } diff --git a/Assets/Plugins/Appboy/Editor/AppboyConfigEditor.cs b/Assets/Plugins/Appboy/Editor/AppboyConfigEditor.cs index d0bef0aac..a09bbeba0 100644 --- a/Assets/Plugins/Appboy/Editor/AppboyConfigEditor.cs +++ b/Assets/Plugins/Appboy/Editor/AppboyConfigEditor.cs @@ -129,6 +129,20 @@ private void IOSBuildGUIPush() { EditorGUILayout.LabelField("Disables automatic user registration for push notifications upon app startup.", EditorStyles.wordWrappedMiniLabel); } + AppboyConfig.IOSDisableProvisionalAuth = EditorGUILayout.ToggleLeft(" Disable Provisional Authorization", AppboyConfig.IOSDisableProvisionalAuth); + if (AppboyConfig.IOSDisableProvisionalAuth) { + EditorGUILayout.LabelField("Users will see the native push prompt dialog at app startup.", EditorStyles.miniBoldLabel); + } else { + EditorGUILayout.LabelField("Disables provisional (quiet) authorization. If disabled, users will see the native push prompt dialog at app startup.", EditorStyles.wordWrappedMiniLabel); + } + + AppboyConfig.IOSDisableAutomaticPushCapability = EditorGUILayout.ToggleLeft(" Disable Automatic Push Capability", AppboyConfig.IOSDisableAutomaticPushCapability); + if (AppboyConfig.IOSDisableAutomaticPushCapability) { + EditorGUILayout.LabelField("You will need to manually add the Push Capability to your Xcode project.", EditorStyles.miniBoldLabel); + } else { + EditorGUILayout.LabelField("Disables adding the Xcode Push Capability automatically.", EditorStyles.wordWrappedMiniLabel); + } + AppboyConfig.IOSPushIsBackgroundEnabled = EditorGUILayout.ToggleLeft(" Enable Background Push", AppboyConfig.IOSPushIsBackgroundEnabled); EditorGUILayout.LabelField("Allows the system to wake your app from suspension when a push notification arrives.", EditorStyles.wordWrappedMiniLabel); diff --git a/Assets/Plugins/Appboy/Editor/PostBuild.cs b/Assets/Plugins/Appboy/Editor/PostBuild.cs index 587c3470f..72948bfd0 100644 --- a/Assets/Plugins/Appboy/Editor/PostBuild.cs +++ b/Assets/Plugins/Appboy/Editor/PostBuild.cs @@ -12,15 +12,16 @@ namespace Appboy.Editor { public class PostBuild { #if UNITY_IOS - private const string ProjectSubpath = "/Unity-iPhone.xcodeproj/project.pbxproj"; private const string AppboyAppDelegatePath = "Libraries/Plugins/iOS/AppboyAppDelegate.mm"; private const string PlistSubpath = "/Info.plist"; + private const string MainTargetName = "Unity-iPhone"; private const string ABKEndpointKey = "Endpoint"; private const string ABKLogLevelKey = "LogLevel"; private const string ABKUnityApiKey = "ApiKey"; private const string ABKUnityAutomaticPushIntegrationKey = "IntegratesPush"; private const string ABKUnityDisableAutomaticPushRegistrationKey = "DisableAutomaticPushRegistration"; + private const string ABKUnityDisableProvisionalAuthKey = "DisableProvisionalAuth"; private const string ABKUnityPushReceivedGameObjectKey = "PushReceivedGameObjectName"; private const string ABKUnityPushReceivedCallbackKey = "PushReceivedCallbackMethodName"; private const string ABKUnityPushOpenedGameObjectKey = "PushOpenedGameObjectName"; @@ -34,15 +35,15 @@ public class PostBuild { private const string ABKUnityHandleInAppMessageDisplayKey = "DisplayInAppMessages"; [PostProcessBuildAttribute(1)] - public static void OnPostprocessBuild(BuildTarget target, string buildPath) { + public static void OnPostprocessBuild(BuildTarget target, string path) { if (target == BuildTarget.iOS) { - ModifyProject(buildPath + ProjectSubpath); - ModifyPlist(buildPath + PlistSubpath); + ModifyPlist(path + PlistSubpath); + ModifyProject(path); } } - private static void ModifyProject(string projectPath) { - // Create PBXProject + private static void ModifyProject(string path) { + var projectPath = PBXProject.GetPBXProjectPath(path); PBXProject project = new PBXProject(); project.ReadFromString(File.ReadAllText(projectPath)); @@ -78,8 +79,6 @@ private static void ModifyProject(string projectPath) { "CoreTelephony.framework", "Social.framework", "Accounts.framework", - "AdSupport.framework", - "StoreKit.framework", "CoreLocation.framework", // optional for location tracking "ImageIO.framework", // required by SDWebImage "MobileCoreServices.framework", // required by FLAnimatedImage @@ -110,12 +109,42 @@ private static void ModifyProject(string projectPath) { project.AddBuildProperty(target, "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks"); } + + if (AppboyConfig.IOSIntegratesPush && !AppboyConfig.IOSDisableAutomaticPushCapability) { + AddPushEntitlement( + path, + project, + targets[0] // the main target + ); + } } - // Write changes to XCode project File.WriteAllText(projectPath, project.WriteToString()); } + private static void AddPushEntitlement(string path, PBXProject project, string target) { + var entitlements = new PlistDocument(); + + string entitlementsFilename = MainTargetName + ".entitlements"; + string entitlementsRelativePath = MainTargetName + "/" + entitlementsFilename; + string entitlementsPath = path + "/" + entitlementsRelativePath; + + if (File.Exists(entitlementsPath)) { + entitlements.ReadFromFile(entitlementsPath); + } + + if (entitlements.root["aps-environment"] != null) { + return; + } else { + entitlements.root.SetString("aps-environment", "development"); + } + + project.AddFile(entitlementsRelativePath, entitlementsFilename); + entitlements.WriteToFile(entitlementsPath); + + project.AddBuildProperty(target, "CODE_SIGN_ENTITLEMENTS", entitlementsRelativePath); + } + private static void AddFileToEmbedFrameworks(PBXProject project, string target, string unityPath, string xcodePath) { string frameworkPath = project.AddFile(unityPath, xcodePath, PBXSourceTree.Source); project.AddFileToBuild(target, frameworkPath); @@ -162,6 +191,7 @@ private static void ModifyPlist(string plistPath) { PlistElementArray backgroundModes = (rootDict["UIBackgroundModes"] == null) ? rootDict.CreateArray("UIBackgroundModes") : rootDict["UIBackgroundModes"].AsArray(); backgroundModes.AddString("remote-notification"); } + appboyUnityDict.SetBoolean(ABKUnityDisableProvisionalAuthKey, AppboyConfig.IOSDisableProvisionalAuth); // Set push listeners if (ValidateListenerFields(ABKUnityPushReceivedGameObjectKey, AppboyConfig.IOSPushReceivedGameObjectName, @@ -204,7 +234,7 @@ private static void ModifyPlist(string plistPath) { } private static void DisplayInvalidSettingsWarning(string key, string details) { - EditorUtility.DisplayDialog("Invalid Appboy Settings", "The " + Regex.Replace(key, @"\B[A-Z]", " $0") + " is blank. " + details + " Set this field in Braze > Braze Configuration.", "OK"); + EditorUtility.DisplayDialog("Invalid Braze Settings", "The " + Regex.Replace(key, @"\B[A-Z]", " $0") + " is blank. " + details + " Set this field in Braze > Braze Configuration.", "OK"); } private static bool ValidateField(string key, string value, string errorDetails) { diff --git a/Assets/Plugins/Appboy/Internal.meta b/Assets/Plugins/Appboy/Internal.meta new file mode 100644 index 000000000..b9d548f13 --- /dev/null +++ b/Assets/Plugins/Appboy/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab5b022ad07294763b86968c6e01168b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs b/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs new file mode 100644 index 000000000..f259bf497 --- /dev/null +++ b/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs @@ -0,0 +1,30 @@ +using System; +using UnityEngine; + +namespace Appboy.Internal { + public class BrazeInternalComponent : MonoBehaviour { + + private PushPromptResponseReceived pushPromptResponseReceived; + private PushTokenReceivedFromSystem pushTokenReceivedFromSystem; + + public void setPushPromptResponseReceivedDelegate(PushPromptResponseReceived responseDelegate) { + pushPromptResponseReceived = responseDelegate; + } + + public void setPushTokenReceivedFromSystemDelegate(PushTokenReceivedFromSystem responseDelegate) { + pushTokenReceivedFromSystem = responseDelegate; + } + + public void onPushPromptResponseReceived(String response) { + if (pushPromptResponseReceived != null) { + pushPromptResponseReceived(Convert.ToBoolean(response)); + } + } + + public void onPushTokenReceivedFromSystem(String token) { + if (pushTokenReceivedFromSystem != null) { + pushTokenReceivedFromSystem(token); + } + } + } +} diff --git a/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs.meta b/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs.meta new file mode 100644 index 000000000..30c56d908 --- /dev/null +++ b/Assets/Plugins/Appboy/Internal/BrazeInternalComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 006e4d620bf2d4c11befcede36edae3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs b/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs new file mode 100644 index 000000000..a611d8808 --- /dev/null +++ b/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs @@ -0,0 +1,30 @@ +using System; +using UnityEngine; + +namespace Appboy.Internal { + public class BrazeInternalGameObject : MonoBehaviour { + + private static GameObject instance; + + private static GameObject Instance { + get { + if (instance == null) { + instance = new GameObject("BrazeInternalCallback"); + instance.AddComponent(); + DontDestroyOnLoad(instance); + } + return instance; + } + } + + public static void setPushPromptResponseReceivedDelegate(PushPromptResponseReceived responseDelegate) { + BrazeInternalComponent internalComponent = Instance.GetComponent(); + internalComponent.setPushPromptResponseReceivedDelegate(responseDelegate); + } + + public static void setPushTokenReceivedFromSystemDelegate(PushTokenReceivedFromSystem responseDelegate) { + BrazeInternalComponent internalComponent = Instance.GetComponent(); + internalComponent.setPushTokenReceivedFromSystemDelegate(responseDelegate); + } + } +} diff --git a/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs.meta b/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs.meta new file mode 100644 index 000000000..c38eacf3d --- /dev/null +++ b/Assets/Plugins/Appboy/Internal/BrazeInternalGameObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9874d28efa3d344afa3967391efe52cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/Tests.meta b/Assets/Plugins/Appboy/Tests.meta new file mode 100644 index 000000000..c19dbf970 --- /dev/null +++ b/Assets/Plugins/Appboy/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 745cb92dce1ef4df79ef620ba31f92cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/Tests/AppboyBindingTester.cs b/Assets/Plugins/Appboy/Tests/AppboyBindingTester.cs index 5667a5624..b9f6dcee2 100755 --- a/Assets/Plugins/Appboy/Tests/AppboyBindingTester.cs +++ b/Assets/Plugins/Appboy/Tests/AppboyBindingTester.cs @@ -11,48 +11,47 @@ namespace Appboy { public class AppboyBindingTester : MonoBehaviour { void Start() { + Debug.Log("AppboyBindingTester starting with component name: " + this.name); } void InAppMessageReceivedCallback(string message) { Debug.Log("InAppMessageReceivedCallback message: " + message); IInAppMessage inApp = InAppMessageFactory.BuildInAppMessage(message); Debug.Log("In-app message received: " + inApp); - // Here we are testing the Unity SDK by manually logging the in-app message's click and impression. - // We should only log the click and impression when the in-app message isn't displayed by Appboy but in Unity. - //inApp.LogClicked(); - //inApp.LogImpression(); - if (inApp is IInAppMessageImmersive) { - IInAppMessageImmersive inAppImmersive = inApp as IInAppMessageImmersive; - if (inAppImmersive.Buttons != null && inAppImmersive.Buttons.Count > 0) { - // Here we are testing the Unity SDK by manually logging the in-app message's first button's click. - // We should only log the button click when the in-app message isn't displayed by Appboy but in Unity. - //inAppImmersive.LogButtonClicked(inAppImmersive.Buttons[0].ButtonID); - } - } + } + + void PromptUserForPushPermissionsCallback(string message) { + Debug.Log("User push permission prompt result was: " + message); + } + + void PushTokenReceivedCallback(string message) { + Debug.Log("Push token received: " + message); } void PushNotificationReceivedCallback(string message) { - Debug.Log("PushNotificationReceivedCallback message: " + message); + Debug.Log("Push received callback for Android received: " + message); PushNotification pushNotification = new PushNotification(message); - Debug.Log("Push Notification received: " + pushNotification); + Debug.Log("Push received message parsed into json: " + pushNotification); } void PushNotificationOpenedCallback(string message) { - Debug.Log("PushNotificationOpenedCallback message: " + message); + Debug.Log("Push opened callback for Android received: " + message); PushNotification pushNotification = new PushNotification(message); - Debug.Log("Push Notification opened: " + pushNotification); + Debug.Log("Push opened message parsed into json: " + pushNotification); } void PushNotificationReceivedCallbackForiOS(string message) { + Debug.Log("Push received callback for iOS received: " + message); JSONClass pushNotificationJson = (JSONClass)JSON.Parse(message); ApplePushNotification pushNotification = new ApplePushNotification(pushNotificationJson); - Debug.Log("Push received Notification event: " + pushNotification); + Debug.Log("Push received message parsed into json: " + pushNotification); } void PushNotificationOpenedCallbackForiOS(string message) { + Debug.Log("Push opened callback for iOS received: " + message); JSONClass pushNotificationJson = (JSONClass)JSON.Parse(message); ApplePushNotification pushNotification = new ApplePushNotification(pushNotificationJson); - Debug.Log("Push opened Notification event: " + pushNotification); + Debug.Log("Push opened message parsed into json: " + pushNotification); } void FeedReceivedCallback(string message) { @@ -71,7 +70,7 @@ void ContentCardsReceivedCallback(string message) { JSONClass json = (JSONClass)JSON.Parse(message); if (json["mContentCards"] != null) { JSONArray jsonArray = (JSONArray)JSON.Parse(json["mContentCards"].ToString()); - Debug.Log(String.Format("Parsed content cards array with {0} cards", jsonArray.Count)); + Debug.Log(String.Format("Parsing Content Cards array of size {0}", jsonArray.Count)); for (int i = 0; i < jsonArray.Count; i++) { JSONClass cardJson = jsonArray[i].AsObject; try { diff --git a/Assets/Plugins/Appboy/Utilities.meta b/Assets/Plugins/Appboy/Utilities.meta new file mode 100644 index 000000000..79b09fe9d --- /dev/null +++ b/Assets/Plugins/Appboy/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 581060ca2af7c4a3680c5237d918720f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/models.meta b/Assets/Plugins/Appboy/models.meta new file mode 100644 index 000000000..6017bdfd5 --- /dev/null +++ b/Assets/Plugins/Appboy/models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 78b8d94eadfe241f0976ce862b0b5e64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/models/AppboyNotificationSubscriptionType.cs b/Assets/Plugins/Appboy/models/AppboyNotificationSubscriptionType.cs index e584d9336..931bf87f3 100644 --- a/Assets/Plugins/Appboy/models/AppboyNotificationSubscriptionType.cs +++ b/Assets/Plugins/Appboy/models/AppboyNotificationSubscriptionType.cs @@ -1,9 +1,22 @@ using System.Collections; namespace Appboy.Models { + + /// + /// User opt-in states. + /// public enum AppboyNotificationSubscriptionType { + /// + /// The user has explicitly opted-in. + /// OPTED_IN = 0, + /// + /// The user is subscribed but has not explicitly opted-in. + /// SUBSCRIBED = 1, + /// + /// The user is unsubscribed or has opted-out. + /// UNSUBSCRIBED = 2 } } diff --git a/Assets/Plugins/Appboy/models/Cards.meta b/Assets/Plugins/Appboy/models/Cards.meta new file mode 100644 index 000000000..624772e27 --- /dev/null +++ b/Assets/Plugins/Appboy/models/Cards.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 430a0954c4eb64461bdf334343ac7fe7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/models/Cards/Card.cs b/Assets/Plugins/Appboy/models/Cards/Card.cs index d9c3d2925..7ed4a0c58 100755 --- a/Assets/Plugins/Appboy/models/Cards/Card.cs +++ b/Assets/Plugins/Appboy/models/Cards/Card.cs @@ -48,9 +48,7 @@ public Card(JSONClass json) { } else { for (int i = 0; i < jsonArray.Count; i++) { CardCategory category = (CardCategory)EnumUtils.TryParse(typeof(CardCategory), jsonArray[i], true, CardCategory.NO_CATEGORY); - if (category != CardCategory.NO_CATEGORY) { - Categories.Add(category); - } + Categories.Add(category); } if (Categories.Count == 0) { Categories.Add(CardCategory.NO_CATEGORY); diff --git a/Assets/Plugins/Appboy/models/Feed.cs b/Assets/Plugins/Appboy/models/Feed.cs index 1734de4c3..14bc86158 100755 --- a/Assets/Plugins/Appboy/models/Feed.cs +++ b/Assets/Plugins/Appboy/models/Feed.cs @@ -24,22 +24,22 @@ public Feed(string message) { json = (JSONClass)JSON.Parse(message); if (json["mFeedCards"] != null) { JSONArray jsonArray = (JSONArray)JSON.Parse(json["mFeedCards"].ToString()); - Debug.Log(String.Format("parsed cards array with {0} of cards", jsonArray.Count)); + Debug.Log(String.Format("Parsing News Feed card array of size {0}.", jsonArray.Count)); for (int i = 0; i < jsonArray.Count; i++) { JSONClass cardJson = jsonArray[i].AsObject; try { - Debug.Log(String.Format("Card NO. {0} json string is {1}", i, cardJson)); + Debug.Log(String.Format("Parsing card with json: {0}", cardJson)); Card card = Feed.CreateCardFromJson(cardJson); if (card != null) { Cards.Add(card); } } catch { - Debug.Log(String.Format("Unable to parse card from {0}", cardJson)); + Debug.Log(String.Format("Unable to parse card from json: {0}", cardJson)); } } } } catch { - throw new ArgumentException("Cannot parse feed JSON message."); + throw new ArgumentException("Could not parse News Feed json."); } IsFromOfflineStorage = json["mFromOfflineStorage"].AsBool; diff --git a/Assets/Plugins/Appboy/models/InAppMessage.meta b/Assets/Plugins/Appboy/models/InAppMessage.meta new file mode 100644 index 000000000..76b18beb6 --- /dev/null +++ b/Assets/Plugins/Appboy/models/InAppMessage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df80300b9949a4830a004fc04e20dd68 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/models/InAppMessage/IInAppMessage.cs b/Assets/Plugins/Appboy/models/InAppMessage/IInAppMessage.cs index 873e31f56..e90bb3cc9 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/IInAppMessage.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/IInAppMessage.cs @@ -5,59 +5,42 @@ namespace Appboy.Models.InAppMessage { public interface IInAppMessage { - // BackgroundColor defines the background color of the in-app message. Color? BackgroundColor { get; set; } - // TextColor defines the message text color of the in-app message. Color? TextColor { get; set; } - // Icon defines the font awesome unicode string of the Appboy icon. - // You can choose to display one of the Appboy icons from Appboy dashboard. When you do so, this property will have the - // unicode string of font awesome. + // The font awesome unicode string of the in-app message icon. string Icon { get; set; } - // IconColor defines the font color of icon property. + // The color of the icon text. Color? IconColor { get; set; } - // IconBackgroundColor defines the background color of icon property. + // The color of the icon background. Color? IconBackgroundColor { get; set; } - // ImageURI defines the URI of the image icon on in-app message. - // When there is a iconImage defined, the iconImage will be used and the value of property icon will - // be ignored. string ImageURI { get; set; } - // This property defines the message displayed within the in-app message. string Message { get; set; } - // This property carries extra data in the form of an dictionary which can be sent down via the Appboy Dashboard. - // You may want to design and implement a custom handler to access this data depending on your use-case. Dictionary Extras { get; set; } - // This property defines the action that will be performed when the in-app message is clicked. - // See the ClickAction enum documentation above offers additional details. ClickAction InAppClickAction { get; } - - // When the in-app message's InAppClickAction is URI, clicking on the in-app message will redirect to the uri defined - // in this property. - // - // This property can be a HTTP URI or a protocol URI. + string URI { get; } - - // InAppDismissType defines the dismissal behavior of the in-app message. + DismissType InAppDismissType { get; set; } - // This property defines the number of seconds before the in-app message is automatically dismissed. + // The number of seconds that should elapse before the in-app message is automatically dismissed. int Duration { get; set; } void LogClicked(); void LogImpression(); - // Set InAppClickAction to be NEWS_FEED or NONE + // Use this method for ClickAction.NEWS_FEED or ClickAction.NONE bool SetInAppClickAction(ClickAction clickAction); - // Set InAppClickAction to be URI + // Use this method for ClickAction.URI bool SetInAppClickAction(ClickAction clickAction, string uri); } } diff --git a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageBase.cs b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageBase.cs index 477af1bb4..78667e30c 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageBase.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageBase.cs @@ -7,9 +7,6 @@ using UnityEngine; namespace Appboy.Models.InAppMessage { - - // Use InAppMessageFactory.BuildInAppMessage(InAppMessageString) to get the in-app message with correct type from - // Appboy SDK. public abstract class InAppMessageBase : IInAppMessage { private const int DefaultDuration = 5000; protected string _jsonString; @@ -17,49 +14,33 @@ public abstract class InAppMessageBase : IInAppMessage { private bool _impressionLogged = false; private int _duration; - // BackgroundColor defines the background color of the in-app message. + // The background color of the in-app message. public Color? BackgroundColor { get; set; } - // TextColor defines the message text color of the in-app message. public Color? TextColor { get; set; } - // Icon defines the font awesome unicode string of the Appboy icon. - // You can choose to display one of the Appboy icons from Appboy dashboard. When you do so, this property will have the - // unicode string of font awesome. + // The font awesome unicode string of the in-app message icon. public string Icon { get; set; } - // IconColor defines the font color of icon property. + // The color of the icon text. public Color? IconColor { get; set; } - // IconBackgroundColor defines the background color of icon property. + // The color of the icon background. public Color? IconBackgroundColor { get; set; } - // imageURI defines the URI of the image icon on in-app message. - // When there is a iconImage defined, the iconImage will be used and the value of property icon will - // be ignored. public string ImageURI { get; set; } - // This property defines the message displayed within the in-app message. public string Message { get; set; } - // This property carries extra data in the form of an dictionary which can be sent down via the Appboy Dashboard. - // You may want to design and implement a custom handler to access this data depending on your use-case. public Dictionary Extras { get; set; } - // This property defines the action that will be performed when the in-app message is clicked. - // See the ClickAction enum documentation above offers additional details. public ClickAction InAppClickAction { get; private set; } - // When the in-app message's InAppClickAction is URI, clicking on the in-app message will redirect to the uri defined - // in this property. - // - // This property can be a HTTP URI or a protocol URI. public string URI { get; private set; } - // InAppDismissType defines the dismissal behavior of the in-app message. public DismissType InAppDismissType { get; set; } - // This property defines the number of seconds before the in-app message is automatically dismissed. + // The number of seconds that should elapse before the in-app message is automatically dismissed. public int Duration { get { return _duration; @@ -81,7 +62,7 @@ protected InAppMessageBase() { public InAppMessageBase(JSONClass json) { if (json == null) { - throw new ArgumentNullException("The JSON Class passed to InAppMessage constructor is null."); + throw new ArgumentNullException("Received null JSONClass."); } _jsonString = json.ToString(); Message = json[InAppMessageConstants.MessageKey]; @@ -92,7 +73,7 @@ public InAppMessageBase(JSONClass json) { URI = json[InAppMessageConstants.URIKey]; ImageURI = json[InAppMessageConstants.ImageURLKey]; if (InAppClickAction == ClickAction.URI && URI == null) { - Debug.Log("The click action cannot be set to URI because the uri is null. Setting click action to NONE."); + Debug.Log("Received ClickAction.URI but URI was null. Setting click action to NONE."); InAppClickAction = ClickAction.NONE; } InAppDismissType = (DismissType)EnumUtils.TryParse(typeof(DismissType), json[InAppMessageConstants.DismissTypeKey], true, DismissType.AUTO_DISMISS); @@ -109,7 +90,7 @@ public void LogClicked() { _clickLogged = true; AppboyBinding.LogInAppMessageClicked(_jsonString); } else { - Debug.Log("The in-app message already logged a click."); + Debug.Log("Already logged a click. Doing nothing."); } } @@ -118,7 +99,7 @@ public void LogImpression() { _impressionLogged = true; AppboyBinding.LogInAppMessageImpression(_jsonString); } else { - Debug.Log("The in-app message already logged an impression."); + Debug.Log("Already logged an impression. Doing nothing."); } } @@ -129,7 +110,7 @@ public bool SetInAppClickAction(ClickAction clickAction) { URI = null; return true; } else { - Debug.LogError("A non-null URI is required in order to set the InAppClickAction to URI."); + Debug.Log("A non-null URI is required in order to set the InAppClickAction to URI."); return false; } } diff --git a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageButton.cs b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageButton.cs index 6c97afe34..83531558e 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageButton.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageButton.cs @@ -28,7 +28,7 @@ public InAppMessageButton(JSONClass json) { URI = json[InAppMessageConstants.ButtonURIKey]; ButtonClickAction = (ClickAction)EnumUtils.TryParse(typeof(ClickAction), json[InAppMessageConstants.ButtonClickActionKey], true, ClickAction.NEWS_FEED); if (ButtonClickAction == ClickAction.URI && URI == null) { - Debug.Log("The click action cannot be set to URI because the uri is null. Setting click action to NONE."); + Debug.Log("Required URI not present for URI click action type. Setting click action to NONE."); ButtonClickAction = ClickAction.NONE; } } @@ -39,7 +39,7 @@ public bool SetButtonClickAction(ClickAction clickAction) { URI = null; return true; } else { - Debug.LogError("A non-null URI is required in order to set the ButtonClickAction to URI."); + Debug.Log("A non-null URI is required for ClickAction.URI."); return false; } } diff --git a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageConstants.cs b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageConstants.cs index 819c1626c..89c9dbb5d 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageConstants.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageConstants.cs @@ -4,7 +4,7 @@ namespace Appboy.Models.InAppMessage { public class InAppMessageConstants { - // In-app message JSON keys + // In-app message base public const string MessageKey = "message"; public const string ExtrasKey = "extras"; public const string ClickActionKey = "click_action"; @@ -19,17 +19,17 @@ public class InAppMessageConstants { public const string IconBackgroundColorKey = "icon_bg_color"; public const string TypeKey = "type"; - // In-app message immersive JSON keys + // In-app message immersive public const string HeaderKey = "header"; public const string HeaderTextColorKey = "header_text_color"; public const string CloseButtonColorKey = "close_btn_color"; public const string ButtonsKey = "btns"; - // In-app message slideup jSON keys + // In-app message slideup public const string SlideFromKey = "slide_from"; public const string HideChevronKey = "hide_chevron"; - // In-app message button keys + // In-app message buttons public const string ButtonIDKey = "id"; public const string ButtonTextKey = "text"; public const string ButtonTextColorKey = "text_color"; @@ -37,16 +37,16 @@ public class InAppMessageConstants { public const string ButtonURIKey = "uri"; public const string ButtonClickActionKey = "click_action"; - // This method parses an in-app message JSON string to be a json object + // Safe wrapper for JSON.Parse public static JSONClass JSONObjectFromString(string JSONString) { if (String.IsNullOrEmpty(JSONString)) { - Debug.LogError("Slideup JSON Message cannot be null or empty."); + Debug.Log("JSON string was null or empty."); } JSONClass json = null; try { json = (JSONClass)JSON.Parse(JSONString); } catch { - Debug.LogError(String.Format("Cannot parse in-app message JSON message {0}.", JSONString)); + Debug.Log(String.Format("Could not parse JSON {0}.", JSONString)); } return json; } diff --git a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageFactory.cs b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageFactory.cs index 9a6622ff0..0eb826192 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageFactory.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageFactory.cs @@ -2,13 +2,23 @@ using Appboy.Utilities; namespace Appboy.Models.InAppMessage { - // This class is the in-app message builder. Passing the in-app message JSON string from Appboy, and the Builder will - // return a corresponding InAppMessageSlideup, InAppMessageModal or InAppMessageFull; public class InAppMessageFactory { + + /// + /// Builds in-app message instances from serialized in-app message objects. + /// + /// Use this method to create a useful model object from the in-app message string + /// passed into your gameobject callback. + /// + /// + /// The serialized in-app message object. + /// + /// + /// An IInAppMessage instance populated by the serialized in-app message object. + /// public static IInAppMessage BuildInAppMessage(string inAppMessageJSONString) { JSONClass json = InAppMessageConstants.JSONObjectFromString(inAppMessageJSONString); if (json != null) { - // If the type is specified, create corresponding in-app message, otherwise, create an in-app message slideup. InAppMessageType type = (InAppMessageType)EnumUtils.TryParse(typeof(InAppMessageType), json[InAppMessageConstants.TypeKey], true, InAppMessageType.SLIDEUP); switch (type) { case InAppMessageType.FULL: diff --git a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageImmersiveBase.cs b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageImmersiveBase.cs index d2d3f61e9..cdf83c1c1 100644 --- a/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageImmersiveBase.cs +++ b/Assets/Plugins/Appboy/models/InAppMessage/InAppMessageImmersiveBase.cs @@ -31,7 +31,7 @@ public InAppMessageImmersiveBase(JSONClass json) : base(json) { if (json[InAppMessageConstants.ButtonsKey] != null) { Buttons = new List(); JSONArray jsonArray = (JSONArray)JSON.Parse(json[InAppMessageConstants.ButtonsKey].ToString()); - Debug.Log(String.Format("parse in-app message with {0} buttons", jsonArray.Count)); + Debug.Log(String.Format("Parsing in-app message with {0} buttons", jsonArray.Count)); for (int i = 0; i < jsonArray.Count; i++) { JSONClass buttonJson = jsonArray[i].AsObject; try { @@ -52,7 +52,7 @@ public void LogButtonClicked(int buttonID) { _buttonClickLogged = true; AppboyBinding.LogInAppMessageButtonClicked(_jsonString, buttonID); } else { - Debug.Log("The in-app message already log a button clicked."); + Debug.Log("Already logged a button click. Doing nothing."); } } } diff --git a/Assets/Plugins/Appboy/models/obj.meta b/Assets/Plugins/Appboy/models/obj.meta new file mode 100644 index 000000000..278ed818b --- /dev/null +++ b/Assets/Plugins/Appboy/models/obj.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d79af308526e94b638dbb283c6d7f262 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Appboy/obj.meta b/Assets/Plugins/Appboy/obj.meta new file mode 100644 index 000000000..7f98ed8da --- /dev/null +++ b/Assets/Plugins/Appboy/obj.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 57578cce0fa9044b2b8ccda087822d0d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS.meta b/Assets/Plugins/iOS.meta new file mode 100644 index 000000000..25a49faba --- /dev/null +++ b/Assets/Plugins/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6d9c7041e643e49958759c15a0d3b847 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/AppboyAppDelegate.mm b/Assets/Plugins/iOS/AppboyAppDelegate.mm index a3af000e4..fdffca311 100644 --- a/Assets/Plugins/iOS/AppboyAppDelegate.mm +++ b/Assets/Plugins/iOS/AppboyAppDelegate.mm @@ -6,66 +6,68 @@ @interface AppboyAppDelegate : UnityAppController +@property (nonatomic,copy) NSDictionary *brazeUnityPlist; + @end @implementation AppboyAppDelegate : UnityAppController # pragma mark - UIApplicationDelegate methods -- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { [super application:application didFinishLaunchingWithOptions:launchOptions]; NSLog(@"AppboyAppDelegate called from application:didFinishLaunchingWithOptions:"); - [[AppboyUnityManager sharedInstance] parsePlist]; + self.brazeUnityPlist = [[AppboyUnityManager sharedInstance] parsePlist]; - // Initialize Appboy + // Initialize Braze [Appboy startWithApiKey:[[AppboyUnityManager sharedInstance] getApiKeyFromUnity] inApplication:application withLaunchOptions:launchOptions withAppboyOptions:@{ABKSDKFlavorKey: @(UNITY)}]; // Set listeners - [[AppboyUnityManager sharedInstance] setListeners]; + [[AppboyUnityManager sharedInstance] setListenersFromPList]; // Register for push notifications - [[AppboyUnityManager sharedInstance] registerForRemoteNotifications]; + if ([self.brazeUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue] && + ![self.brazeUnityPlist[ABKUnityDisableAutomaticPushRegistrationKey] boolValue]) { + BOOL provisional = ![self.brazeUnityPlist[ABKUnityDisableProvisionalAuthKey] boolValue]; + [[AppboyUnityManager sharedInstance] registerForRemoteNotificationsWithProvisional:provisional]; + } return YES; } -- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { if ([UnityAppController instancesRespondToSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]) { [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } - NSLog(@"AppboyAppDelegate called from application:didRegisterForRemoteNotificationsWithDeviceToken with token %@", deviceToken); - // Register push token with Appboy - [[AppboyUnityManager sharedInstance] registerPushToken:deviceToken]; -} - -- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { - if ([UnityAppController instancesRespondToSelector:@selector(application:didReceiveRemoteNotification:)]) { - [super application:application didReceiveRemoteNotification:userInfo]; + // Register device token with Braze + if ([self.brazeUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { + NSLog(@"Automatic push integration enabled. Sending device token to Braze: %@", deviceToken); + [[AppboyUnityManager sharedInstance] registerPushToken:deviceToken]; + } else{ + NSLog(@"Automatic push integration disabled. Ignoring device token %@", deviceToken); } - NSLog(@"AppboyAppDelegate called from application:didReceiveRemoteNotification:. UIApplicationState is %ld", (long)[[UIApplication sharedApplication] applicationState]); - - // Pass notification to Appboy - [[AppboyUnityManager sharedInstance] registerApplication:application - didReceiveRemoteNotification:userInfo - fetchCompletionHandler:nil]; } -- (void) application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo +- (void)application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { if ([UnityAppController instancesRespondToSelector:@selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) { [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } NSLog(@"AppboyAppDelegate called from application:didReceiveRemoteNotification:fetchCompletionHandler:. UIApplicationState is %ld", (long)[[UIApplication sharedApplication] applicationState]); - // Pass notification to Appboy - [[AppboyUnityManager sharedInstance] registerApplication:application - didReceiveRemoteNotification:userInfo - fetchCompletionHandler:completionHandler]; + // Pass notification to Braze + if ([self.brazeUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { + [[AppboyUnityManager sharedInstance] registerApplication:application + didReceiveRemoteNotification:userInfo + fetchCompletionHandler:completionHandler]; + } else { + NSLog(@"Automatic push integration disabled. Not forwarding notification."); + } } @end diff --git a/Assets/Plugins/iOS/AppboyBinding.m b/Assets/Plugins/iOS/AppboyBinding.m index fc2a19882..c2469129c 100644 --- a/Assets/Plugins/iOS/AppboyBinding.m +++ b/Assets/Plugins/iOS/AppboyBinding.m @@ -5,6 +5,12 @@ char* convertNSStringToCString(const NSString* nsString); +# pragma mark - Appboy + +void _changeUser(const char* userId) { + [[Appboy sharedInstance] changeUser:GetStringParam(userId)]; +} + void _logCustomEvent(const char* eventName, const char* properties) { NSMutableDictionary *eventProperties = [NSMutableDictionary dictionaryWithCapacity:1]; if (properties != NULL && properties != nil) { @@ -14,11 +20,7 @@ void _logCustomEvent(const char* eventName, const char* properties) { options:NSJSONReadingMutableContainers error:&jsonError]; } - [[AppboyUnityManager sharedInstance] logCustomEvent:GetStringParam(eventName) withProperties:eventProperties]; -} - -void _changeUser(const char* userId) { - [[AppboyUnityManager sharedInstance] changeUser:GetStringParam(userId)]; + [[Appboy sharedInstance] logCustomEvent:GetStringParam(eventName) withProperties:eventProperties]; } void _logPurchase(const char* productId, const char* currencyCode, const char* price, int quantity, const char* properties) { @@ -30,134 +32,176 @@ void _logPurchase(const char* productId, const char* currencyCode, const char* p options:NSJSONReadingMutableContainers error:&jsonError]; } - [[AppboyUnityManager sharedInstance] logPurchase:GetStringParam(productId) - inCurrency:GetStringParam(currencyCode) - atPrice:GetStringParam(price) - withQuantity:quantity - withProperties:purchaseProperties]; + [[Appboy sharedInstance] logPurchase:GetStringParam(productId) + inCurrency:GetStringParam(currencyCode) + atPrice:[NSDecimalNumber decimalNumberWithString:GetStringParam(price)] + withQuantity:quantity + andProperties:(NSDictionary *)purchaseProperties]; + +} + +void _requestGeofences(int latitude, int longitude) { + [[Appboy sharedInstance] requestGeofencesWithLongitude:longitude + latitude:latitude]; } +void _requestImmediateDataFlush() { + [[Appboy sharedInstance] flushDataAndProcessRequestQueue]; +} + +void _addAlias(const char* alias, const char* label) { + [[Appboy sharedInstance].user addAlias:GetStringParam(alias) withLabel:GetStringParam(label)]; +} + +# pragma mark - ABKUser + void _setUserFirstName(const char* firstName) { - [[AppboyUnityManager sharedInstance] setUserFirstName:GetStringParam(firstName)]; + [Appboy sharedInstance].user.firstName = GetStringParam(firstName); } void _setUserLastName(const char* lastName) { - [[AppboyUnityManager sharedInstance] setUserLastName:GetStringParam(lastName)]; + [Appboy sharedInstance].user.lastName = GetStringParam(lastName); } void _setUserPhoneNumber(const char* phoneNumber) { - [[AppboyUnityManager sharedInstance] setUserPhoneNumber:GetStringParam(phoneNumber)]; + [Appboy sharedInstance].user.phone = GetStringParam(phoneNumber); } void _setUserAvatarImageURL(const char* imageURL) { - [[AppboyUnityManager sharedInstance] setUserAvatarImageURL:GetStringParam(imageURL)]; + [Appboy sharedInstance].user.avatarImageURL = GetStringParam(imageURL); } void _setUserEmail(const char* email) { - [[AppboyUnityManager sharedInstance] setUserEmail:GetStringParam(email)]; -} - -void _setUserBio(const char* bio) { - [[AppboyUnityManager sharedInstance] setUserBio:GetStringParam(bio)]; + [Appboy sharedInstance].user.email = GetStringParam(email); } void _setUserGender(int gender) { - [[AppboyUnityManager sharedInstance] setUserGender:gender]; + [[Appboy sharedInstance].user setGender:(ABKUserGenderType)gender]; } void _setUserDateOfBirth(int year, int month, int day) { - [[AppboyUnityManager sharedInstance] setUserDateOfBirthToYear:year Month:month andDay:day]; + NSDateComponents *comps = [[NSDateComponents alloc] init]; + [comps setDay:day]; + [comps setMonth:month]; + [comps setYear:year]; + NSCalendar *gregorian = [[NSCalendar alloc] + initWithCalendarIdentifier:NSGregorianCalendar]; + NSDate *date = [gregorian dateFromComponents:comps]; + [Appboy sharedInstance].user.dateOfBirth = date; } void _setUserCountry(const char* country) { - [[AppboyUnityManager sharedInstance] setUserCountry:GetStringParam(country)]; + [Appboy sharedInstance].user.country = GetStringParam(country); } void _setUserHomeCity(const char* city) { - [[AppboyUnityManager sharedInstance] setUserHomeCity:GetStringParam(city)]; -} - -void _setUserIsSubscribedToEmails(bool isSubscribedToEmails) { - [[AppboyUnityManager sharedInstance] setUserIsSubscribedToEmails:isSubscribedToEmails]; + [Appboy sharedInstance].user.homeCity = GetStringParam(city); } void _setUserEmailNotificationSubscriptionType(int emailNotificationSubscriptionType) { - [[AppboyUnityManager sharedInstance] setUserEmailNotificationSubscriptionType:emailNotificationSubscriptionType]; + [[Appboy sharedInstance].user setEmailNotificationSubscriptionType:(ABKNotificationSubscriptionType)emailNotificationSubscriptionType]; } void _setUserPushNotificationSubscriptionType(int pushNotificationSubscriptionType) { - [[AppboyUnityManager sharedInstance] setUserPushNotificationSubscriptionType:pushNotificationSubscriptionType]; + [[Appboy sharedInstance].user setPushNotificationSubscriptionType:(ABKNotificationSubscriptionType)pushNotificationSubscriptionType]; } -void _setCustomUserAttributeBool(const char* key, bool val) { - [[AppboyUnityManager sharedInstance] setUserCustomAttributeWithKey:GetStringParam(key) andBOOLValue:val]; +void _setCustomUserAttributeBool(const char* key, bool value) { + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andBOOLValue:value]; } -void _setCustomUserAttributeInt(const char* key, int val) { - [[AppboyUnityManager sharedInstance] setUserCustomAttributeWithKey:GetStringParam(key) andIntegerValue:val]; +void _setCustomUserAttributeInt(const char* key, int value) { + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andIntegerValue:value]; } -void _setCustomUserAttributeFloat(const char* key, float val) { - NSString *floatString = [NSString stringWithFormat:@"%f", val]; - [[AppboyUnityManager sharedInstance] setUserCustomAttributeWithKey:GetStringParam(key) andDoubleValue:[floatString doubleValue]]; +void _setCustomUserAttributeFloat(const char* key, float value) { + NSString *floatString = [NSString stringWithFormat:@"%f", value]; + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andDoubleValue:[floatString doubleValue]]; } -void _setCustomUserAttributeString(const char* key, const char* val) { - [[AppboyUnityManager sharedInstance] setUserCustomAttributeWithKey:GetStringParam(key) andStringValue:GetStringParam(val)]; +void _setCustomUserAttributeString(const char* key, const char* value) { + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andStringValue:GetStringParam(value)]; } void _setCustomUserAttributeToNow(const char* key) { - [[AppboyUnityManager sharedInstance] setUserCustomAttributeToNowWithKey:GetStringParam(key)]; + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andDateValue:[NSDate date]]; } void _setCustomUserAttributeToSecondsFromEpoch(const char* key, long seconds) { - [[AppboyUnityManager sharedInstance] setUserCustomAttributeWithKey:GetStringParam(key) toDateAsSecondsFromEpoch:(NSTimeInterval)seconds]; + [[Appboy sharedInstance].user setCustomAttributeWithKey:GetStringParam(key) andDateValue:[NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)seconds]]; } void _unsetCustomUserAttribute(const char* key) { - [[AppboyUnityManager sharedInstance] unsetUserCustomAttributeWithKey:GetStringParam(key)]; + [[Appboy sharedInstance].user unsetCustomAttributeWithKey:GetStringParam(key)]; } void _incrementCustomUserAttribute(const char* key, int incrementValue) { - [[AppboyUnityManager sharedInstance] incrementCustomUserAttributeWithKey:GetStringParam(key) by:incrementValue]; + [[Appboy sharedInstance].user incrementCustomUserAttribute:GetStringParam(key) by:incrementValue]; } void _setCustomUserAttributeArray(const char* key, const char* array[], int size) { if (array == NULL || array == nil) { - [[AppboyUnityManager sharedInstance] setCustomAttributeArrayWithKey:GetStringParam(key) - array:nil]; + [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:GetStringParam(key) array:nil]; } else if (size == 0) { - [[AppboyUnityManager sharedInstance] setCustomAttributeArrayWithKey:GetStringParam(key) - array:[NSMutableArray arrayWithCapacity:1]]; + [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:GetStringParam(key) array:[NSMutableArray arrayWithCapacity:1]]; } else { NSMutableArray *customAttributeArray = [NSMutableArray arrayWithCapacity:size]; for (int i = 0; i < size; i ++) { NSString *value = GetStringParam(array[i]); [customAttributeArray addObject:value]; } - [[AppboyUnityManager sharedInstance] setCustomAttributeArrayWithKey:GetStringParam(key) - array:customAttributeArray]; + [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:GetStringParam(key) array:customAttributeArray]; } } void _addToCustomUserAttributeArray(const char* key, const char* value) { - [[AppboyUnityManager sharedInstance] addToCustomAttributeArrayWithKey:GetStringParam(key) value:GetStringParam(value)]; + [[Appboy sharedInstance].user addToCustomAttributeArrayWithKey:GetStringParam(key) value:GetStringParam(value)]; } void _removeFromCustomUserAttributeArray(const char* key, const char* value) { - [[AppboyUnityManager sharedInstance] removeFromCustomAttributeArrayWithKey:GetStringParam(key) value:GetStringParam(value)]; + [[Appboy sharedInstance].user removeFromCustomAttributeArrayWithKey:GetStringParam(key) value:GetStringParam(value)]; } +void _setAttributionData(const char* network, const char* campaign,const char* adgroup, const char* creative) { + ABKAttributionData *attributionData = [[ABKAttributionData alloc] + initWithNetwork:GetStringParam(network) + campaign:GetStringParam(campaign) + adGroup:GetStringParam(adgroup) + creative:GetStringParam(creative)]; + [[Appboy sharedInstance].user setAttributionData:attributionData]; +} + +# pragma mark - Social Media void _setUserFacebookData(const char* facebookId, const char* firstName, const char* lastName, const char* email, const char* bio, const char* cityName, int gender, int numberOfFriends, const char* birthday) { - [[AppboyUnityManager sharedInstance] setUserFacebookData:GetStringParam(facebookId) firstName:GetStringParam(firstName) lastName:GetStringParam(lastName) email:GetStringParam(email) - bio:GetStringParam(bio) cityName:GetStringParam(cityName) gender:gender numberOfFriends:numberOfFriends birthday:GetStringParam(birthday)]; + [[AppboyUnityManager sharedInstance] setUserFacebookData:GetStringParam(facebookId) + firstName:GetStringParam(firstName) + lastName:GetStringParam(lastName) + email:GetStringParam(email) + bio:GetStringParam(bio) + cityName:GetStringParam(cityName) + gender:gender + numberOfFriends:numberOfFriends + birthday:GetStringParam(birthday)]; } void _setUserTwitterData(int twitterUserId, const char* twitterHandle, const char* name, const char* description, int followerCount, int followingCount, int tweetCount, const char* profileImageUrl) { - [[AppboyUnityManager sharedInstance] setUserTwitterData:twitterUserId twitterHandle:GetStringParam(twitterHandle) name:GetStringParam(name) description:GetStringParam(description) followerCount:followerCount followingCount:followingCount tweetCount:tweetCount profileImageUrl:GetStringParam(profileImageUrl)]; + [[AppboyUnityManager sharedInstance] setUserTwitterData:twitterUserId + twitterHandle:GetStringParam(twitterHandle) + name:GetStringParam(name) + description:GetStringParam(description) + followerCount:followerCount + followingCount:followingCount + tweetCount:tweetCount + profileImageUrl:GetStringParam(profileImageUrl)]; +} + +# pragma mark - In-app message analytics + +void _logInAppMessageImpression(const char* inAppMessageJSONString) { + [[AppboyUnityManager sharedInstance] logInAppMessageImpression:GetStringParam(inAppMessageJSONString)]; } void _logInAppMessageClicked(const char* inAppMessageJSONString) { @@ -168,10 +212,14 @@ void _logInAppMessageButtonClicked(const char* inAppMessageJSONString, int butto [[AppboyUnityManager sharedInstance] logInAppMessageButtonClicked:GetStringParam(inAppMessageJSONString) withButtonID:buttonID]; } -void _logInAppMessageImpression(const char* inAppMessageJSONString) { - [[AppboyUnityManager sharedInstance] logInAppMessageImpression:GetStringParam(inAppMessageJSONString)]; +# pragma mark - In-app message display + +void _displayNextInAppMessage(bool withDelegate) { + [[AppboyUnityManager sharedInstance] displayNextInAppMessageWithDelegate:withDelegate]; } +# pragma mark - News Feed analytics + void _logCardImpression(const char* cardJSONString) { [[AppboyUnityManager sharedInstance] logCardImpression:GetStringParam(cardJSONString)]; } @@ -180,6 +228,12 @@ void _logCardClicked(const char* cardJSONString) { [[AppboyUnityManager sharedInstance] logCardClicked:GetStringParam(cardJSONString)]; } +void _logFeedDisplayed() { + [[Appboy sharedInstance] logFeedDisplayed]; +} + +# pragma mark - News Feed refresh + void _requestFeedRefresh() { [[AppboyUnityManager sharedInstance] requestFeedRefresh]; } @@ -188,6 +242,8 @@ void _requestFeedRefreshFromCache() { [[AppboyUnityManager sharedInstance] requestFeedFromCache:nil]; } +# pragma mark - Content Card analytics + void _logContentCardImpression(const char* cardJSONString) { [[AppboyUnityManager sharedInstance] logContentCardImpression:GetStringParam(cardJSONString)]; } @@ -200,10 +256,12 @@ void _logContentCardDismissed(const char* cardJSONString) { [[AppboyUnityManager sharedInstance] logContentCardDismissed:GetStringParam(cardJSONString)]; } -void _logFeedDisplayed() { - [[AppboyUnityManager sharedInstance] logFeedDisplayed]; +void _logContentCardsDisplayed() { + [[Appboy sharedInstance] logContentCardsDisplayed]; } +# pragma mark - Content Card refresh + void _requestContentCardsRefresh() { [[AppboyUnityManager sharedInstance] requestContentCardsRefresh]; } @@ -212,52 +270,45 @@ void _requestContentCardsRefreshFromCache() { [[AppboyUnityManager sharedInstance] requestContentCardsFromCache:nil]; } -void _logContentCardsDisplayed() { - [[AppboyUnityManager sharedInstance] logContentCardsDisplayed]; -} - -void _displayNextInAppMessage(bool withDelegate) { - [[AppboyUnityManager sharedInstance] displayNextInAppMessageWithDelegate:withDelegate]; -} +# pragma mark - Data management void _wipeData() { - [AppboyUnityManager wipeDataAndDisableForAppRun]; + [Appboy wipeDataAndDisableForAppRun]; } void _enableSDK() { - [AppboyUnityManager requestEnableSDKOnNextAppRun]; + [Appboy disableSDK]; } void _disableSDK() { - [AppboyUnityManager disableSDK]; + [Appboy requestEnableSDKOnNextAppRun]; } -void _setAttributionData(const char* network, const char* campaign,const char* adgroup, const char* creative) { - [[AppboyUnityManager sharedInstance] setAttributionData:GetStringParam(network) campaign:GetStringParam(campaign) adgroup:GetStringParam(adgroup) creative:GetStringParam(creative)]; -} +# pragma mark - Push -void _requestGeofences(int latitude, int longitude) { - [[Appboy sharedInstance] requestGeofencesWithLongitude:longitude - latitude:latitude]; +void _registerAppboyPushMessages(const char* tokenBase64) { + [[AppboyUnityManager sharedInstance] registerPushTokenBase64:GetStringParam(tokenBase64)]; } -void _requestImmediateDataFlush() { - [[Appboy sharedInstance] flushDataAndProcessRequestQueue]; +void _promptUserForPushPermissions(bool provisional) { + [[AppboyUnityManager sharedInstance] registerForRemoteNotificationsWithProvisional:provisional]; } -void _registerAppboyPushMessages(const char* tokenBase64) { - [[AppboyUnityManager sharedInstance] registerPushTokenBase64:GetStringParam(tokenBase64)]; -} +# pragma mark - Device Id char* _getInstallTrackingId() { NSString* deviceId = [[Appboy sharedInstance] getDeviceId]; return convertNSStringToCString(deviceId); } -void _addAlias(const char* alias, const char* label) { - [[AppboyUnityManager sharedInstance] addAlias:GetStringParam(alias) withLabel:GetStringParam(label)]; +# pragma mark - Gameobject callbacks + +void _configureListener(int messageType, const char* gameobject, const char* method) { + [[AppboyUnityManager sharedInstance] configureListenerFor:messageType withGameObject:GetStringParam(gameobject) withMethod:GetStringParam(method)]; } +# pragma mark - Internal + char* convertNSStringToCString(const NSString* nsString) { if (nsString == NULL) { return NULL; @@ -269,3 +320,7 @@ void _addAlias(const char* alias, const char* label) { return cString; } + +void _configureInternalListener(int messageType) { + [[AppboyUnityManager sharedInstance] configureInternalListenerFor:messageType]; +} diff --git a/Assets/Plugins/iOS/AppboyUnityManager.h b/Assets/Plugins/iOS/AppboyUnityManager.h index 67cd3fdd6..f5acba03d 100644 --- a/Assets/Plugins/iOS/AppboyUnityManager.h +++ b/Assets/Plugins/iOS/AppboyUnityManager.h @@ -4,6 +4,7 @@ static NSString *const ABKUnityApiKey = @"ApiKey"; static NSString *const ABKUnityAutomaticPushIntegrationKey = @"IntegratesPush"; static NSString *const ABKUnityDisableAutomaticPushRegistrationKey = @"DisableAutomaticPushRegistration"; +static NSString *const ABKUnityDisableProvisionalAuthKey = @"DisableProvisionalAuth"; static NSString *const ABKUnitySetPushListenerKey = @"SetPushListener"; static NSString *const ABKUnityPushReceivedGameObjectKey = @"PushReceivedGameObjectName"; static NSString *const ABKUnityPushReceivedCallbackKey = @"PushReceivedCallbackMethodName"; @@ -17,6 +18,20 @@ static NSString *const ABKUnityContentCardsGameObjectKey = @"ContentCardsGameObj static NSString *const ABKUnityContentCardsCallbackKey = @"ContentCardsCallbackMethodName"; static NSString *const ABKUnityHandleInAppMessageDisplayKey = @"DisplayInAppMessages"; +/** + * These must correspond 1:1 to BrazeUnityMessageType in AppboyBinding.cs. + */ +typedef NS_ENUM(NSInteger, ABKUnityMessageType) { + ABKPushPermissionsPromptResponse = 0, + ABKPushTokenReceivedFromSystem = 1, + ABKPushReceived = 2, + ABKPushOpened = 3, + ABKPushDeleted = 4, + ABKInAppMessageReceived = 5, + ABKNewsFeedUpdated = 6, + ABKContentCardsUpdated = 7 +}; + @interface AppboyUnityManager : NSObject @property (nonatomic,copy) NSDictionary *appboyUnityPlist; @@ -30,71 +45,90 @@ static NSString *const ABKUnityHandleInAppMessageDisplayKey = @"DisplayInAppMess @property (nonatomic, copy) NSString *unityPushReceivedCallbackFunctionName; @property (nonatomic, copy) NSString *unityPushOpenedGameObjectName; @property (nonatomic, copy) NSString *unityPushOpenedCallbackFunctionName; +@property (nonatomic, copy) NSString *unityPushPermissionsPromptResponseGameObjectName; +@property (nonatomic, copy) NSString *unityPushPermissionsPromptResponseFunctionName; +@property (nonatomic, copy) NSString *unityPushTokenReceivedFromSystemGameObjectName; +@property (nonatomic, copy) NSString *unityPushTokenReceivedFromSystemFunctionName; +@property (nonatomic) BOOL sendInternalPushPermissionsPromptResponse; +@property (nonatomic) BOOL sendPushTokenReceivedFromSystem; + (AppboyUnityManager *) sharedInstance; -- (void) showStreamView; -- (void) logCustomEvent:(NSString *)eventName withProperties:(NSDictionary *)properties; -- (void) changeUser:(NSString *)userId; -- (void) logPurchase:(NSString *)productId inCurrency:(NSString *)currencyCode - atPrice:(NSString *)price withQuantity:(NSUInteger)quantity withProperties:(NSDictionary *)properties; -- (void) setUserFirstName:(NSString *)firstName; -- (void) setUserLastName:(NSString *)lastName; -- (void) setUserPhoneNumber:(NSString *)number; -- (void) setUserAvatarImageURL:(NSString *)imageURL; -- (void) setUserEmail:(NSString *)email; -- (void) setUserBio:(NSString *)bio; -- (void) setUserGender:(NSInteger)gender; -- (void) setUserDateOfBirthToYear:(NSInteger)year Month:(NSInteger)month andDay:(NSInteger)day; -- (void) setUserCountry:(NSString *)country; -- (void) setUserHomeCity:(NSString *)city; -- (void) setUserIsSubscribedToEmails:(BOOL)subscribedToEmail; -- (void) setUserEmailNotificationSubscriptionType:(NSInteger)emailNotificationSubscriptionType; -- (void) setUserPushNotificationSubscriptionType:(NSInteger)pushNotificationSubscriptionType; -- (void) setUserCustomAttributeWithKey:(NSString *)key andBOOLValue:(BOOL)value; -- (void) setUserCustomAttributeWithKey:(NSString *)key andIntegerValue:(NSInteger)value; -- (void) setUserCustomAttributeWithKey:(NSString *)key andDoubleValue:(double)value; -- (void) setUserCustomAttributeWithKey:(NSString *)key andStringValue:(NSString *)value; -- (void) setUserCustomAttributeToNowWithKey:(NSString *)key; -- (void) setUserCustomAttributeWithKey:(NSString *)key toDateAsSecondsFromEpoch:(NSTimeInterval)seconds; -- (void) unsetUserCustomAttributeWithKey:(NSString *)key; -- (void) incrementCustomUserAttributeWithKey:(NSString *)key by:(NSInteger)incrementValue; -- (void) setCustomAttributeArrayWithKey:(NSString *)key array:(NSArray *)valueArray; -- (void) addToCustomAttributeArrayWithKey:(NSString *)key value:(NSString *)value; -- (void) removeFromCustomAttributeArrayWithKey:(NSString *)key value:(NSString *)value; -- (void) setAttributionData:(NSString *)network campaign:(NSString *)campaign adgroup:(NSString *)adgroup creative:(NSString *)creative; -- (void) setUserFacebookData:(NSString *)facebookId firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email bio:(NSString *)bio cityName:(NSString *)cityName gender:(NSInteger)gender numberOfFriends:(NSInteger)numberOfFriends birthday:(NSString *)birthday; -- (void) setUserTwitterData:(NSInteger)twitterUserId twitterHandle:(NSString *)twitterHandle name:(NSString *)name description:(NSString *)description followerCount:(NSInteger)followerCount followingCount:(NSInteger)followingCount tweetCount:(NSInteger)tweetCount profileImageUrl:(NSString *)profileImageUrl; -- (void) parsePlist; -- (NSString *) getApiKeyFromUnity; -- (void) setListeners; -- (void) addInAppMessageListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; -- (void) addFeedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; -- (void) addContentCardsListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; -- (void) addPushReceivedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; -- (void) addPushOpenedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; -- (void) registerForRemoteNotifications; -- (void) registerPushToken:(NSData *)deviceToken; -- (void) registerPushTokenBase64:(NSString *)deviceTokenBase64; -- (void) registerApplication:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; -- (void) logInAppMessageClicked:(NSString *)inAppMessageJSONString; -- (void) logInAppMessageButtonClicked:(NSString *)inAppMessageJSONString withButtonID:(NSInteger)buttonID; -- (void) logInAppMessageImpression:(NSString *)inAppMessageJSONString; -- (void) logCardImpression:(NSString *)cardJSONString; -- (void) logCardClicked:(NSString *)cardJSONString; -- (void) requestFeedRefresh; -- (void) requestFeedFromCache:(NSNotification *)notification; -- (void) logFeedDisplayed; -- (void) requestContentCardsRefresh; -- (void) requestContentCardsFromCache:(NSNotification *)notification; -- (void) logContentCardsDisplayed; -- (void) logContentCardImpression:(NSString *)cardJSONString; -- (void) logContentCardClicked:(NSString *)cardJSONString; -- (void) logContentCardDismissed:(NSString *)cardJSONString; -- (void) displayNextInAppMessageWithDelegate:(BOOL)withDelegate; -- (void) requestGeofences; -- (void) requestImmediateDataFlush; -- (void) addAlias:(NSString *)alias withLabel:(NSString *)label; -+ (void) wipeDataAndDisableForAppRun; -+ (void) disableSDK; -+ (void) requestEnableSDKOnNextAppRun; +- (NSString *)getApiKeyFromUnity; +- (NSDictionary *)parsePlist; + +// Social +- (void)setUserFacebookData:(NSString *)facebookId firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email bio:(NSString *)bio cityName:(NSString *)cityName gender:(NSInteger)gender numberOfFriends:(NSInteger)numberOfFriends birthday:(NSString *)birthday; +- (void)setUserTwitterData:(NSInteger)twitterUserId twitterHandle:(NSString *)twitterHandle name:(NSString *)name description:(NSString *)description followerCount:(NSInteger)followerCount followingCount:(NSInteger)followingCount tweetCount:(NSInteger)tweetCount profileImageUrl:(NSString *)profileImageUrl; + +// In-app messages +- (void)logInAppMessageImpression:(NSString *)inAppMessageJSONString; +- (void)logInAppMessageClicked:(NSString *)inAppMessageJSONString; +- (void)logInAppMessageButtonClicked:(NSString *)inAppMessageJSONString withButtonID:(NSInteger)buttonID; +- (void)displayNextInAppMessageWithDelegate:(BOOL)withDelegate; + +// News Feed +- (void)logCardImpression:(NSString *)cardJSONString; +- (void)logCardClicked:(NSString *)cardJSONString; +- (void)requestFeedRefresh; +- (void)requestFeedFromCache:(NSNotification *)notification; + +// Content Cards +- (void)logContentCardImpression:(NSString *)cardJSONString; +- (void)logContentCardClicked:(NSString *)cardJSONString; +- (void)logContentCardDismissed:(NSString *)cardJSONString; +- (void)requestContentCardsRefresh; +- (void)requestContentCardsFromCache:(NSNotification *)notification; + +/*! + * @discussion Passes the device token to Braze. The caller is responsible for respecting + * automatic integration and automatic registration configuration as this method is unaware of Braze-specific configuration. + * + * Any Game object method configured for ABKUnityMessageType.ABKPushTokenReceivedFromSystem will be notified. + * + * @param deviceToken the device token + */ +- (void)registerPushToken:(NSData *)deviceToken; + +/*! + * @discussion Passes the device token to Braze. The caller is responsible for respecting + * automatic integration and automatic registration configuration as this method is unaware of Braze-specific configuration. + * + * Any Game object method configured for ABKUnityMessageType.ABKPushTokenReceivedFromSystem will be notified. + * + * @param deviceToken the device token + */ +- (void)registerPushTokenBase64:(NSString *)deviceTokenBase64; + +/*! + * @discussion Requests authorization and registers the user for notifications. The caller is responsible for respecting + * automatic integration and automatic registration configuration as this method is unaware of Braze-specific configuration. + * + * When the user responds to the request authorization permission prompt, any Game object method configured for + * ABKUnityMessageType.ABKPushPermissionsPromptResponse will be notified. + * @param provisional If set to true, on iOS 12 and above, provisional authorization will be requested. + */ +- (void)registerForRemoteNotificationsWithProvisional:(BOOL)provisional; + +- (void)configureListenerFor:(NSInteger)messageType withGameObject:(NSString *)gameobject withMethod:(NSString *)method; +- (void)setListenersFromPList; +- (void)addInAppMessageListenerWithObjectNameAndSetDelegate:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; +- (void)addFeedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; +- (void)addContentCardsListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; +- (void)addPushReceivedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; +- (void)addPushOpenedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod; + +/*! + * @discussion Passes the push notification data to Braze. The caller is responsible for respecting + * automatic integration and automatic registration configuration as this method is unaware of Braze-specific configuration. + * + * Any Game object method configured for ABKUnityMessageType.ABKPushReceived or ABKUnityMessageType.ABKPushOpened + * will be notified as appropriate. + * + * @param deviceToken the device token + */ +- (void)registerApplication:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; + +// Internal +- (void)configureInternalListenerFor:(NSInteger)messageType; + @end diff --git a/Assets/Plugins/iOS/AppboyUnityManager.mm b/Assets/Plugins/iOS/AppboyUnityManager.mm index f94a24907..12d070d7e 100644 --- a/Assets/Plugins/iOS/AppboyUnityManager.mm +++ b/Assets/Plugins/iOS/AppboyUnityManager.mm @@ -4,12 +4,20 @@ #import #import +static NSString *const ABKInternalCallback = @"BrazeInternalCallback"; +static NSString *const ABKInternalPushPermissionsPromptResponse = @"onPushPromptResponseReceived"; +static NSString *const ABKInternalPushTokenReceivedFromSystem = @"onPushTokenReceivedFromSystem"; + @interface ABKCard(proxy) -- (NSMutableDictionary *) proxyForJson; +- (NSMutableDictionary *)proxyForJson; @end @interface ABKInAppMessage(proxy) -- (NSMutableDictionary *) proxyForJson; +- (NSMutableDictionary *)proxyForJson; +@end + +@interface AppboyUnityManager() ++ (NSString *)hexStringFromNSData:(NSData *)data; @end @implementation AppboyUnityManager @@ -23,132 +31,21 @@ + (AppboyUnityManager*)sharedInstance { return sharedInstance; } -- (void) logCustomEvent:(NSString *)eventName withProperties:(NSDictionary *)properties { - [[Appboy sharedInstance] logCustomEvent:eventName withProperties:properties]; -} - -- (void) changeUser:(NSString *)userId { - [[Appboy sharedInstance] changeUser:userId]; -} - -- (void) logPurchase:(NSString *)productId inCurrency:(NSString *)currencyCode - atPrice:(NSString *)price withQuantity:(NSUInteger)quantity withProperties:(NSDictionary *)properties { - [[Appboy sharedInstance] logPurchase:productId - inCurrency:currencyCode - atPrice:[NSDecimalNumber decimalNumberWithString:price] - withQuantity:quantity - andProperties:(NSDictionary *)properties]; -} - -- (void) setUserFirstName:(NSString *)firstName { - [Appboy sharedInstance].user.firstName = firstName; -} - -- (void) setUserLastName:(NSString *)lastName { - [Appboy sharedInstance].user.lastName = lastName; -} - -- (void) setUserPhoneNumber:(NSString *)number { - [Appboy sharedInstance].user.phone = number; -} - -- (void) setUserAvatarImageURL:(NSString *)imageURL { - [Appboy sharedInstance].user.avatarImageURL = imageURL; -} - -- (void) setUserEmail:(NSString *)email { - [Appboy sharedInstance].user.email = email; -} - -- (void) setUserGender:(NSInteger)gender { - [[Appboy sharedInstance].user setGender:(ABKUserGenderType)gender]; -} +# pragma mark - Config -- (void) setUserDateOfBirthToYear:(NSInteger)year Month:(NSInteger)month andDay:(NSInteger)day { - NSDateComponents *comps = [[NSDateComponents alloc] init]; - [comps setDay:day]; - [comps setMonth:month]; - [comps setYear:year]; - NSCalendar *gregorian = [[NSCalendar alloc] - initWithCalendarIdentifier:NSGregorianCalendar]; - NSDate *date = [gregorian dateFromComponents:comps]; - [Appboy sharedInstance].user.dateOfBirth = date; -} - -- (void) setUserCountry:(NSString *)country { - [Appboy sharedInstance].user.country = country; -} - -- (void) setUserHomeCity:(NSString *)city { - [Appboy sharedInstance].user.homeCity = city; -} - -- (void) setUserEmailNotificationSubscriptionType:(NSInteger)emailNotificationSubscriptionType { - [[Appboy sharedInstance].user setEmailNotificationSubscriptionType:(ABKNotificationSubscriptionType)emailNotificationSubscriptionType]; -} - -- (void) setUserPushNotificationSubscriptionType:(NSInteger)pushNotificationSubscriptionType { - [[Appboy sharedInstance].user setPushNotificationSubscriptionType:(ABKNotificationSubscriptionType)pushNotificationSubscriptionType]; -} - -- (void) setUserCustomAttributeWithKey:(NSString *)key andBOOLValue:(BOOL)value { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andBOOLValue:value]; -} - -- (void) setUserCustomAttributeWithKey:(NSString *)key andIntegerValue:(NSInteger)value { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andIntegerValue:value]; -} - -- (void) setUserCustomAttributeWithKey:(NSString *)key andDoubleValue:(double)value { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andDoubleValue:value]; -} - -- (void) addAlias:(NSString *)alias withLabel:(NSString *)label { - [[Appboy sharedInstance].user addAlias:alias withLabel:label]; -} - -- (void) setUserCustomAttributeWithKey:(NSString *)key andStringValue:(NSString *)value { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andStringValue:value]; -} - -- (void) setUserCustomAttributeToNowWithKey:(NSString *)key { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andDateValue:[NSDate date]]; -} - -- (void) setUserCustomAttributeWithKey:(NSString *)key toDateAsSecondsFromEpoch:(NSTimeInterval)seconds { - [[Appboy sharedInstance].user setCustomAttributeWithKey:key andDateValue:[NSDate dateWithTimeIntervalSince1970:seconds]]; -} - -- (void) unsetUserCustomAttributeWithKey:(NSString *)key { - [[Appboy sharedInstance].user unsetCustomAttributeWithKey:key]; -} - -- (void) incrementCustomUserAttributeWithKey:(NSString *)key by:(NSInteger)incrementValue { - [[Appboy sharedInstance].user incrementCustomUserAttribute:key by:incrementValue]; -} - -- (void) setCustomAttributeArrayWithKey:(NSString *)key array:(NSArray *)valueArray { - [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:key array:valueArray]; -} - -- (void) addToCustomAttributeArrayWithKey:(NSString *)key value:(NSString *)value { - [[Appboy sharedInstance].user addToCustomAttributeArrayWithKey:key value:value]; +- (NSString *)getApiKeyFromUnity { + return self.appboyUnityPlist[ABKUnityApiKey]; } -- (void) removeFromCustomAttributeArrayWithKey:(NSString *)key value:(NSString *)value { - [[Appboy sharedInstance].user removeFromCustomAttributeArrayWithKey:key value:value]; +- (NSDictionary *)parsePlist { + NSDictionary* appboyUnityPlist = [[NSBundle mainBundle] infoDictionary][@"Appboy"][@"Unity"]; + self.appboyUnityPlist = appboyUnityPlist; + return appboyUnityPlist; } -- (void) setAttributionData:(NSString *)network campaign:(NSString *)campaign adgroup:(NSString *)adgroup creative:(NSString *)creative { - ABKAttributionData *attributionData = [[ABKAttributionData alloc] - initWithNetwork:network - campaign:campaign - adGroup:adgroup - creative:creative]; - [[Appboy sharedInstance].user setAttributionData:attributionData]; -} +# pragma mark - Social Media -- (void) setUserFacebookData:(NSString *)facebookId firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email bio:(NSString *)bio cityName:(NSString *)cityName gender:(NSInteger)gender numberOfFriends:(NSInteger)numberOfFriends birthday:(NSString *)birthday { +- (void)setUserFacebookData:(NSString *)facebookId firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email bio:(NSString *)bio cityName:(NSString *)cityName gender:(NSInteger)gender numberOfFriends:(NSInteger)numberOfFriends birthday:(NSString *)birthday { NSMutableDictionary *facebookData = [NSMutableDictionary dictionary]; facebookData[@"id"] = facebookId; facebookData[@"first_name"] = firstName; @@ -172,7 +69,7 @@ - (void) setUserFacebookData:(NSString *)facebookId firstName:(NSString *)firstN [Appboy sharedInstance].user.facebookUser = facebookUser; } -- (void) setUserTwitterData:(NSInteger)twitterUserId twitterHandle:(NSString *)twitterHandle name:(NSString *)name description:(NSString *)description followerCount:(NSInteger)followerCount followingCount:(NSInteger)followingCount tweetCount:(NSInteger)tweetCount profileImageUrl:(NSString *)profileImageUrl { +- (void)setUserTwitterData:(NSInteger)twitterUserId twitterHandle:(NSString *)twitterHandle name:(NSString *)name description:(NSString *)description followerCount:(NSInteger)followerCount followingCount:(NSInteger)followingCount tweetCount:(NSInteger)tweetCount profileImageUrl:(NSString *)profileImageUrl { ABKTwitterUser *twitterUser = [[ABKTwitterUser alloc] init]; twitterUser.userDescription = description; if (twitterUserId > 0) { @@ -193,173 +90,46 @@ - (void) setUserTwitterData:(NSInteger)twitterUserId twitterHandle:(NSString *)t [Appboy sharedInstance].user.twitterUser = twitterUser; } -// ABKInAppMessageDelegate methods -- (ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage { - if (self.unityInAppMessageGameObjectName == nil) { - NSLog(@"Not sending a Unity message in response to an in-app message being received because " - "no message receiver was defined. To implement custom behavior in response to a in-app" - "message being received, you must register a GameObject and method name with Appboy " - "by calling [[AppboyUnityManager sharedInstance] addInAppMessageListenerWithObjectName: callbackMethodName:]."); - return ABKDisplayInAppMessageNow; - } - if (self.unityInAppMessageCallbackFunctionName == nil) { - NSLog(@"Not sending a Unity message in response to a in-app message being received because " - "no method name was defined for the %@. To implement custom behavior in response to a in-app " - "message being received, you must register a GameObject and method name with Appboy " - "[[AppboyUnityManager sharedInstance] addInAppMessageListenerWithObjectName: callbackMethodName:].", - self.unityInAppMessageGameObjectName); - return ABKDisplayInAppMessageNow; - } - NSLog(@"Sending an in-app message to %@:%@.", self.unityInAppMessageGameObjectName, self.unityInAppMessageCallbackFunctionName); - - NSData *inAppMessageData = [inAppMessage serializeToData]; - NSString *dataString = [[NSString alloc] initWithData:inAppMessageData encoding:NSUTF8StringEncoding]; - NSLog(@"dataString is %@.", dataString); - UnitySendMessage([self.unityInAppMessageGameObjectName cStringUsingEncoding:NSUTF8StringEncoding], - [self.unityInAppMessageCallbackFunctionName cStringUsingEncoding:NSUTF8StringEncoding], - [dataString cStringUsingEncoding:NSUTF8StringEncoding]); - if ([self.appboyUnityPlist[ABKUnityHandleInAppMessageDisplayKey] boolValue]) { - return ABKDisplayInAppMessageNow; - } - return ABKDiscardInAppMessage; -} - -// Internal methods for communications between Appboy and Unity -- (void) parsePlist { - self.appboyUnityPlist = [[NSBundle mainBundle] infoDictionary][@"Appboy"][@"Unity"]; -} - -- (void) setListeners { - [self addPushReceivedListenerWithObjectName:self.appboyUnityPlist[ABKUnityPushReceivedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityPushReceivedCallbackKey]]; - [self addPushOpenedListenerWithObjectName:self.appboyUnityPlist[ABKUnityPushOpenedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityPushOpenedCallbackKey]]; - [self addInAppMessageListenerWithObjectName:self.appboyUnityPlist[ABKUnityInAppMessageGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityInAppMessageCallbackKey]]; - [self addContentCardsListenerWithObjectName:self.appboyUnityPlist[ABKUnityContentCardsGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityContentCardsCallbackKey]]; - [self addFeedListenerWithObjectName:self.appboyUnityPlist[ABKUnityFeedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityFeedCallbackKey]]; -} - -- (NSString *) getApiKeyFromUnity { - return self.appboyUnityPlist[ABKUnityApiKey]; -} +# pragma mark - In-app message analytics -- (void) addInAppMessageListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { - if (gameObject != nil && callbackMethod != nil) { - [Appboy sharedInstance].inAppMessageController.delegate = [AppboyUnityManager sharedInstance]; - self.unityInAppMessageGameObjectName = gameObject; - self.unityInAppMessageCallbackFunctionName = callbackMethod; - } -} - -- (void) addFeedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { - if (gameObject != nil && callbackMethod != nil) { - self.unityFeedGameObjectName = gameObject; - self.unityFeedCallbackFunctionName = callbackMethod; - } -} - -- (void) addContentCardsListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { - if (gameObject != nil && callbackMethod != nil) { - self.unityContentCardsGameObjectName = gameObject; - self.unityContentCardsCallbackFunctionName = callbackMethod; - } -} - -- (void) addPushReceivedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { - if (gameObject != nil && callbackMethod != nil) { - self.unityPushReceivedGameObjectName = gameObject; - self.unityPushReceivedCallbackFunctionName = callbackMethod; - } -} - -- (void) addPushOpenedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { - if (gameObject != nil && callbackMethod != nil) { - self.unityPushOpenedGameObjectName = gameObject; - self.unityPushOpenedCallbackFunctionName = callbackMethod; - } -} - -// Push related methods -- (void) registerForRemoteNotifications { - if ([self.appboyUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue] && ![self.appboyUnityPlist[ABKUnityDisableAutomaticPushRegistrationKey] boolValue]) { - UIUserNotificationType notificationSettingTypes = (UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound); - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - center.delegate = self; - UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; - if (@available(iOS 12.0, *)) { - options = options | UNAuthorizationOptionProvisional; - } - [center requestAuthorizationWithOptions:options - completionHandler:^(BOOL granted, NSError *_Nullable error) { - [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; - }]; - [[UIApplication sharedApplication] registerForRemoteNotifications]; - } else { - UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationSettingTypes categories:nil]; - [[UIApplication sharedApplication] registerForRemoteNotifications]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; - } - } -} - -- (void) registerPushToken:(NSData *)deviceToken { - if ([self.appboyUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { - [[Appboy sharedInstance] registerDeviceToken:deviceToken]; - } -} - -- (void) registerPushTokenBase64:(NSString *)deviceTokenBase64 { - NSData *data = [[NSData alloc] initWithBase64EncodedString:deviceTokenBase64 options:0]; - [[Appboy sharedInstance] registerDeviceToken:data]; -} - -- (void) registerApplication:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)notification - fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { - if (self.appboyUnityPlist == nil || [self.appboyUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { - [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:notification fetchCompletionHandler:completionHandler]; - [self forwardNotification:notification]; - } -} - -- (void) userNotificationCenter:(UNUserNotificationCenter *)center - didReceiveNotificationResponse:(UNNotificationResponse *)response - withCompletionHandler:(void (^)(void))completionHandler { - if (completionHandler) { - completionHandler(); - } - - if (self.appboyUnityPlist == nil || [self.appboyUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { - [[Appboy sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; - [self forwardNotification:response.notification.request.content.userInfo]; - } +- (void)logInAppMessageImpression:(NSString *)inAppMessageJSONString { + ABKInAppMessage *inAppMessage = [[ABKInAppMessage alloc] init]; + [self getInAppMessageFromString:inAppMessageJSONString withInAppMessage:inAppMessage]; + [inAppMessage logInAppMessageImpression]; } -- (void) logInAppMessageClicked:(NSString *)inAppMessageJSONString { +- (void)logInAppMessageClicked:(NSString *)inAppMessageJSONString { ABKInAppMessage *inAppMessage = [[ABKInAppMessage alloc] init]; [self getInAppMessageFromString:inAppMessageJSONString withInAppMessage:inAppMessage]; [inAppMessage logInAppMessageClicked]; } -- (void) logInAppMessageButtonClicked:(NSString *)inAppMessageJSONString withButtonID:(NSInteger)buttonID { +- (void)logInAppMessageButtonClicked:(NSString *)inAppMessageJSONString withButtonID:(NSInteger)buttonID { ABKInAppMessageImmersive *inAppMessageImmersive = [[ABKInAppMessageImmersive alloc] init]; [self getInAppMessageFromString:inAppMessageJSONString withInAppMessage:inAppMessageImmersive]; [inAppMessageImmersive logInAppMessageClickedWithButtonID:buttonID]; } -- (void) logInAppMessageImpression:(NSString *)inAppMessageJSONString { - ABKInAppMessage *inAppMessage = [[ABKInAppMessage alloc] init]; - [self getInAppMessageFromString:inAppMessageJSONString withInAppMessage:inAppMessage]; - [inAppMessage logInAppMessageImpression]; -} - -- (void) getInAppMessageFromString:(NSString *)inAppMessageJSONString withInAppMessage:(ABKInAppMessage *)inAppMessage { +- (void)getInAppMessageFromString:(NSString *)inAppMessageJSONString withInAppMessage:(ABKInAppMessage *)inAppMessage { NSData *inAppMessageData = [inAppMessageJSONString dataUsingEncoding:NSUTF8StringEncoding]; NSError *e = nil; id deserializedInAppMessageDict = [NSJSONSerialization JSONObjectWithData:inAppMessageData options:NSJSONReadingMutableContainers error:&e]; [inAppMessage setValuesForKeysWithDictionary:deserializedInAppMessageDict]; } -- (void) logCardImpression:(NSString *)cardJSONString { +# pragma mark - In-app message display + +- (void)displayNextInAppMessageWithDelegate:(BOOL)withDelegate { + ABKInAppMessageController *delegate = nil; + if (withDelegate) { + delegate = [Appboy sharedInstance].inAppMessageController.delegate; + } + [[Appboy sharedInstance].inAppMessageController displayNextInAppMessageWithDelegate:delegate]; +} + +# pragma mark - News Feed analytics + +- (void)logCardImpression:(NSString *)cardJSONString { NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; NSError *e = nil; id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; @@ -367,7 +137,7 @@ - (void) logCardImpression:(NSString *)cardJSONString { [card logCardImpression]; } -- (void) logCardClicked:(NSString *)cardJSONString { +- (void)logCardClicked:(NSString *)cardJSONString { NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; NSError *e = nil; id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; @@ -375,7 +145,9 @@ - (void) logCardClicked:(NSString *)cardJSONString { [card logCardClicked]; } -- (void) requestFeedRefresh { +# pragma mark - News Feed refresh + +- (void)requestFeedRefresh { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(requestFeedFromCache:) name:ABKFeedUpdatedNotification @@ -383,30 +155,16 @@ - (void) requestFeedRefresh { [[Appboy sharedInstance] requestFeedRefresh]; } -- (void) logFeedDisplayed { - [[Appboy sharedInstance] logFeedDisplayed]; -} - -- (void) requestFeedFromCache:(NSNotification *)notification { +- (void)requestFeedFromCache:(NSNotification *)notification { BOOL fromOfflineStorage = YES; if (notification != nil) { [[NSNotificationCenter defaultCenter] removeObserver:self name:ABKFeedUpdatedNotification object:nil]; fromOfflineStorage = NO; } - if (self.unityFeedGameObjectName == nil) { - NSLog(@"Not sending a Unity message in response to a feed cards request because " - "no message receiver was defined. To implement custom behavior in response to a feed" - "being received, you must register a GameObject and method name with Appboy " - "by calling [[AppboyUnityManager sharedInstance] addFeedListenerWithObjectName: callbackMethodName:]."); - } - if (self.unityFeedCallbackFunctionName == nil) { - NSLog(@"Not sending a Unity message in response to a feed cards request because " - "no method name was defined for the %@. To implement custom behavior in response to a feed " - "being received, you must register a GameObject and method name with Appboy " - "[[AppboyUnityManager sharedInstance] addFeedListenerWithObjectName: callbackMethodName:].", - self.unityFeedGameObjectName); + if (self.unityFeedCallbackFunctionName == nil || self.unityFeedGameObjectName == nil) { + NSLog(@"No properly configured game object. Not forwarding News Feed message."); + return; } - NSLog(@"Sending cards to %@:%@.", self.unityFeedGameObjectName, self.unityFeedCallbackFunctionName); NSMutableArray *cardsDictionaryArray = [NSMutableArray arrayWithCapacity:1]; NSArray *cards = [[Appboy sharedInstance].feedController getCardsInCategories:ABKCardCategoryAll]; @@ -420,43 +178,67 @@ - (void) requestFeedFromCache:(NSNotification *)notification { @"mTimestamp" : [NSNumber numberWithDouble:timestamp], @"mFromOfflineStorage" : [NSNumber numberWithBool:fromOfflineStorage]}; NSError *error; - NSString *feedString; NSData *feedData = [NSJSONSerialization dataWithJSONObject:feedDictionary options:0 error:&error]; if (!feedData) { - NSLog(@"Got an error %@ when parsing Appboy feed to json data.", error); - } else { - feedString = [[NSString alloc] initWithData:feedData encoding:NSUTF8StringEncoding]; + NSLog(@"Error parsing News Feed to json: %@", error); + return; } - if (feedString != nil) { - NSLog(@"the message that's going to pass to Unity is: \n %@", feedString); - UnitySendMessage([self.unityFeedGameObjectName cStringUsingEncoding:NSUTF8StringEncoding], - [self.unityFeedCallbackFunctionName cStringUsingEncoding:NSUTF8StringEncoding], - [feedString cStringUsingEncoding:NSUTF8StringEncoding]); + NSString *feedString = [[NSString alloc] initWithData:feedData encoding:NSUTF8StringEncoding]; + if (feedString == nil) { + NSLog(@"Error parsing News Feed json to string"); + return; } + [self unitySendMessageTo:self.unityFeedGameObjectName withMethod:self.unityFeedCallbackFunctionName withMessage:feedString]; +} + +# pragma mark - Content Card analytics + +- (void)logContentCardImpression:(NSString *)cardJSONString { + NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *e = nil; + id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; + ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; + [card logContentCardImpression]; +} + +- (void)logContentCardClicked:(NSString *)cardJSONString { + NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *e = nil; + id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; + ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; + [card logContentCardClicked]; } -- (void) requestContentCardsFromCache:(NSNotification *)notification { +- (void)logContentCardDismissed:(NSString *)cardJSONString { + NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *e = nil; + id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; + ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; + [card logContentCardDismissed]; +} + +# pragma mark - Content Card refresh + +- (void)requestContentCardsRefresh { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(requestContentCardsFromCache:) + name:ABKContentCardsProcessedNotification + object:nil]; + [[Appboy sharedInstance] requestContentCardsRefresh]; +} + +- (void)requestContentCardsFromCache:(NSNotification *)notification { BOOL fromOfflineStorage = YES; if (notification != nil) { [[NSNotificationCenter defaultCenter] removeObserver:self name:ABKContentCardsProcessedNotification object:nil]; fromOfflineStorage = NO; } - if (self.unityContentCardsGameObjectName == nil) { - NSLog(@"Not sending a Unity message in response to a content cards request because " - "no message receiver was defined. To implement custom behavior in response to a cards" - "being received, you must register a GameObject and method name with Appboy " - "by calling [[AppboyUnityManager sharedInstance] addContentCardsListenerWithObjectName: callbackMethodName:]."); + if (self.unityContentCardsCallbackFunctionName == nil || self.unityContentCardsGameObjectName == nil) { + NSLog(@"No properly configured game object. Not forwarding Content Cards message."); + return; } - if (self.unityContentCardsCallbackFunctionName == nil) { - NSLog(@"Not sending a Unity message in response to a content cards request because " - "no method name was defined for the %@. To implement custom behavior in response to cards " - "being received, you must register a GameObject and method name with Appboy " - "[[AppboyUnityManager sharedInstance] addContentCardsListenerWithObjectName: callbackMethodName:].", - self.unityContentCardsGameObjectName); - } - NSLog(@"Sending cards to %@:%@.", self.unityContentCardsGameObjectName, self.unityContentCardsCallbackFunctionName); NSMutableArray *cardsDictionaryArray = [NSMutableArray arrayWithCapacity:1]; NSArray *cards = [[Appboy sharedInstance].contentCardsController getContentCards]; @@ -470,80 +252,83 @@ - (void) requestContentCardsFromCache:(NSNotification *)notification { @"mTimestamp" : [NSNumber numberWithDouble:timestamp], @"mFromOfflineStorage" : [NSNumber numberWithBool:fromOfflineStorage]}; NSError *error; - NSString *contentCardsString; NSData *contentCardsData = [NSJSONSerialization dataWithJSONObject:contentCardsDictionary options:0 error:&error]; if (!contentCardsData) { - NSLog(@"Got an error %@ when parsing Appboy feed to json data.", error); - } else { - contentCardsString = [[NSString alloc] initWithData:contentCardsData encoding:NSUTF8StringEncoding]; + NSLog(@"Error parsing Content Cards to json: %@", error); + return; } - if (contentCardsString != nil) { - NSLog(@"the message that's going to pass to Unity is: \n %@", contentCardsString); - UnitySendMessage([self.unityContentCardsGameObjectName cStringUsingEncoding:NSUTF8StringEncoding], - [self.unityContentCardsCallbackFunctionName cStringUsingEncoding:NSUTF8StringEncoding], - [contentCardsString cStringUsingEncoding:NSUTF8StringEncoding]); + NSString *contentCardsString = [[NSString alloc] initWithData:contentCardsData encoding:NSUTF8StringEncoding]; + if (contentCardsString == nil) { + NSLog(@"Error parsing Content Cards json to string"); + return; } + [self unitySendMessageTo:self.unityContentCardsGameObjectName withMethod:self.unityContentCardsCallbackFunctionName withMessage:contentCardsString]; } -- (void) logContentCardsDisplayed { - [[Appboy sharedInstance] logContentCardsDisplayed]; -} +# pragma mark - Push -- (void) requestContentCardsRefresh { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(requestContentCardsFromCache:) - name:ABKContentCardsProcessedNotification - object:nil]; - [[Appboy sharedInstance] requestContentCardsRefresh]; -} - -- (void) logContentCardImpression:(NSString *)cardJSONString { - NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; - NSError *e = nil; - id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; - ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; - [card logContentCardImpression]; -} - -- (void) logContentCardClicked:(NSString *)cardJSONString { - NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; - NSError *e = nil; - id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; - ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; - [card logContentCardClicked]; +- (void)registerPushToken:(NSData *)data { + [[Appboy sharedInstance] registerDeviceToken:data]; + NSString *token = [AppboyUnityManager hexStringFromNSData:data]; + if (self.unityPushTokenReceivedFromSystemGameObjectName != nil && self.unityPushTokenReceivedFromSystemFunctionName != nil) { + [self unitySendMessageTo:self.unityPushTokenReceivedFromSystemGameObjectName withMethod:self.unityPushTokenReceivedFromSystemFunctionName withMessage:token]; + } + if (self.sendPushTokenReceivedFromSystem) { + [self unitySendMessageTo:ABKInternalCallback withMethod:ABKInternalPushTokenReceivedFromSystem withMessage:token]; + } } -- (void) logContentCardDismissed:(NSString *)cardJSONString { - NSData *cardData = [cardJSONString dataUsingEncoding:NSUTF8StringEncoding]; - NSError *e = nil; - id deserializedCardDict = [NSJSONSerialization JSONObjectWithData:cardData options:NSJSONReadingMutableContainers error:&e]; - ABKContentCard *card = [ABKContentCard deserializeCardFromDictionary:deserializedCardDict]; - [card logContentCardDismissed]; +- (void)registerPushTokenBase64:(NSString *)deviceTokenBase64 { + NSData *data = [[NSData alloc] initWithBase64EncodedString:deviceTokenBase64 options:0]; + [self registerPushToken:data]; } -- (void) displayNextInAppMessageWithDelegate:(BOOL)withDelegate { - ABKInAppMessageController *delegate = nil; - if (withDelegate) { - delegate = [Appboy sharedInstance].inAppMessageController.delegate; ++ (NSString *)hexStringFromNSData:(NSData *)data { + NSUInteger dataLength = data.length; + if (dataLength == 0) { + return nil; } - [[Appboy sharedInstance].inAppMessageController displayNextInAppMessageWithDelegate:delegate]; -} - -+ (void) wipeDataAndDisableForAppRun { - [Appboy wipeDataAndDisableForAppRun]; -} -+ (void) disableSDK { - [Appboy disableSDK]; + const unsigned char *dataBuffer = (const unsigned char *)data.bytes; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + for (int i = 0; i < dataLength; ++i) { + [hexString appendFormat:@"%02.2hhx", dataBuffer[i]]; + } + return [hexString copy]; } -+ (void) requestEnableSDKOnNextAppRun { - [Appboy requestEnableSDKOnNextAppRun]; +- (void)registerForRemoteNotificationsWithProvisional:(BOOL)provisional { + UIUserNotificationType notificationSettingTypes = (UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + center.delegate = self; + UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; + if (@available(iOS 12.0, *)) { + if (provisional) { + options = options | UNAuthorizationOptionProvisional; + } + } + [center requestAuthorizationWithOptions:options + completionHandler:^(BOOL granted, NSError *_Nullable error) { + [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; + if (self.unityPushPermissionsPromptResponseGameObjectName != nil && self.unityPushPermissionsPromptResponseFunctionName != nil) { + [self unitySendMessageTo:self.unityPushPermissionsPromptResponseGameObjectName withMethod:self.unityPushPermissionsPromptResponseFunctionName withMessage:(granted ? @"true" : @"false")]; + } + if (self.sendInternalPushPermissionsPromptResponse) { + [self unitySendMessageTo:ABKInternalCallback withMethod:ABKInternalPushPermissionsPromptResponse withMessage:(granted ? @"true" : @"false")]; + } + }]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; + } else { + UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationSettingTypes categories:nil]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + } } -- (void) forwardNotification:(NSDictionary *)notification { +- (void)forwardNotification:(NSDictionary *)notification { // generate a new dictionary that rearrange the notification elements NSMutableDictionary *aps = [NSMutableDictionary dictionaryWithDictionary:[notification objectForKey:@"aps"]]; @@ -561,58 +346,190 @@ - (void) forwardNotification:(NSDictionary *)notification { [aps setObject:extraDictionary forKey:@"extra"]; } - if ([NSJSONSerialization isValidJSONObject:aps]) { - NSError *pushParsingError = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:aps options:0 error:&pushParsingError]; - - if (pushParsingError == nil) { - NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - - UIApplication *application = [UIApplication sharedApplication]; - if (application.applicationState == UIApplicationStateActive) { - if (self.unityPushReceivedGameObjectName == nil) { - NSLog(@"Not sending a Unity message in response to a push notification being received because " - "no message receiver was defined. To implement custom behavior in response to a push " - "notification being received, you must register a GameObject and method name with Appboy " - "by calling [AppboyUnityManager sharedInstance] addPushReceivedListenerWithObjectName: callbackMethodName]."); - return; - } - if (self.unityPushReceivedCallbackFunctionName == nil) { - NSLog(@"Not sending a Unity message in response to a push notification being received because " - "no method name was defined for the %@. To implement custom behavior in response to a push " - "notification being received, you must register a GameObject and method name with Appboy " - "by calling [AppboyUnityManager sharedInstance] addPushReceivedListenerWithObjectName: callbackMethodName].", - self.unityPushReceivedGameObjectName); - return; - } - NSLog(@"Sending a notification received message to %@:%@.", self.unityPushReceivedGameObjectName, self.unityPushReceivedCallbackFunctionName); - - UnitySendMessage([self.unityPushReceivedGameObjectName cStringUsingEncoding:NSUTF8StringEncoding], - [self.unityPushReceivedCallbackFunctionName cStringUsingEncoding:NSUTF8StringEncoding], - [dataString cStringUsingEncoding:NSUTF8StringEncoding]); - } else { - if (self.unityPushOpenedGameObjectName == nil) { - NSLog(@"Not sending a Unity message in response to a push notification being opened because " - "no message receiver was defined. To implement custom behavior in response to a push " - "notification being opened, you must register a GameObject and method name with Appboy " - "by calling [AppboyUnityManager sharedInstance] addPushOpenedListenerWithObjectName: callbackMethodName]."); - return; - } - if (self.unityPushOpenedCallbackFunctionName == nil) { - NSLog(@"Not sending a Unity message in response to a push notification being opened because " - "no method name was defined for the %@. To implement custom behavior in response to a push " - "notification being opened, you must register a GameObject and method name with Appboy " - "[AppboyUnityManager sharedInstance] addPushOpenedListenerWithObjectName: callbackMethodName].", - self.unityPushOpenedGameObjectName); - return; - } - NSLog(@"Sending a notification opened message to %@:%@.", self.unityPushOpenedGameObjectName, self.unityPushOpenedCallbackFunctionName); - - UnitySendMessage([self.unityPushOpenedGameObjectName cStringUsingEncoding:NSUTF8StringEncoding], - [self.unityPushOpenedCallbackFunctionName cStringUsingEncoding:NSUTF8StringEncoding], - [dataString cStringUsingEncoding:NSUTF8StringEncoding]); - } + if (![NSJSONSerialization isValidJSONObject:aps]) { + NSLog(@"Invalid json received. Not forwarding push message."); + return; + } + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:aps options:0 error:&error]; + if (error != nil) { + NSLog(@"Error serializing json. Not forwarding push message: %@", [error localizedDescription]); + } + + NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + UIApplication *application = [UIApplication sharedApplication]; + if (application.applicationState == UIApplicationStateActive) { + if (self.unityPushReceivedGameObjectName == nil || self.unityPushReceivedCallbackFunctionName == nil) { + NSLog(@"No properly configured game object. Not forwarding push received message."); + return; + } + [self unitySendMessageTo:self.unityPushReceivedGameObjectName withMethod:self.unityPushReceivedCallbackFunctionName withMessage:dataString]; + } else { + if (self.unityPushOpenedGameObjectName == nil || self.unityPushOpenedCallbackFunctionName == nil) { + NSLog(@"No properly configured game object. Not forwarding push opened message."); + return; } + [self unitySendMessageTo:self.unityPushOpenedGameObjectName withMethod:self.unityPushOpenedCallbackFunctionName withMessage:dataString]; + } +} + +# pragma mark - Gameobject callbacks + +- (void)configureListenerFor:(NSInteger)messageType withGameObject:(NSString *)gameobject withMethod:(NSString *)method { + switch ((ABKUnityMessageType)messageType) { + case ABKPushPermissionsPromptResponse: + NSLog(@"Setting push permissions prompt response listener to object %@, method %@", gameobject, method); + self.unityPushPermissionsPromptResponseGameObjectName = gameobject; + self.unityPushPermissionsPromptResponseFunctionName = method; + break; + case ABKPushTokenReceivedFromSystem: + NSLog(@"Setting push token received from system listener to object %@, method %@", gameobject, method); + self.unityPushTokenReceivedFromSystemGameObjectName = gameobject; + self.unityPushTokenReceivedFromSystemFunctionName = method; + break; + case ABKPushReceived: + NSLog(@"Setting push received listener to object %@, method %@", gameobject, method); + [self addPushReceivedListenerWithObjectName:gameobject callbackMethodName:method]; + break; + case ABKPushOpened: + NSLog(@"Setting push opened listener to object %@, method %@", gameobject, method); + [self addPushOpenedListenerWithObjectName:gameobject callbackMethodName:method]; + break; + case ABKPushDeleted: + NSLog(@"Push opened not supported."); + break; + case ABKInAppMessageReceived: + NSLog(@"Setting in-app message received listener to object %@, method %@", gameobject, method); + [self addInAppMessageListenerWithObjectNameAndSetDelegate:gameobject callbackMethodName:method]; + break; + case ABKNewsFeedUpdated: + NSLog(@"Setting News Feed updated listener to object %@, method %@", gameobject, method); + [self addFeedListenerWithObjectName:gameobject callbackMethodName:method]; + break; + case ABKContentCardsUpdated: + NSLog(@"Setting Content Cards updated listener to object %@, method %@", gameobject, method); + [self addContentCardsListenerWithObjectName:gameobject callbackMethodName:method]; + break; + default: + NSLog(@"Unknown message type received."); + return; + } +} + +- (void)setListenersFromPList { + [self addPushReceivedListenerWithObjectName:self.appboyUnityPlist[ABKUnityPushReceivedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityPushReceivedCallbackKey]]; + [self addPushOpenedListenerWithObjectName:self.appboyUnityPlist[ABKUnityPushOpenedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityPushOpenedCallbackKey]]; + [self addInAppMessageListenerWithObjectNameAndSetDelegate:self.appboyUnityPlist[ABKUnityInAppMessageGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityInAppMessageCallbackKey]]; + [self addContentCardsListenerWithObjectName:self.appboyUnityPlist[ABKUnityContentCardsGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityContentCardsCallbackKey]]; + [self addFeedListenerWithObjectName:self.appboyUnityPlist[ABKUnityFeedGameObjectKey] callbackMethodName:self.appboyUnityPlist[ABKUnityFeedCallbackKey]]; +} + +- (void)addInAppMessageListenerWithObjectNameAndSetDelegate:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { + if (gameObject != nil && callbackMethod != nil) { + [Appboy sharedInstance].inAppMessageController.delegate = [AppboyUnityManager sharedInstance]; + self.unityInAppMessageGameObjectName = gameObject; + self.unityInAppMessageCallbackFunctionName = callbackMethod; + } +} + +- (void)addFeedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { + if (gameObject != nil && callbackMethod != nil) { + self.unityFeedGameObjectName = gameObject; + self.unityFeedCallbackFunctionName = callbackMethod; + } +} + +- (void)addContentCardsListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { + if (gameObject != nil && callbackMethod != nil) { + self.unityContentCardsGameObjectName = gameObject; + self.unityContentCardsCallbackFunctionName = callbackMethod; + } +} + +- (void)addPushReceivedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { + if (gameObject != nil && callbackMethod != nil) { + self.unityPushReceivedGameObjectName = gameObject; + self.unityPushReceivedCallbackFunctionName = callbackMethod; + } +} + +- (void)addPushOpenedListenerWithObjectName:(NSString *)gameObject callbackMethodName:(NSString *)callbackMethod { + if (gameObject != nil && callbackMethod != nil) { + self.unityPushOpenedGameObjectName = gameObject; + self.unityPushOpenedCallbackFunctionName = callbackMethod; + } +} + +# pragma mark - UIApplicationDelegate + +- (void)registerApplication:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)notification + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:notification fetchCompletionHandler:completionHandler]; + [self forwardNotification:notification]; +} + +# pragma mark - UNUserNotificationCenterDelegate + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void (^)(void))completionHandler { + if (completionHandler) { + completionHandler(); + } + + if ([self.appboyUnityPlist[ABKUnityAutomaticPushIntegrationKey] boolValue]) { + [[Appboy sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; + [self forwardNotification:response.notification.request.content.userInfo]; + } +} + +# pragma mark - ABKInAppMessageControllerDelegate + +/** + * @discussion Set on the shared Appboy instance when a game object is configured to receive in-app messages. + */ +- (ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage { + if (self.unityInAppMessageCallbackFunctionName == nil || self.unityInAppMessageGameObjectName == nil) { + NSLog(@"No properly configured game object for in-app messages. Returning ABKDisplayInAppMessageNow.", + self.unityInAppMessageGameObjectName); + return ABKDisplayInAppMessageNow; + } + + NSData *inAppMessageData = [inAppMessage serializeToData]; + NSString *dataString = [[NSString alloc] initWithData:inAppMessageData encoding:NSUTF8StringEncoding]; + [self unitySendMessageTo:self.unityInAppMessageGameObjectName withMethod:self.unityInAppMessageCallbackFunctionName withMessage:dataString]; + if ([self.appboyUnityPlist[ABKUnityHandleInAppMessageDisplayKey] boolValue]) { + NSLog(@"Braze configured to display in-app messages despite presence of game object listener. Returning ABKDisplayInAppMessageNow."); + return ABKDisplayInAppMessageNow; + } + return ABKDiscardInAppMessage; +} + +# pragma mark - Internal Communication + +- (void)unitySendMessageTo:(NSString *)gameObjectName withMethod:(NSString *)methodName withMessage:(NSString *)message { + NSLog(@"Sending message to %@:%@.", gameObjectName, methodName); + UnitySendMessage([gameObjectName UTF8String], + [methodName UTF8String], + [message UTF8String]); +} + +- (void)configureInternalListenerFor:(NSInteger)messageType { + switch ((ABKUnityMessageType)messageType) { + case ABKPushPermissionsPromptResponse: + NSLog(@"Enabling internal push permissions prompt response listener."); + self.sendInternalPushPermissionsPromptResponse = YES; + break; + case ABKPushTokenReceivedFromSystem: + NSLog(@"Enabling push token received from system listener."); + self.sendPushTokenReceivedFromSystem = YES; + break; + default: + NSLog(@"Unknown internal message type received."); + return; } } diff --git a/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Appboy_iOS_SDK.meta b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Appboy_iOS_SDK.meta new file mode 100644 index 000000000..6c94f8019 --- /dev/null +++ b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Appboy_iOS_SDK.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5e8ae10fcfc554bd8840e2d1787a6af4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Headers.meta b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Headers.meta new file mode 100644 index 000000000..a1f67790c --- /dev/null +++ b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Headers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 59d3d1cc8cbe24b21a98cdd0463614fe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Modules.meta b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Modules.meta new file mode 100644 index 000000000..c39a88287 --- /dev/null +++ b/Assets/Plugins/iOS/Appboy_iOS_SDK.framework/Modules.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 878ec4b6e380f47dbbb98e0507600728 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/SDWebImage.framework/Headers.meta b/Assets/Plugins/iOS/SDWebImage.framework/Headers.meta new file mode 100644 index 000000000..3eb061e5b --- /dev/null +++ b/Assets/Plugins/iOS/SDWebImage.framework/Headers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60eb0b185d9144c55985149ff6e5db95 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/SDWebImage.framework/Modules.meta b/Assets/Plugins/iOS/SDWebImage.framework/Modules.meta new file mode 100644 index 000000000..657cb69c0 --- /dev/null +++ b/Assets/Plugins/iOS/SDWebImage.framework/Modules.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2bd9c2ede956d47afb9a427a5a138aee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/SDWebImage.framework/PrivateHeaders.meta b/Assets/Plugins/iOS/SDWebImage.framework/PrivateHeaders.meta new file mode 100644 index 000000000..7a3c23b84 --- /dev/null +++ b/Assets/Plugins/iOS/SDWebImage.framework/PrivateHeaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f38ec10d63a145f9bd48f39127b169c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/SDWebImage.framework/SDWebImage.meta b/Assets/Plugins/iOS/SDWebImage.framework/SDWebImage.meta new file mode 100644 index 000000000..432d24a09 --- /dev/null +++ b/Assets/Plugins/iOS/SDWebImage.framework/SDWebImage.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 69663173c0f5c4712a7e738101a6ab0c +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/CHANGELOG.md b/CHANGELOG.md index a3c88db47..7cddbb546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## 3.0.0 + +##### Important +- This release contains several minor changes to our iOS push code. Most integrations will be unaffected, however, we recommend additional testing. + +##### Breaking +- Updated the Android plugin to use [Braze Android SDK 13.0.0](https://github.com/Appboy/appboy-android-sdk/blob/master/CHANGELOG.md#1300). +- If automatic iOS push integration is enabled, Braze will now automatically add the Xcode Push Capability in `OnPostprocessBuild()`. + - To disable this, check "Disable Automatic Push Capability" in the Braze configuration editor. +- In `AppboyUnityManager.mm`: + - `registerForRemoteNotifications:` has been replaced with `registerForRemoteNotificationsWithProvisional:(BOOL)provisional`. If using this method, note that the new method calls Apple's APIs directly and does not respect Braze configuration's settings for automatic push integration and registration. + - `registerApplication:didReceiveRemoteNotification:fetchCompletionHandler:` and `registerPushToken` have also been updated to no longer internally read Braze config. + - Several obsolete methods were removed, including methods where the manager trivially wrapped the native `Appboy` instance. + - Most integrations will not be affected by these changes. + +##### Added +- Added the option to disable iOS provisional push authorization when automatic iOS push integration is enabled. + - To use, check "Disable Provisional Authorization" in the Braze configuration editor. + - When provisional push authorization is disabled, users will see the native push prompt dialog at app startup. +- Added `AppboyBinding.ConfigureListener()` as an alternative method for configuring GameObject listeners for push, in-app messages, Content Cards, and News Feed. Use the new `BrazeUnityMessageType` enum to specify the desired message type. + - On iOS, to receive push opened and received callbacks, `Integrate Push With Braze` must be enabled. +- Added `AppboyBinding.PromptUserForPushPermissions(bool provisional)` to request authorization and register for push notifications on iOS. + - Set `provisional` to `true` to request provisional authorization, or `false` to show the push prompt directly. + - If you would like to read the user response, pass an instance of `PushPromptResponseReceived` into the method. + - We recommend using this method with the following settings: + - `Integrate Push With Braze` enabled. + - `Disable Automatic Push Registration` enabled. +- Added `AppboyBinding.SetPushTokenReceivedFromSystemDelegate()` to receive push tokens Braze receives from the OS (iOS only). + +##### Fixed +- Braze push delegates are no longer called automatically in fully manual integrations. + - Automatic push integration must be enabled for Braze push delegates to function. + ## 2.8.0 ##### Breaking diff --git a/scripts/manual_move_aars_from_m2.sh b/scripts/manual_move_aars_from_m2.sh new file mode 100755 index 000000000..fe1e9619d --- /dev/null +++ b/scripts/manual_move_aars_from_m2.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +VERSION=$1 + +cp ~/.m2/repository/com/appboy/android-sdk-base/$VERSION/android-sdk-base-$VERSION.aar ../Assets/Plugins/Android/appboy.aar +cp ~/.m2/repository/com/appboy/android-sdk-ui/$VERSION/android-sdk-ui-$VERSION.aar ../Assets/Plugins/Android/appboy-ui.aar +cp ~/.m2/repository/com/appboy/android-sdk-unity/$VERSION/android-sdk-unity-$VERSION.aar ../Assets/Plugins/Android/appboy-unity.aar diff --git a/scripts/release.rb b/scripts/release.rb index 7811543f9..fdcbdbbba 100755 --- a/scripts/release.rb +++ b/scripts/release.rb @@ -10,7 +10,7 @@ def check_and_remove_directory(directory) if File.exists?(directory) - `rm -rf #{directory}` + `rm -rf #{directory}` end end @@ -83,6 +83,7 @@ def pretty_puts(msg) check_and_remove_directory("Public/Plugins") check_and_remove_directory("Public/Assets") check_and_remove_directory("Public/scripts") +check_and_remove_directory("unity-samples/build") pretty_puts("Copying sample app, scripts and Assets folder into Public") puts `cp -vR "Assets" "Public"`