///---------------------------------------------- /// Flurry Analytics Plugin /// Copyright © 2016 Aleksei Kuzin ///---------------------------------------------- using UnityEngine; using System.Collections; using System.Collections.Generic; namespace KHD { public static class FlurryAnalyticsAndroid { #if UNITY_ANDROID private const string FLURRY_ANGENT_CLASS_NAME = "com.flurry.android.FlurryAgent"; private const string UNITY_PLAYER_CLASS_NAME = "com.unity3d.player.UnityPlayer"; private const string UNITY_PLAYER_ACTIVITY_NAME = "currentActivity"; private static AndroidJavaClass Flurry { get { if (!IsAndroidPlayer()) { return null; } if (_flurry == null) { _flurry = new AndroidJavaClass(FLURRY_ANGENT_CLASS_NAME); } return _flurry; } } private static AndroidJavaClass _flurry; #endif #if UNITY_EDITOR /// /// Generates debug logs to Editor console. /// private static bool _editorDebugLogEnabled = false; #endif public static void Dispose() { #if UNITY_ANDROID if (_flurry != null) { _flurry.Dispose(); _flurry = null; } #endif } public static void Init(string apiKey, bool captureUncaughtExceptions = false) { #if UNITY_ANDROID DebugLog("Init with key: " + apiKey + " capture exceptions: " + captureUncaughtExceptions); if (!IsAndroidPlayer()) { return; } if (apiKey != null) { apiKey = apiKey.ToUpper(); } if (string.IsNullOrEmpty(apiKey)) { DebugLogError("Android API key is null or empty, please provide valid API key"); } using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) { using (var activity = unityPlayer.GetStatic(UNITY_PLAYER_ACTIVITY_NAME)) { Flurry.CallStatic("setCaptureUncaughtExceptions", captureUncaughtExceptions); Flurry.CallStatic("init", activity, apiKey.ToUpper()); } } #endif } public static void OnStartSession() { #if UNITY_ANDROID DebugLog("Session started"); if (!IsAndroidPlayer()) { return; } using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) { using (var activity = unityPlayer.GetStatic(UNITY_PLAYER_ACTIVITY_NAME)) { Flurry.CallStatic("onStartSession", activity); } } #endif } public static void OnEndSession() { #if UNITY_ANDROID DebugLog("Session ended"); if (!IsAndroidPlayer()) { return; } using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) { using (var activity = unityPlayer.GetStatic(UNITY_PLAYER_ACTIVITY_NAME)) { Flurry.CallStatic("onStartSession", activity); } } #endif } /// /// Enable/Disable Flurry SDK debug logs. /// /// By default logs are disabled. /// Should be called before StartSession. /// public static void SetLogEnabled(bool enabled) { #if UNITY_EDITOR _editorDebugLogEnabled = enabled; #endif #if UNITY_ANDROID if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setLogEnabled", enabled); #endif } /// /// Explicitly set app version.. /// Should be called before StartSession. /// /// App version. public static void SetVersionName(string versionName) { #if UNITY_ANDROID DebugLog("Application version changed to: " + versionName); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setVersionName", versionName); #endif } /// /// Set whether Flurry should record location. Defaults to true. /// public static void SetReportLocation(bool report) { #if UNITY_ANDROID DebugLog("Set report location: " + report); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setReportLocation", report); #endif } /// /// Sets the time the app may be in the background before starting a new session upon resume. /// Default value 10000 ms. /// /// Should be called before StartSession. /// public static void SetContinueSessionMillis(long milliseconds) { #if UNITY_ANDROID DebugLog("Set continue session seconds to: " + (milliseconds / 1000)); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setContinueSessionMillis", milliseconds); #endif } /// /// Used to allow/disallow Flurry SDK to report uncaught exceptions. The feature is enabled by default. /// /// Should be called before init. /// public static void SetCaptureUncaughtExceptions(bool enabled) { #if UNITY_ANDROID DebugLog("Set capture unchaught exceptions: " + enabled); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setCaptureUncaughtExceptions", enabled); #endif } /// /// Enables Flurry Pulse. /// Please see https://developer.yahoo.com/flurry-pulse/ for more details. /// /// Should be called before StartSession. /// public static void SetPulseEnabled(bool enabled) { #if UNITY_ANDROID if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setPulseEnabled", enabled); #endif } /// /// Assign a unique id for a user in your app. /// public static void SetUserId(string userId) { #if UNITY_ANDROID DebugLog("Set unique user id: " + userId); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setUserId", userId); #endif } /// /// Set user's gender. /// public static void SetGender(FlurryAnalytics.Gender gender) { #if UNITY_ANDROID DebugLog("Set user gender: " + gender); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setGender", (byte)(gender == FlurryAnalytics.Gender.Male ? 1 : 0)); #endif } /// /// Use this method to capture the age of your user. /// public static void SetAge(int age) { #if UNITY_ANDROID DebugLog("Set user age: " + age); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setAge", age); #endif } /// /// Set the default location. /// Useful when it is necessary to test analytics /// from other countries or places, or if your app is /// location-aware and you do not wish the Flurry SDK to determine the user location via GPS. /// public static void SetLocation(float latitude, float longitude) { #if UNITY_ANDROID DebugLog("Set location: " + latitude + ", " + longitude); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("setLocation", latitude, longitude); #endif } /// /// Records a custom event specified by eventName. /// /// /// Name of the event. For maximum effectiveness, we recommend using a naming scheme /// that can be easily understood by non-technical people in your business domain. /// /// If set to true event will be timed. /// Call EndTimedEvent to stop timed event. public static void LogEvent(string eventName, bool isTimed = false) { #if UNITY_ANDROID DebugLog("Log event: " + eventName + " isTimed: " + isTimed); if (!IsAndroidPlayer()) { return; } Flurry.CallStatic("logEvent", eventName, isTimed); #endif } /// /// Log custom event with parameters. /// A maximum of 10 parameter names may be associated with any event. /// /// /// Name of the event. For maximum effectiveness, we recommend using a naming scheme /// that can be easily understood by non-technical people in your business domain. /// /// Parameters. /// If set to true this will be a timed event. /// Call EndTimedEvent to stop timed event. public static void LogEventWithParameters(string eventName, Dictionary parameters, bool isTimed = false) { #if UNITY_ANDROID DebugLog("Log event: " + eventName + " isTimed: " + isTimed + " with parameters"); if (!IsAndroidPlayer()) { return; } using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) { Flurry.CallStatic("logEvent", eventName, hashMap, isTimed); } #endif } /// /// Ends the timed event. /// /// /// Name of the event. For maximum effectiveness, we recommend using a naming scheme /// that can be easily understood by non-technical people in your business domain. /// /// Parameters. public static void EndTimedEvent(string eventName, Dictionary parameters = null) { #if UNITY_ANDROID DebugLog("End timed event: " + eventName); if (!IsAndroidPlayer()) { return; } if (parameters == null) { Flurry.CallStatic("endTimedEvent", eventName); } else { using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) { Flurry.CallStatic("endTimedEvent", eventName, hashMap); } } #endif } /// /// Log a payment. /// /// The name of the product purchased. /// The id of the product purchased. /// The number of products purchased. /// The price of the the products purchased in the given currency. /// The currency for the price argument. /// A unique identifier for the transaction used to make the purchase. /// the parameters which should be submitted with this event. public static void LogPayment(string productName, string productId, int quantity, double price, string currency, string transactionId, Dictionary parameters) { #if UNITY_ANDROID DebugLog("Log payment: " + productName); if (parameters == null) { parameters = new Dictionary(); } using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) { Flurry.CallStatic("logPayment", productName, productId, quantity, price, currency, transactionId, hashMap); } #endif } #if UNITY_ANDROID private static AndroidJavaObject ConvertDictionaryToJavaHashMap(Dictionary parameters) { var hashMap = new AndroidJavaObject("java.util.HashMap"); var put = AndroidJNIHelper.GetMethodID(hashMap.GetRawClass(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); foreach (var entry in parameters) { using (var key = new AndroidJavaObject("java.lang.String", entry.Key)) { using (var value = new AndroidJavaObject("java.lang.String", entry.Value)) { AndroidJNI.CallObjectMethod(hashMap.GetRawObject(), put, AndroidJNIHelper.CreateJNIArgArray(new object[] { key, value })); } } } return hashMap; } #endif private static bool EditorDebugLogEnabled() { #if UNITY_EDITOR return _editorDebugLogEnabled; #else return false; #endif } private static bool IsAndroidPlayer() { return Application.platform == RuntimePlatform.Android; } private static void DebugLog(string log) { if (!EditorDebugLogEnabled()) { return; } Debug.Log("[FlurryAnalyticsPlugin]: " + log); } private static void DebugLogError(string error) { Debug.Log("[FlurryAnalyticsPlugin]: " + error); } } }