FlurryAnalyticsAndroid.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. ///----------------------------------------------
  2. /// Flurry Analytics Plugin
  3. /// Copyright © 2016 Aleksei Kuzin
  4. ///----------------------------------------------
  5. using UnityEngine;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. namespace KHD {
  9. public static class FlurryAnalyticsAndroid {
  10. #if UNITY_ANDROID
  11. private const string FLURRY_ANGENT_CLASS_NAME = "com.flurry.android.FlurryAgent";
  12. private const string UNITY_PLAYER_CLASS_NAME = "com.unity3d.player.UnityPlayer";
  13. private const string UNITY_PLAYER_ACTIVITY_NAME = "currentActivity";
  14. private static AndroidJavaClass Flurry {
  15. get {
  16. if (!IsAndroidPlayer()) {
  17. return null;
  18. }
  19. if (_flurry == null) {
  20. _flurry = new AndroidJavaClass(FLURRY_ANGENT_CLASS_NAME);
  21. }
  22. return _flurry;
  23. }
  24. }
  25. private static AndroidJavaClass _flurry;
  26. #endif
  27. #if UNITY_EDITOR
  28. /// <summary>
  29. /// Generates debug logs to Editor console.
  30. /// </summary>
  31. private static bool _editorDebugLogEnabled = false;
  32. #endif
  33. public static void Dispose() {
  34. #if UNITY_ANDROID
  35. if (_flurry != null) {
  36. _flurry.Dispose();
  37. _flurry = null;
  38. }
  39. #endif
  40. }
  41. public static void Init(string apiKey, bool captureUncaughtExceptions = false) {
  42. #if UNITY_ANDROID
  43. DebugLog("Init with key: " + apiKey + " capture exceptions: " + captureUncaughtExceptions);
  44. if (!IsAndroidPlayer()) {
  45. return;
  46. }
  47. if (apiKey != null) {
  48. apiKey = apiKey.ToUpper();
  49. }
  50. if (string.IsNullOrEmpty(apiKey)) {
  51. DebugLogError("Android API key is null or empty, please provide valid API key");
  52. }
  53. using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) {
  54. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>(UNITY_PLAYER_ACTIVITY_NAME)) {
  55. Flurry.CallStatic("setCaptureUncaughtExceptions", captureUncaughtExceptions);
  56. Flurry.CallStatic("init", activity, apiKey.ToUpper());
  57. }
  58. }
  59. #endif
  60. }
  61. public static void OnStartSession() {
  62. #if UNITY_ANDROID
  63. DebugLog("Session started");
  64. if (!IsAndroidPlayer()) {
  65. return;
  66. }
  67. using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) {
  68. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>(UNITY_PLAYER_ACTIVITY_NAME)) {
  69. Flurry.CallStatic("onStartSession", activity);
  70. }
  71. }
  72. #endif
  73. }
  74. public static void OnEndSession() {
  75. #if UNITY_ANDROID
  76. DebugLog("Session ended");
  77. if (!IsAndroidPlayer()) {
  78. return;
  79. }
  80. using (var unityPlayer = new AndroidJavaClass(UNITY_PLAYER_CLASS_NAME)) {
  81. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>(UNITY_PLAYER_ACTIVITY_NAME)) {
  82. Flurry.CallStatic("onStartSession", activity);
  83. }
  84. }
  85. #endif
  86. }
  87. /// <summary>
  88. /// Enable/Disable Flurry SDK debug logs.
  89. ///
  90. /// By default logs are disabled.
  91. /// Should be called before StartSession.
  92. /// </summary>
  93. public static void SetLogEnabled(bool enabled) {
  94. #if UNITY_EDITOR
  95. _editorDebugLogEnabled = enabled;
  96. #endif
  97. #if UNITY_ANDROID
  98. if (!IsAndroidPlayer()) {
  99. return;
  100. }
  101. Flurry.CallStatic("setLogEnabled", enabled);
  102. #endif
  103. }
  104. /// <summary>
  105. /// Explicitly set app version..
  106. /// Should be called before StartSession.
  107. /// </summary>
  108. /// <param name="appVersion">App version.</param>
  109. public static void SetVersionName(string versionName) {
  110. #if UNITY_ANDROID
  111. DebugLog("Application version changed to: " + versionName);
  112. if (!IsAndroidPlayer()) {
  113. return;
  114. }
  115. Flurry.CallStatic("setVersionName", versionName);
  116. #endif
  117. }
  118. /// <summary>
  119. /// Set whether Flurry should record location. Defaults to true.
  120. /// </summary>
  121. public static void SetReportLocation(bool report) {
  122. #if UNITY_ANDROID
  123. DebugLog("Set report location: " + report);
  124. if (!IsAndroidPlayer()) {
  125. return;
  126. }
  127. Flurry.CallStatic("setReportLocation", report);
  128. #endif
  129. }
  130. /// <summary>
  131. /// Sets the time the app may be in the background before starting a new session upon resume.
  132. /// Default value 10000 ms.
  133. ///
  134. /// Should be called before StartSession.
  135. /// </summary>
  136. public static void SetContinueSessionMillis(long milliseconds) {
  137. #if UNITY_ANDROID
  138. DebugLog("Set continue session seconds to: " + (milliseconds / 1000));
  139. if (!IsAndroidPlayer()) {
  140. return;
  141. }
  142. Flurry.CallStatic("setContinueSessionMillis", milliseconds);
  143. #endif
  144. }
  145. /// <summary>
  146. /// Used to allow/disallow Flurry SDK to report uncaught exceptions. The feature is enabled by default.
  147. ///
  148. /// Should be called before init.
  149. /// </summary>
  150. public static void SetCaptureUncaughtExceptions(bool enabled) {
  151. #if UNITY_ANDROID
  152. DebugLog("Set capture unchaught exceptions: " + enabled);
  153. if (!IsAndroidPlayer()) {
  154. return;
  155. }
  156. Flurry.CallStatic("setCaptureUncaughtExceptions", enabled);
  157. #endif
  158. }
  159. /// <summary>
  160. /// Enables Flurry Pulse.
  161. /// Please see https://developer.yahoo.com/flurry-pulse/ for more details.
  162. ///
  163. /// Should be called before StartSession.
  164. /// </summary>
  165. public static void SetPulseEnabled(bool enabled) {
  166. #if UNITY_ANDROID
  167. if (!IsAndroidPlayer()) {
  168. return;
  169. }
  170. Flurry.CallStatic("setPulseEnabled", enabled);
  171. #endif
  172. }
  173. /// <summary>
  174. /// Assign a unique id for a user in your app.
  175. /// </summary>
  176. public static void SetUserId(string userId) {
  177. #if UNITY_ANDROID
  178. DebugLog("Set unique user id: " + userId);
  179. if (!IsAndroidPlayer()) {
  180. return;
  181. }
  182. Flurry.CallStatic("setUserId", userId);
  183. #endif
  184. }
  185. /// <summary>
  186. /// Set user's gender.
  187. /// </summary>
  188. public static void SetGender(FlurryAnalytics.Gender gender) {
  189. #if UNITY_ANDROID
  190. DebugLog("Set user gender: " + gender);
  191. if (!IsAndroidPlayer()) {
  192. return;
  193. }
  194. Flurry.CallStatic("setGender", (byte)(gender == FlurryAnalytics.Gender.Male ? 1 : 0));
  195. #endif
  196. }
  197. /// <summary>
  198. /// Use this method to capture the age of your user.
  199. /// </summary>
  200. public static void SetAge(int age) {
  201. #if UNITY_ANDROID
  202. DebugLog("Set user age: " + age);
  203. if (!IsAndroidPlayer()) {
  204. return;
  205. }
  206. Flurry.CallStatic("setAge", age);
  207. #endif
  208. }
  209. /// <summary>
  210. /// Set the default location.
  211. /// Useful when it is necessary to test analytics
  212. /// from other countries or places, or if your app is
  213. /// location-aware and you do not wish the Flurry SDK to determine the user location via GPS.
  214. /// </summary>
  215. public static void SetLocation(float latitude, float longitude) {
  216. #if UNITY_ANDROID
  217. DebugLog("Set location: " + latitude + ", " + longitude);
  218. if (!IsAndroidPlayer()) {
  219. return;
  220. }
  221. Flurry.CallStatic("setLocation", latitude, longitude);
  222. #endif
  223. }
  224. /// <summary>
  225. /// Records a custom event specified by eventName.
  226. /// </summary>
  227. /// <param name="eventName">
  228. /// Name of the event. For maximum effectiveness, we recommend using a naming scheme
  229. /// that can be easily understood by non-technical people in your business domain.
  230. /// </param>
  231. /// <param name="isTimed">If set to <c>true</c> event will be timed.
  232. /// Call EndTimedEvent to stop timed event.</param>
  233. public static void LogEvent(string eventName, bool isTimed = false) {
  234. #if UNITY_ANDROID
  235. DebugLog("Log event: " + eventName + " isTimed: " + isTimed);
  236. if (!IsAndroidPlayer()) {
  237. return;
  238. }
  239. Flurry.CallStatic<AndroidJavaObject>("logEvent", eventName, isTimed);
  240. #endif
  241. }
  242. /// <summary>
  243. /// Log custom event with parameters.
  244. /// A maximum of 10 parameter names may be associated with any event.
  245. /// </summary>
  246. /// <param name="eventName">
  247. /// Name of the event. For maximum effectiveness, we recommend using a naming scheme
  248. /// that can be easily understood by non-technical people in your business domain.
  249. /// </param>
  250. /// <param name="parameters">Parameters.</param>
  251. /// <param name="isTimed">If set to <c>true</c> this will be a timed event.
  252. /// Call EndTimedEvent to stop timed event.</param>
  253. public static void LogEventWithParameters(string eventName, Dictionary<string, string> parameters,
  254. bool isTimed = false) {
  255. #if UNITY_ANDROID
  256. DebugLog("Log event: " + eventName + " isTimed: " + isTimed + " with parameters");
  257. if (!IsAndroidPlayer()) {
  258. return;
  259. }
  260. using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) {
  261. Flurry.CallStatic<AndroidJavaObject>("logEvent", eventName, hashMap, isTimed);
  262. }
  263. #endif
  264. }
  265. /// <summary>
  266. /// Ends the timed event.
  267. /// </summary>
  268. /// <param name="eventName">
  269. /// Name of the event. For maximum effectiveness, we recommend using a naming scheme
  270. /// that can be easily understood by non-technical people in your business domain.
  271. /// </param>
  272. /// <param name="parameters">Parameters.</param>
  273. public static void EndTimedEvent(string eventName, Dictionary<string, string> parameters = null) {
  274. #if UNITY_ANDROID
  275. DebugLog("End timed event: " + eventName);
  276. if (!IsAndroidPlayer()) {
  277. return;
  278. }
  279. if (parameters == null) {
  280. Flurry.CallStatic("endTimedEvent", eventName);
  281. } else {
  282. using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) {
  283. Flurry.CallStatic("endTimedEvent", eventName, hashMap);
  284. }
  285. }
  286. #endif
  287. }
  288. /// <summary>
  289. /// Log a payment.
  290. /// </summary>
  291. /// <param name="productName">The name of the product purchased.</param>
  292. /// <param name="productId">The id of the product purchased.</param>
  293. /// <param name="quantity">The number of products purchased.</param>
  294. /// <param name="price">The price of the the products purchased in the given currency.</param>
  295. /// <param name="currency">The currency for the price argument.</param>
  296. /// <param name="transactionId">A unique identifier for the transaction used to make the purchase.</param>
  297. /// <param name="parameters">the parameters which should be submitted with this event.</param>
  298. public static void LogPayment(string productName,
  299. string productId,
  300. int quantity,
  301. double price,
  302. string currency,
  303. string transactionId,
  304. Dictionary<string, string> parameters) {
  305. #if UNITY_ANDROID
  306. DebugLog("Log payment: " + productName);
  307. if (parameters == null) {
  308. parameters = new Dictionary<string, string>();
  309. }
  310. using (var hashMap = ConvertDictionaryToJavaHashMap(parameters)) {
  311. Flurry.CallStatic<AndroidJavaObject>("logPayment", productName, productId,
  312. quantity, price, currency, transactionId, hashMap);
  313. }
  314. #endif
  315. }
  316. #if UNITY_ANDROID
  317. private static AndroidJavaObject ConvertDictionaryToJavaHashMap(Dictionary<string, string> parameters) {
  318. var hashMap = new AndroidJavaObject("java.util.HashMap");
  319. var put = AndroidJNIHelper.GetMethodID(hashMap.GetRawClass(), "put",
  320. "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
  321. foreach (var entry in parameters) {
  322. using (var key = new AndroidJavaObject("java.lang.String", entry.Key)) {
  323. using (var value = new AndroidJavaObject("java.lang.String", entry.Value)) {
  324. AndroidJNI.CallObjectMethod(hashMap.GetRawObject(), put,
  325. AndroidJNIHelper.CreateJNIArgArray(new object[] { key, value }));
  326. }
  327. }
  328. }
  329. return hashMap;
  330. }
  331. #endif
  332. private static bool EditorDebugLogEnabled() {
  333. #if UNITY_EDITOR
  334. return _editorDebugLogEnabled;
  335. #else
  336. return false;
  337. #endif
  338. }
  339. private static bool IsAndroidPlayer() {
  340. return Application.platform == RuntimePlatform.Android;
  341. }
  342. private static void DebugLog(string log) {
  343. if (!EditorDebugLogEnabled()) {
  344. return;
  345. }
  346. Debug.Log("[FlurryAnalyticsPlugin]: " + log);
  347. }
  348. private static void DebugLogError(string error) {
  349. Debug.Log("[FlurryAnalyticsPlugin]: " + error);
  350. }
  351. }
  352. }