public WWW RequestGameInfo(SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { string game_key = GA.Settings.GameKey; string requestInfo = "game_key=" + game_key + "&keys=area%7Cevent_id%7Cbuild"; //Get the url with the request type string url = GetURL(Requests[RequestType.GA_GetHeatmapGameInfo]); url += "/?" + requestInfo; //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.Settings.ApiKey)); Debug.Log("API Key: " + GA.Settings.ApiKey); url = url.Replace(" ", "%20"); //Try to send the data WWW www = new WWW(url, new byte[] { 0 }, headers); GA.RunCoroutine(Request(www, RequestType.GA_GetHeatmapGameInfo, successEvent, errorEvent),()=>www.isDone); return www; }
public WWW RequestGameInfo(SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { if (string.IsNullOrEmpty(GA.SettingsGA.GameKey)) { GA.LogWarning("Game key not set - please setup your Game key in GA_Settings, under the Basic tab"); return null; } if (string.IsNullOrEmpty(GA.SettingsGA.ApiKey)) { GA.LogWarning("API key not set - please setup your API key in GA_Settings, under the Advanced tab"); return null; } string game_key = GA.SettingsGA.GameKey; string requestInfo = "game_key=" + game_key + "&keys=area%7Cevent_id%7Cbuild"; requestInfo = requestInfo.Replace(" ", "%20"); //Get the url with the request type string url = GetURL(Requests[RequestType.GA_GetHeatmapGameInfo]); url += "/?" + requestInfo; WWW www = null; #if UNITY_FLASH //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", GA_Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #else //Set the authorization header to contain an MD5 hash of the JSON array string + the private key #if UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 Hashtable headers = new Hashtable(); #else Dictionary<string, string> headers = new Dictionary<string, string>(); #endif headers.Add("Authorization", GA_Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #endif GA.RunCoroutine(Request(www, RequestType.GA_GetHeatmapGameInfo, successEvent, errorEvent),()=>www.isDone); return www; }
public WWW RequestGameInfo(SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { if (string.IsNullOrEmpty(GA.SettingsGA.GameKey)) { GA.LogWarning("Game key not set - please setup your Game key in GA_Settings, under the Basic tab"); return(null); } if (string.IsNullOrEmpty(GA.SettingsGA.ApiKey)) { GA.LogWarning("API key not set - please setup your API key in GA_Settings, under the Advanced tab"); return(null); } string game_key = GA.SettingsGA.GameKey; string requestInfo = "game_key=" + game_key + "&keys=area%7Cevent_id%7Cbuild"; requestInfo = requestInfo.Replace(" ", "%20"); //Get the url with the request type string url = GetURL(Requests[RequestType.GA_GetHeatmapGameInfo]); url += "/?" + requestInfo; WWW www = null; #if !UNITY_WP8 && !UNITY_METRO //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #else //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Dictionary <string, string> headers = new Dictionary <string, string>(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #endif GA.RunCoroutine(Request(www, RequestType.GA_GetHeatmapGameInfo, successEvent, errorEvent), () => www.isDone); return(www); }
public WWW RequestHeatmapData(List<string> events, string area, DateTime? startDate, DateTime? endDate, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { string game_key = GA.SettingsGA.GameKey; string event_ids = ""; for (int i = 0; i < events.Count; i++) { if (i == events.Count - 1) event_ids += events[i]; else event_ids += events[i] + "|"; } string requestInfo = "game_key=" + game_key + "&event_ids=" + event_ids + "&area=" + area; requestInfo = requestInfo.Replace(" ", "%20"); if (startDate.HasValue && endDate.HasValue) { DateTime startDT = new DateTime(startDate.Value.Year, startDate.Value.Month, startDate.Value.Day, 0, 0, 0); DateTime endDT = new DateTime(endDate.Value.Year, endDate.Value.Month, endDate.Value.Day, 0, 0, 0); requestInfo += "&start_ts=" + DateTimeToUnixTimestamp(startDT) + "&end_ts=" + DateTimeToUnixTimestamp(endDT); } //Get the url with the request type string url = GetURL(Requests[RequestType.GA_GetHeatmapData]); url += "/?" + requestInfo; //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data WWW www = new WWW(url, new byte[] { 0 }, headers); GA.RunCoroutine(Request(www, RequestType.GA_GetHeatmapData, successEvent, errorEvent),()=>www.isDone); return www; }
public WWW RequestHeatmapData(List <string> events, string area, string build, DateTime?startDate, DateTime?endDate, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { string game_key = GA.SettingsGA.GameKey; string event_ids = ""; for (int i = 0; i < events.Count; i++) { if (i == events.Count - 1) { event_ids += events[i]; } else { event_ids += events[i] + "|"; } } string requestInfo = "game_key=" + game_key + "&event_ids=" + event_ids + "&area=" + area; if (!build.Equals("")) { requestInfo += "&build=" + build; } requestInfo = requestInfo.Replace(" ", "%20"); if (startDate.HasValue && endDate.HasValue) { DateTime startDT = new DateTime(startDate.Value.Year, startDate.Value.Month, startDate.Value.Day, 0, 0, 0); DateTime endDT = new DateTime(endDate.Value.Year, endDate.Value.Month, endDate.Value.Day, 0, 0, 0); requestInfo += "&start_ts=" + DateTimeToUnixTimestamp(startDT) + "&end_ts=" + DateTimeToUnixTimestamp(endDT); } //Get the url with the request type string url = GetURL(Requests[RequestType.GA_GetHeatmapData]); url += "/?" + requestInfo; WWW www = null; #if !UNITY_WP8 && !UNITY_METRO //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #else //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Dictionary <string, string> headers = new Dictionary <string, string>(); headers.Add("Authorization", GA.API.Submit.CreateMD5Hash(requestInfo + GA.SettingsGA.ApiKey)); //Try to send the data www = new WWW(url, new byte[] { 0 }, headers); #endif GA.RunCoroutine(Request(www, RequestType.GA_GetHeatmapData, successEvent, errorEvent), () => www.isDone); return(www); }
public WWW RequestHeatmapData(List <string> events, string area, string build, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { return(RequestHeatmapData(events, area, build, null, null, successEvent, errorEvent)); }
private IEnumerator Request(WWW www, RequestType requestType, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { yield return(www); GA.Log("GameAnalytics: URL " + www.url); try { if (!string.IsNullOrEmpty(www.error)) { throw new Exception(www.error); } //Get the JSON object from the response string text = www.text; text = text.Replace("null", "0"); Hashtable returnParam = (Hashtable)GA_MiniJSON.JsonDecode(text); if (returnParam != null) { GA.Log("GameAnalytics: Result: " + text); if (successEvent != null) { successEvent(requestType, returnParam, errorEvent); } } else { throw new Exception(text); } } catch (Exception e) { if (errorEvent != null) { errorEvent(e.Message); } } }
private IEnumerator Request(WWW www, RequestType requestType, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { yield return www; GA.Log("GameAnalytics: URL " + www.url); try { if (!string.IsNullOrEmpty(www.error)) { throw new Exception(www.error); } //Get the JSON object from the response string text = www.text; text = text.Replace("null","0"); Hashtable returnParam = (Hashtable)GA_MiniJSON.JsonDecode(text); if (returnParam != null) { GA.Log("GameAnalytics: Result: " + text); if (successEvent != null) { successEvent(requestType, returnParam, errorEvent); } } else { throw new Exception(text); } } catch (Exception e) { if (errorEvent != null) { errorEvent(e.Message); } } }
public WWW RequestHeatmapData(List<string> events, string area, string build, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { return RequestHeatmapData(events, area, build, null, null, successEvent, errorEvent); }
/// <summary> /// Devides a list of messages into categories and calls Submit to send the messages to the GA servers. /// </summary> /// <param name="item"> /// The list of messages (queue) <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> public void SubmitQueue(List <Item> queue, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string pubKey, string priKey) { if ((_publicKey.Equals("") || _privateKey.Equals("")) && (pubKey.Equals("") || priKey.Equals(""))) { if (!gaTracking) { GA.LogError("Game Key and/or Secret Key not set. Open GA_Settings to set keys."); } return; } //GA_TODO: Optimize by moving dictionary outside this fucntion. Submit is called often Dictionary <CategoryType, List <Item> > categories = new Dictionary <CategoryType, List <Item> >(); /* Put all the items in the queue into a list containing only the messages of that category type. * This way we end up with a list of items for each category type */ foreach (Item item in queue) { if (categories.ContainsKey(item.Type)) { /* If we already added another item of this type then remove the UserID, SessionID, and Build values if necessary. * These values only need to be present in each message once, since they will be the same for all items */ /* TODO: below not supported yet in API (exclude information) * activate once redundant data can be trimmed */ /* * if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) * item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]); * * if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) * item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID]); * * if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) * item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build]); */ /* TODO: remove below when API supports exclusion of data */ if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); } if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID], GA.API.GenericInfo.SessionID); } if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build], GA.SettingsGA.Build); } categories[item.Type].Add(item); } else { /* If we did not add another item of this type yet, then add the UserID, SessionID, and Build values if necessary. * These values only need to be present in each message once, since they will be the same for all items */ if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); } if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID], GA.API.GenericInfo.SessionID); } if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) { item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build], GA.SettingsGA.Build); } categories.Add(item.Type, new List <Item> { item }); } } GA.RunCoroutine(Submit(categories, successEvent, errorEvent, gaTracking, pubKey, priKey)); }
/// <summary> /// Takes a dictionary with a item list for each category type. All items in each category are submitted together to the GA server. /// </summary> /// <param name="item"> /// The list of items, each holding a message and service type <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> /// <returns> /// A <see cref="IEnumerator"/> /// </returns> public void Submit(Dictionary <CategoryType, List <Item> > categories, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string pubKey, string priKey) { if (pubKey.Equals("")) { pubKey = _publicKey; } if (priKey.Equals("")) { priKey = _privateKey; } //For each existing category, submit a message containing all the items of that category type foreach (KeyValuePair <CategoryType, List <Item> > kvp in categories) { List <Item> items = kvp.Value; if (items.Count == 0) { continue; } //Since all the items must have the same category (we make sure they do below) we can get the category from the first item CategoryType serviceType = items[0].Type; string url = GetURL(Categories[serviceType], pubKey); //Make sure that all items are of the same category type, and put all the parameter collections into a list List <Hashtable> itemsParameters = new List <Hashtable>(); for (int i = 0; i < items.Count; i++) { if (serviceType != items[i].Type) { GA.LogWarning("GA Error: All messages in a submit must be of the same service/category type."); if (errorEvent != null) { errorEvent(items); } } // if user ID is missing from the item add it now (could f.x. happen if custom user id is enabled, // and the item was added before the custom user id was provided) if (!items[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) { items[i].Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); } else if (items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] == null) { items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] = GA.API.GenericInfo.UserID; } Hashtable parameters; if (items[i].Count > 1) { /* so far we don't do anything special with stacked messages - we just send a single message * GA_TODO: stacked messages should be handle correctly.*/ parameters = items[i].Parameters; } else { parameters = items[i].Parameters; } itemsParameters.Add(parameters); } //Make a JSON array string out of the list of parameter collections string json = DictToJson(itemsParameters); /* If we do not have access to a network connection (or we are roaming (mobile devices) and GA_static_api.Settings.ALLOWROAMING is false), * and data is set to be archived, then archive the data and pretend the message was sent successfully */ if (GA.SettingsGA.ArchiveData && !gaTracking && !GA.SettingsGA.InternetConnectivity) { if (GA.SettingsGA.DebugMode) { GA.Log("GA: Archiving data (no network connection)."); } GA.API.Archive.ArchiveData(json, serviceType); if (successEvent != null) { successEvent(items, true); } } else if (!GA.SettingsGA.InternetConnectivity) { if (!gaTracking) { GA.LogWarning("GA Error: No network connection."); } if (errorEvent != null) { errorEvent(items); } } string jsonHash = CreateMD5Hash(json + priKey); WWW www = CreateSubmitWWW(url, json, jsonHash); GA.RunCoroutine(SendWWW(www, successEvent, errorEvent, gaTracking, json, jsonHash, items)); } }
/// <summary> /// Takes a dictionary with a item list for each category type. All items in each category are submitted together to the GA server. /// </summary> /// <param name="item"> /// The list of items, each holding a message and service type <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> /// <returns> /// A <see cref="IEnumerator"/> /// </returns> public void Submit(Dictionary<CategoryType, List<Item>> categories, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string pubKey, string priKey) { if (pubKey.Equals("")) pubKey = _publicKey; if (priKey.Equals("")) priKey = _privateKey; //For each existing category, submit a message containing all the items of that category type foreach (KeyValuePair<CategoryType, List<Item>> kvp in categories) { List<Item> items = kvp.Value; if (items.Count == 0) { continue; } //Since all the items must have the same category (we make sure they do below) we can get the category from the first item CategoryType serviceType = items[0].Type; string url = GetURL(Categories[serviceType], pubKey); //Make sure that all items are of the same category type, and put all the parameter collections into a list List<Hashtable> itemsParameters = new List<Hashtable>(); for (int i = 0; i < items.Count; i++) { if (serviceType != items[i].Type) { GA.LogWarning("GA Error: All messages in a submit must be of the same service/category type."); if (errorEvent != null) { errorEvent(items); } } // if user ID is missing from the item add it now (could f.x. happen if custom user id is enabled, // and the item was added before the custom user id was provided) if (!items[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) items[i].Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); else if (items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] == null) items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] = GA.API.GenericInfo.UserID; Hashtable parameters; if (items[i].Count > 1) { /* so far we don't do anything special with stacked messages - we just send a single message * GA_TODO: stacked messages should be handle correctly.*/ parameters = items[i].Parameters; } else { parameters = items[i].Parameters; } itemsParameters.Add(parameters); } //Make a JSON array string out of the list of parameter collections string json = DictToJson(itemsParameters); /* If we do not have access to a network connection (or we are roaming (mobile devices) and GA_static_api.Settings.ALLOWROAMING is false), * and data is set to be archived, then archive the data and pretend the message was sent successfully */ if (GA.SettingsGA.ArchiveData && !gaTracking && !GA.SettingsGA.InternetConnectivity) { if (GA.SettingsGA.DebugMode) { GA.Log("GA: Archiving data (no network connection)."); } GA.API.Archive.ArchiveData(json, serviceType); if (successEvent != null) { successEvent(items, true); } } else if (!GA.SettingsGA.InternetConnectivity) { if (!gaTracking) GA.LogWarning("GA Error: No network connection."); if (errorEvent != null) { errorEvent(items); } } string jsonHash = CreateMD5Hash(json + priKey); WWW www = CreateSubmitWWW(url, json, jsonHash); GA.RunCoroutine(SendWWW(www, successEvent, errorEvent, gaTracking, json, jsonHash, items)); } }
public static IEnumerator SendWWW(WWW www, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string json, string jsonHash, List<Item> items) { #if !UNITY_FLASH && !UNITY_WP8 && !UNITY_METRO //Set thread priority low www.threadPriority = ThreadPriority.Low; #endif //Wait for response yield return www; if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA URL: " + www.url); GA.Log("GA Submit: " + json); GA.Log("GA Hash: " + jsonHash); } try { if (!string.IsNullOrEmpty(www.error) && !CheckServerReply(www)) { throw new Exception(www.error); } //Get the JSON object from the response Hashtable returnParam = (Hashtable)GA_MiniJSON.JsonDecode(www.text); //If the response contains the key "status" with the value "ok" we know that the message was sent and recieved successfully if ((returnParam != null && returnParam.ContainsKey("status") && returnParam["status"].ToString().Equals("ok")) || CheckServerReply(www)) { if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA Result: " + www.text); } if (successEvent != null) { successEvent(items, true); } } else { /* The message was not sent and recieved successfully: Stop submitting all together if something * is completely wrong and we know we will not be able to submit any messages at all.. * Such as missing or invalid public and/or private keys */ if (returnParam != null && returnParam.ContainsKey("message") && returnParam["message"].ToString().Equals("Game not found") && returnParam.ContainsKey("code") && returnParam["code"].ToString().Equals("400")) { if (!gaTracking) GA.LogWarning("GA Error: " + www.text + " (NOTE: make sure your Game Key and Secret Key match the keys you recieved from the Game Analytics website. It might take a few minutes before a newly added game will be able to recieve data.)"); //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (!gaTracking) GA.LogWarning("GA Error: " + www.text); if (errorEvent != null) { errorEvent(items); } } } } catch (Exception e) { if (!gaTracking) GA.LogWarning("GA Error: " + e.Message); /* If we hit one of these errors we should not attempt to send the message again * (if necessary we already threw a GA Error which may be tracked) */ if (e.Message.Contains("400 Bad Request")) { //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (errorEvent != null) { errorEvent(items); } } } }
public static IEnumerator SendWWW(WWW www, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string json, string jsonHash, List <Item> items) { #if !UNITY_FLASH && !UNITY_WP8 && !UNITY_METRO //Set thread priority low www.threadPriority = ThreadPriority.Low; #endif //Wait for response yield return(www); if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA URL: " + www.url); GA.Log("GA Submit: " + json); GA.Log("GA Hash: " + jsonHash); } try { if (!string.IsNullOrEmpty(www.error) && !CheckServerReply(www)) { throw new Exception(www.error); } //Get the JSON object from the response Hashtable returnParam = (Hashtable)GA_MiniJSON.JsonDecode(www.text); //If the response contains the key "status" with the value "ok" we know that the message was sent and recieved successfully if ((returnParam != null && returnParam.ContainsKey("status") && returnParam["status"].ToString().Equals("ok")) || CheckServerReply(www)) { if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA Result: " + www.text); } if (successEvent != null) { successEvent(items, true); } } else { /* The message was not sent and recieved successfully: Stop submitting all together if something * is completely wrong and we know we will not be able to submit any messages at all.. * Such as missing or invalid public and/or private keys */ if (returnParam != null && returnParam.ContainsKey("message") && returnParam["message"].ToString().Equals("Game not found") && returnParam.ContainsKey("code") && returnParam["code"].ToString().Equals("400")) { if (!gaTracking) { GA.LogWarning("GA Error: " + www.text + " (NOTE: make sure your Game Key and Secret Key match the keys you recieved from the Game Analytics website. It might take a few minutes before a newly added game will be able to recieve data.)"); } //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (!gaTracking) { GA.LogWarning("GA Error: " + www.text); } if (errorEvent != null) { errorEvent(items); } } } } catch (Exception e) { if (!gaTracking) { GA.LogWarning("GA Error: " + e.Message); } /* If we hit one of these errors we should not attempt to send the message again * (if necessary we already threw a GA Error which may be tracked) */ if (e.Message.Contains("400 Bad Request")) { //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (errorEvent != null) { errorEvent(items); } } } }
/// <summary> /// Takes a dictionary with a item list for each category type. All items in each category are submitted together to the GA server. /// </summary> /// <param name="item"> /// The list of items, each holding a message and service type <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> /// <returns> /// A <see cref="IEnumerator"/> /// </returns> public IEnumerator Submit(Dictionary<CategoryType, List<Item>> categories, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { //For each existing category, submit a message containing all the items of that category type foreach (KeyValuePair<CategoryType, List<Item>> kvp in categories) { List<Item> items = kvp.Value; if (items.Count == 0) { yield break; } //Since all the items must have the same category (we make sure they do below) we can get the category from the first item CategoryType serviceType = items[0].Type; string url = GetURL(Categories[serviceType]); //Make sure that all items are of the same category type, and put all the parameter collections into a list List<Dictionary<string, object>> itemsParameters = new List<Dictionary<string, object>>(); for (int i = 0; i < items.Count; i++) { if (serviceType != items[i].Type) { GA.LogWarning("GA Error: All messages in a submit must be of the same service/category type."); if (errorEvent != null) { errorEvent(items); } yield break; } // if user ID is missing from the item add it now (could f.x. happen if custom user id is enabled, // and the item was added before the custom user id was provided) if (!items[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) items[i].Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); else if (items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] == null) items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] = GA.API.GenericInfo.UserID; Dictionary<string, object> parameters; if (items[i].Count > 1) { /* so far we don't do anything special with stacked messages - we just send a single message * GA_TODO: stacked messages should be handle correctly.*/ parameters = items[i].Parameters; } else { parameters = items[i].Parameters; } itemsParameters.Add(parameters); } //Make a JSON array string out of the list of parameter collections string json = DictToJson(itemsParameters); /* If we do not have access to a network connection (or we are roaming (mobile devices) and GA_static_api.Settings.ALLOWROAMING is false), * and data is set to be archived, then archive the data and pretend the message was sent successfully */ if (GA.Settings.ArchiveData && !GA.Settings.InternetConnectivity) { if (GA.Settings.DebugMode) { GA.Log("GA: Archiving data (no network connection)."); } GA.API.Archive.ArchiveData(json, serviceType); if (successEvent != null) { successEvent(items, true); } yield break; } else if (!GA.Settings.InternetConnectivity) { GA.LogWarning("GA Error: No network connection."); if (errorEvent != null) { errorEvent(items); } yield break; } //Prepare the JSON array string for sending by converting it to a byte array byte[] data = Encoding.ASCII.GetBytes(json); //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", CreateMD5Hash(json + _privateKey)); //Try to send the data WWW www = new WWW(url, data, headers); //Set thread priority low www.threadPriority = ThreadPriority.Low; //Wait for response yield return www; if (GA.Settings.DebugMode) { GA.Log("GA URL: " + url); GA.Log("GA Submit: " + json); GA.Log("GA Hash: " + CreateMD5Hash(json + _privateKey)); } try { if (www.error != null && !CheckServerReply(www)) { throw new Exception(www.error); } //Get the JSON object from the response Dictionary<string, object> returnParam = JsonMapper.ToObject<Dictionary<string, object>>(www.text); //If the response contains the key "status" with the value "ok" we know that the message was sent and recieved successfully if ((returnParam != null && returnParam.ContainsKey("status") && returnParam["status"].ToString().Equals("ok")) || CheckServerReply(www)) { if (GA.Settings.DebugMode) { GA.Log("GA Result: " + www.text); } if (successEvent != null) { successEvent(items, true); } } else { /* The message was not sent and recieved successfully: Stop submitting all together if something * is completely wrong and we know we will not be able to submit any messages at all.. * Such as missing or invalid public and/or private keys */ if (returnParam != null && returnParam.ContainsKey("message") && returnParam["message"].ToString().Equals("Game not found") && returnParam.ContainsKey("code") && returnParam["code"].ToString().Equals("400")) { GA.LogWarning("GA Error: " + www.text + " (NOTE: make sure your Game Key and Secret Key match the keys you recieved from the Game Analytics website. It might take a few minutes before a newly added game will be able to recieve data.)"); //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { GA.LogWarning("GA Error: " + www.text); if (errorEvent != null) { errorEvent(items); } } } } catch (Exception e) { GA.LogWarning("GA Error: " + e.Message); /* If we hit one of these errors we should not attempt to send the message again * (if necessary we already threw a GA Error which may be tracked) */ if (e.Message.Contains("400 Bad Request")) { //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (errorEvent != null) { errorEvent(items); } } } } }
/// <summary> /// Devides a list of messages into categories and calls Submit to send the messages to the GA servers. /// </summary> /// <param name="item"> /// The list of messages (queue) <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> public void SubmitQueue(List<Item> queue, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent) { if (_publicKey.Equals("") || _privateKey.Equals("")) { GA.LogError("Game Key and/or Secret Key not set. Open GA_Settings to set keys."); return; } //GA_TODO: Optimize by moving dictionary outside this fucntion. Submit is called often Dictionary<CategoryType, List<Item>> categories = new Dictionary<CategoryType, List<Item>>(); /* Put all the items in the queue into a list containing only the messages of that category type. * This way we end up with a list of items for each category type */ foreach (Item item in queue) { if (categories.ContainsKey(item.Type)) { /* If we already added another item of this type then remove the UserID, SessionID, and Build values if necessary. * These values only need to be present in each message once, since they will be the same for all items */ /* TODO: below not supported yet in API (exclude information) * activate once redundant data can be trimmed */ /* if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]); if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID]); if (item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) item.Parameters.Remove(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build]); */ /* TODO: remove below when API supports exclusion of data */ if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID], GA.API.GenericInfo.SessionID); if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build], GA.Settings.Build); categories[item.Type].Add(item); } else { /* If we did not add another item of this type yet, then add the UserID, SessionID, and Build values if necessary. * These values only need to be present in each message once, since they will be the same for all items */ if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.SessionID], GA.API.GenericInfo.SessionID); if (!item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build])) item.Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Build], GA.Settings.Build); categories.Add(item.Type, new List<Item> { item }); } } GA.RunCoroutine(Submit(categories, successEvent, errorEvent)); }
/// <summary> /// Takes a dictionary with a item list for each category type. All items in each category are submitted together to the GA server. /// </summary> /// <param name="item"> /// The list of items, each holding a message and service type <see cref="Item"/> /// </param> /// <param name="successEvent"> /// If successful this will be fired <see cref="SubmitSuccessHandler"/> /// </param> /// <param name="errorEvent"> /// If an error occurs this will be fired <see cref="SubmitErrorHandler"/> /// </param> /// <returns> /// A <see cref="IEnumerator"/> /// </returns> public IEnumerator Submit(Dictionary <CategoryType, List <Item> > categories, SubmitSuccessHandler successEvent, SubmitErrorHandler errorEvent, bool gaTracking, string pubKey, string priKey) { if (pubKey.Equals("")) { pubKey = _publicKey; } if (priKey.Equals("")) { priKey = _privateKey; } //For each existing category, submit a message containing all the items of that category type foreach (KeyValuePair <CategoryType, List <Item> > kvp in categories) { List <Item> items = kvp.Value; if (items.Count == 0) { yield break; } //Since all the items must have the same category (we make sure they do below) we can get the category from the first item CategoryType serviceType = items[0].Type; string url = GetURL(Categories[serviceType], pubKey); //Make sure that all items are of the same category type, and put all the parameter collections into a list List <Hashtable> itemsParameters = new List <Hashtable>(); for (int i = 0; i < items.Count; i++) { if (serviceType != items[i].Type) { GA.LogWarning("GA Error: All messages in a submit must be of the same service/category type."); if (errorEvent != null) { errorEvent(items); } yield break; } // if user ID is missing from the item add it now (could f.x. happen if custom user id is enabled, // and the item was added before the custom user id was provided) if (!items[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID])) { items[i].Parameters.Add(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID], GA.API.GenericInfo.UserID); } else if (items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] == null) { items[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.UserID]] = GA.API.GenericInfo.UserID; } Hashtable parameters; if (items[i].Count > 1) { /* so far we don't do anything special with stacked messages - we just send a single message * GA_TODO: stacked messages should be handle correctly.*/ parameters = items[i].Parameters; } else { parameters = items[i].Parameters; } itemsParameters.Add(parameters); } //Make a JSON array string out of the list of parameter collections string json = DictToJson(itemsParameters); /* If we do not have access to a network connection (or we are roaming (mobile devices) and GA_static_api.Settings.ALLOWROAMING is false), * and data is set to be archived, then archive the data and pretend the message was sent successfully */ if (GA.SettingsGA.ArchiveData && !gaTracking && !GA.SettingsGA.InternetConnectivity) { if (GA.SettingsGA.DebugMode) { GA.Log("GA: Archiving data (no network connection)."); } GA.API.Archive.ArchiveData(json, serviceType); if (successEvent != null) { successEvent(items, true); } yield break; } else if (!GA.SettingsGA.InternetConnectivity) { if (!gaTracking) { GA.LogWarning("GA Error: No network connection."); } if (errorEvent != null) { errorEvent(items); } yield break; } //Prepare the JSON array string for sending by converting it to a byte array byte[] data = Encoding.UTF8.GetBytes(json); WWW www = null; #if !UNITY_WP8 && !UNITY_METRO //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Hashtable headers = new Hashtable(); headers.Add("Authorization", CreateMD5Hash(json + priKey)); //headers.Add("Content-Length", data.Length); //Try to send the data www = new WWW(url, data, headers); #else //Set the authorization header to contain an MD5 hash of the JSON array string + the private key Dictionary <string, string> headers = new Dictionary <string, string>(); headers.Add("Authorization", CreateMD5Hash(json + priKey)); headers.Add("Content-Length", data.Length.ToString()); //Try to send the data www = new WWW(url, data, headers); #endif #if !UNITY_FLASH && !UNITY_WP8 && !UNITY_METRO //Set thread priority low www.threadPriority = ThreadPriority.Low; #endif //Wait for response yield return(www); if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA URL: " + url); GA.Log("GA Submit: " + json); GA.Log("GA Hash: " + CreateMD5Hash(json + priKey)); } try { if (www.error != null && !CheckServerReply(www)) { throw new Exception(www.error); } //Get the JSON object from the response Hashtable returnParam = (Hashtable)GA_MiniJSON.JsonDecode(www.text); //If the response contains the key "status" with the value "ok" we know that the message was sent and recieved successfully if ((returnParam != null && returnParam.ContainsKey("status") && returnParam["status"].ToString().Equals("ok")) || CheckServerReply(www)) { if (GA.SettingsGA.DebugMode && !gaTracking) { GA.Log("GA Result: " + www.text); } if (successEvent != null) { successEvent(items, true); } } else { /* The message was not sent and recieved successfully: Stop submitting all together if something * is completely wrong and we know we will not be able to submit any messages at all.. * Such as missing or invalid public and/or private keys */ if (returnParam != null && returnParam.ContainsKey("message") && returnParam["message"].ToString().Equals("Game not found") && returnParam.ContainsKey("code") && returnParam["code"].ToString().Equals("400")) { if (!gaTracking) { GA.LogWarning("GA Error: " + www.text + " (NOTE: make sure your Game Key and Secret Key match the keys you recieved from the Game Analytics website. It might take a few minutes before a newly added game will be able to recieve data.)"); } //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (!gaTracking) { GA.LogWarning("GA Error: " + www.text); } if (errorEvent != null) { errorEvent(items); } } } } catch (Exception e) { if (!gaTracking) { GA.LogWarning("GA Error: " + e.Message); } /* If we hit one of these errors we should not attempt to send the message again * (if necessary we already threw a GA Error which may be tracked) */ if (e.Message.Contains("400 Bad Request")) { //An error event with a null parameter will stop the GA wrapper from submitting messages if (errorEvent != null) { errorEvent(null); } } else { if (errorEvent != null) { errorEvent(items); } } } } }