Ta android + ios
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
namespace ThinkingData.Analytics.Utils
|
||||
{
|
||||
public class TDCommonUtils
|
||||
{
|
||||
public static string FormatDate(DateTime dateTime, TimeZoneInfo timeZone)
|
||||
{
|
||||
bool success = true;
|
||||
DateTime univDateTime = dateTime.ToUniversalTime();
|
||||
TimeSpan timeSpan = new TimeSpan();
|
||||
try
|
||||
{
|
||||
timeSpan = timeZone.BaseUtcOffset;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
success = false;
|
||||
//if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("FormatDate - TimeSpan get failed : " + e.Message);
|
||||
}
|
||||
try
|
||||
{
|
||||
if (timeZone.IsDaylightSavingTime(dateTime))
|
||||
{
|
||||
TimeSpan timeSpan1 = TimeSpan.FromHours(1);
|
||||
timeSpan = timeSpan.Add(timeSpan1);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
success = false;
|
||||
//if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("FormatDate: IsDaylightSavingTime get failed : " + e.Message);
|
||||
}
|
||||
if (success == false)
|
||||
{
|
||||
timeSpan = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
|
||||
}
|
||||
try
|
||||
{
|
||||
DateTime dateNew = univDateTime + timeSpan;
|
||||
return dateNew.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return univDateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string FormatDate(DateTime dateTime, TDTimeZone timeZone) {
|
||||
DateTime univDateTime = dateTime.ToUniversalTime();
|
||||
TimeSpan span;
|
||||
switch (timeZone)
|
||||
{
|
||||
case TDTimeZone.Local:
|
||||
span = TimeZoneInfo.Local.BaseUtcOffset;
|
||||
break;
|
||||
case TDTimeZone.UTC:
|
||||
span = TimeSpan.Zero;
|
||||
break;
|
||||
case TDTimeZone.Asia_Shanghai:
|
||||
span = TimeSpan.FromHours(8);
|
||||
break;
|
||||
case TDTimeZone.Asia_Tokyo:
|
||||
span = TimeSpan.FromHours(9);
|
||||
break;
|
||||
case TDTimeZone.America_Los_Angeles:
|
||||
span = TimeSpan.FromHours(-7);
|
||||
break;
|
||||
case TDTimeZone.America_New_York:
|
||||
span = TimeSpan.FromHours(-4);
|
||||
break;
|
||||
default:
|
||||
span = TimeSpan.Zero;
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
DateTime dateNew = univDateTime + span;
|
||||
return dateNew.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception) {
|
||||
}
|
||||
return univDateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56ef1531e6e06454aa9a6700b418632d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ThinkingData.Analytics.Utils
|
||||
{
|
||||
public class TDLog
|
||||
{
|
||||
private static bool enableLog;
|
||||
public static void EnableLog(bool enabled)
|
||||
{
|
||||
enableLog = enabled;
|
||||
}
|
||||
|
||||
public static bool GetEnable()
|
||||
{
|
||||
return enableLog;
|
||||
}
|
||||
|
||||
|
||||
public static void i(string message)
|
||||
{
|
||||
if (enableLog)
|
||||
{
|
||||
Debug.Log("[ThinkingData] Info: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(string message)
|
||||
{
|
||||
if (enableLog)
|
||||
{
|
||||
Debug.Log("[ThinkingData] Debug: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(string message)
|
||||
{
|
||||
if (enableLog)
|
||||
{
|
||||
Debug.LogError("[ThinkingData] Error: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(string message)
|
||||
{
|
||||
if (enableLog)
|
||||
{
|
||||
Debug.LogWarning("[ThinkingData] Warning: " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b49f9a69526c4417963783322d7e904
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
* MIT License. Forked from GA_MiniJSON.
|
||||
* I modified it so that it could be used for TD limitations.
|
||||
*/
|
||||
// using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ThinkingData.Analytics.Utils
|
||||
{
|
||||
/* Based on the JSON parser from
|
||||
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
|
||||
*
|
||||
* I simplified it so that it doesn't throw exceptions
|
||||
* and can be used in Unity iPhone with maximum code stripping.
|
||||
*/
|
||||
/// <summary>
|
||||
/// This class encodes and decodes JSON strings.
|
||||
/// Spec. details, see http://www.json.org/
|
||||
///
|
||||
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
|
||||
/// All numbers are parsed to floats.
|
||||
/// </summary>
|
||||
public class TDMiniJson
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the string json into a value
|
||||
/// </summary>
|
||||
/// <param name="json">A JSON string.</param>
|
||||
/// <returns>An List<object>, a Dictionary<string, object>, a double, an integer, a string, null, true, or false</returns>
|
||||
public static Dictionary<string, object> Deserialize(string json)
|
||||
{
|
||||
// save the string for debug information
|
||||
if (json == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Parser.Parse(json);
|
||||
}
|
||||
|
||||
sealed class Parser : IDisposable
|
||||
{
|
||||
const string WORD_BREAK = "{}[],:\"";
|
||||
|
||||
public static bool IsWordBreak(char c)
|
||||
{
|
||||
return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1;
|
||||
}
|
||||
|
||||
enum TOKEN
|
||||
{
|
||||
NONE,
|
||||
CURLY_OPEN,
|
||||
CURLY_CLOSE,
|
||||
SQUARED_OPEN,
|
||||
SQUARED_CLOSE,
|
||||
COLON,
|
||||
COMMA,
|
||||
STRING,
|
||||
NUMBER,
|
||||
TRUE,
|
||||
FALSE,
|
||||
NULL
|
||||
};
|
||||
|
||||
StringReader json;
|
||||
|
||||
Parser(string jsonString)
|
||||
{
|
||||
json = new StringReader(jsonString);
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> Parse(string jsonString)
|
||||
{
|
||||
using (var instance = new Parser(jsonString))
|
||||
{
|
||||
return instance.ParseObject();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
json.Dispose();
|
||||
json = null;
|
||||
}
|
||||
|
||||
Dictionary<string, object> ParseObject()
|
||||
{
|
||||
Dictionary<string, object> table = new Dictionary<string, object>();
|
||||
|
||||
// ditch opening brace
|
||||
json.Read();
|
||||
|
||||
// {
|
||||
while (true)
|
||||
{
|
||||
switch (NextToken)
|
||||
{
|
||||
case TOKEN.NONE:
|
||||
return null;
|
||||
case TOKEN.COMMA:
|
||||
continue;
|
||||
case TOKEN.CURLY_CLOSE:
|
||||
return table;
|
||||
default:
|
||||
// name
|
||||
string name = ParseString();
|
||||
if (name == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// :
|
||||
if (NextToken != TOKEN.COLON)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// ditch the colon
|
||||
json.Read();
|
||||
|
||||
// value
|
||||
table[name] = ParseValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<object> ParseArray()
|
||||
{
|
||||
List<object> array = new List<object>();
|
||||
|
||||
// ditch opening bracket
|
||||
json.Read();
|
||||
|
||||
// [
|
||||
var parsing = true;
|
||||
while (parsing)
|
||||
{
|
||||
TOKEN nextToken = NextToken;
|
||||
|
||||
switch (nextToken)
|
||||
{
|
||||
case TOKEN.NONE:
|
||||
return null;
|
||||
case TOKEN.COMMA:
|
||||
continue;
|
||||
case TOKEN.SQUARED_CLOSE:
|
||||
parsing = false;
|
||||
break;
|
||||
default:
|
||||
object value = ParseByToken(nextToken);
|
||||
|
||||
array.Add(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
object ParseValue()
|
||||
{
|
||||
TOKEN nextToken = NextToken;
|
||||
return ParseByToken(nextToken);
|
||||
}
|
||||
|
||||
object ParseByToken(TOKEN token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case TOKEN.STRING:
|
||||
string str = ParseString();
|
||||
DateTime dateTime;
|
||||
if (DateTime.TryParseExact(str, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
{
|
||||
return dateTime;
|
||||
}
|
||||
return str;
|
||||
case TOKEN.NUMBER:
|
||||
return ParseNumber();
|
||||
case TOKEN.CURLY_OPEN:
|
||||
return ParseObject();
|
||||
case TOKEN.SQUARED_OPEN:
|
||||
return ParseArray();
|
||||
case TOKEN.TRUE:
|
||||
return true;
|
||||
case TOKEN.FALSE:
|
||||
return false;
|
||||
case TOKEN.NULL:
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string ParseString()
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
char c;
|
||||
|
||||
// ditch opening quote
|
||||
json.Read();
|
||||
|
||||
bool parsing = true;
|
||||
while (parsing)
|
||||
{
|
||||
|
||||
if (json.Peek() == -1)
|
||||
{
|
||||
parsing = false;
|
||||
break;
|
||||
}
|
||||
|
||||
c = NextChar;
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
parsing = false;
|
||||
break;
|
||||
case '\\':
|
||||
if (json.Peek() == -1)
|
||||
{
|
||||
parsing = false;
|
||||
break;
|
||||
}
|
||||
|
||||
c = NextChar;
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
s.Append(c);
|
||||
break;
|
||||
case 'b':
|
||||
s.Append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
s.Append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
s.Append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
s.Append('\r');
|
||||
break;
|
||||
case 't':
|
||||
s.Append('\t');
|
||||
break;
|
||||
case 'u':
|
||||
var hex = new char[4];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
hex[i] = NextChar;
|
||||
}
|
||||
|
||||
s.Append((char)Convert.ToInt32(new string(hex), 16));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
object ParseNumber()
|
||||
{
|
||||
string number = NextWord;
|
||||
|
||||
if (number.IndexOf('.') == -1 && number.IndexOf('E') == -1 && number.IndexOf('e') == -1)
|
||||
{
|
||||
long parsedInt;
|
||||
Int64.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedInt);
|
||||
return parsedInt;
|
||||
}
|
||||
|
||||
double parsedDouble;
|
||||
if (!Double.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble))
|
||||
{
|
||||
Double.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CreateSpecificCulture("es-ES"), out parsedDouble);
|
||||
}
|
||||
return parsedDouble;
|
||||
}
|
||||
|
||||
void EatWhitespace()
|
||||
{
|
||||
while (Char.IsWhiteSpace(PeekChar))
|
||||
{
|
||||
json.Read();
|
||||
|
||||
if (json.Peek() == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char PeekChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToChar(json.Peek());
|
||||
}
|
||||
}
|
||||
|
||||
char NextChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToChar(json.Read());
|
||||
}
|
||||
}
|
||||
|
||||
string NextWord
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder word = new StringBuilder();
|
||||
|
||||
while (!IsWordBreak(PeekChar))
|
||||
{
|
||||
word.Append(NextChar);
|
||||
|
||||
if (json.Peek() == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return word.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
TOKEN NextToken
|
||||
{
|
||||
get
|
||||
{
|
||||
EatWhitespace();
|
||||
|
||||
if (json.Peek() == -1)
|
||||
{
|
||||
return TOKEN.NONE;
|
||||
}
|
||||
|
||||
switch (PeekChar)
|
||||
{
|
||||
case '{':
|
||||
return TOKEN.CURLY_OPEN;
|
||||
case '}':
|
||||
json.Read();
|
||||
return TOKEN.CURLY_CLOSE;
|
||||
case '[':
|
||||
return TOKEN.SQUARED_OPEN;
|
||||
case ']':
|
||||
json.Read();
|
||||
return TOKEN.SQUARED_CLOSE;
|
||||
case ',':
|
||||
json.Read();
|
||||
return TOKEN.COMMA;
|
||||
case '"':
|
||||
return TOKEN.STRING;
|
||||
case ':':
|
||||
return TOKEN.COLON;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
return TOKEN.NUMBER;
|
||||
}
|
||||
|
||||
switch (NextWord)
|
||||
{
|
||||
case "false":
|
||||
return TOKEN.FALSE;
|
||||
case "true":
|
||||
return TOKEN.TRUE;
|
||||
case "null":
|
||||
return TOKEN.NULL;
|
||||
}
|
||||
|
||||
return TOKEN.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
|
||||
/// </summary>
|
||||
/// <param name="json">A Dictionary<string, object> / List<object></param>
|
||||
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
|
||||
public static string Serialize(object obj, Func<DateTime, string> func = null)
|
||||
{
|
||||
return Serializer.Serialize(obj, func);
|
||||
}
|
||||
|
||||
sealed class Serializer
|
||||
{
|
||||
StringBuilder builder;
|
||||
Func<DateTime, string> func;
|
||||
|
||||
Serializer()
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
|
||||
public static string Serialize(object obj, Func<DateTime, string> func)
|
||||
{
|
||||
var instance = new Serializer();
|
||||
instance.func = func;
|
||||
|
||||
instance.SerializeValue(obj);
|
||||
|
||||
return instance.builder.ToString();
|
||||
}
|
||||
|
||||
void SerializeValue(object value)
|
||||
{
|
||||
IList asList;
|
||||
IDictionary asDict;
|
||||
string asStr;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
builder.Append("null");
|
||||
}
|
||||
else if ((asStr = value as string) != null)
|
||||
{
|
||||
SerializeString(asStr);
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
builder.Append((bool)value ? "true" : "false");
|
||||
}
|
||||
else if ((asList = value as IList) != null)
|
||||
{
|
||||
SerializeArray(asList);
|
||||
}
|
||||
else if ((asDict = value as IDictionary) != null)
|
||||
{
|
||||
SerializeObject(asDict);
|
||||
}
|
||||
else if (value is char)
|
||||
{
|
||||
SerializeString(new string((char)value, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializeOther(value);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeObject(IDictionary obj)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
builder.Append('{');
|
||||
|
||||
foreach (object e in obj.Keys)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
builder.Append(',');
|
||||
}
|
||||
|
||||
SerializeString(e.ToString());
|
||||
builder.Append(':');
|
||||
|
||||
SerializeValue(obj[e]);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append('}');
|
||||
}
|
||||
|
||||
void SerializeArray(IList anArray)
|
||||
{
|
||||
builder.Append('[');
|
||||
|
||||
bool first = true;
|
||||
|
||||
foreach (object obj in anArray)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
builder.Append(',');
|
||||
}
|
||||
|
||||
SerializeValue(obj);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
void SerializeString(string str)
|
||||
{
|
||||
builder.Append('\"');
|
||||
|
||||
char[] charArray = str.ToCharArray();
|
||||
foreach (var c in charArray)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
builder.Append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
builder.Append("\\\\");
|
||||
break;
|
||||
case '\b':
|
||||
builder.Append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
builder.Append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
builder.Append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
builder.Append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
builder.Append("\\t");
|
||||
break;
|
||||
default:
|
||||
int codepoint = Convert.ToInt32(c);
|
||||
if ((codepoint >= 32) && (codepoint <= 126))
|
||||
{
|
||||
builder.Append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("\\u");
|
||||
builder.Append(codepoint.ToString("x4"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append('\"');
|
||||
}
|
||||
|
||||
void SerializeOther(object value)
|
||||
{
|
||||
// NOTE: decimals lose precision during serialization.
|
||||
// They always have, I'm just letting you know.
|
||||
// Previously floats and doubles lost precision too.
|
||||
if (value is float)
|
||||
{
|
||||
builder.Append(((float)value).ToString("R", System.Globalization.CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (value is int
|
||||
|| value is uint
|
||||
|| value is long
|
||||
|| value is sbyte
|
||||
|| value is byte
|
||||
|| value is short
|
||||
|| value is ushort
|
||||
|| value is ulong)
|
||||
{
|
||||
builder.Append(value);
|
||||
}
|
||||
else if (value is double)
|
||||
{
|
||||
builder.Append(Convert.ToDouble(value).ToString("R", System.Globalization.CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (value is decimal) {
|
||||
builder.Append(Convert.ToDecimal(value).ToString("G", System.Globalization.CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (value is DateTime)
|
||||
{
|
||||
builder.Append('\"');
|
||||
DateTime dateTime = (DateTime)value;
|
||||
if (null != func)
|
||||
{
|
||||
builder.Append(func((DateTime)value));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture));
|
||||
}
|
||||
builder.Append('\"');
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializeString(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03cd5f56606ff4dd694406f5594f0cf2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ThinkingData.Analytics.Utils
|
||||
{
|
||||
public class TDPropertiesChecker
|
||||
{
|
||||
private static readonly Regex keyPattern = new Regex(@"^[a-zA-Z][a-zA-Z\d_#]{0,49}$");
|
||||
private static readonly List<string> propertyNameWhitelist = new List<string>() { "#scene_name", "#scene_path", "#app_crashed_reason" };
|
||||
|
||||
public static bool IsNumeric(object obj)
|
||||
{
|
||||
return obj is sbyte
|
||||
|| obj is byte
|
||||
|| obj is short
|
||||
|| obj is ushort
|
||||
|| obj is int
|
||||
|| obj is uint
|
||||
|| obj is long
|
||||
|| obj is ulong
|
||||
|| obj is double
|
||||
|| obj is decimal
|
||||
|| obj is float;
|
||||
}
|
||||
public static bool IsString(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
return obj is string;
|
||||
}
|
||||
public static bool IsDictionary(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
return (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(Dictionary<,>));
|
||||
}
|
||||
public static bool IsList(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
return (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(List<>)) || obj is Array;
|
||||
}
|
||||
public static bool CheckProperties<V>(Dictionary<string, V> properties)
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool ret = true;
|
||||
foreach(KeyValuePair<string, V> kv in properties)
|
||||
{
|
||||
if (!CheckString(kv.Key))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
if (!(kv.Value is string || kv.Value is DateTime || kv.Value is bool || IsNumeric(kv.Value) || IsList(kv.Value) || IsDictionary(kv.Value)))
|
||||
{
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect property - property values must be one of: String, Numberic, Boolean, DateTime, Array, Row");
|
||||
ret = false;
|
||||
}
|
||||
if (IsString(kv.Value) && !CheckProperties(kv.Value as string))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
if (IsNumeric(kv.Value)) {
|
||||
double number = Convert.ToDouble(kv.Value);
|
||||
if (!CheckProperties(number))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (IsList(kv.Value) && !CheckProperties(kv.Value as List<object>)) {
|
||||
ret = false;
|
||||
}
|
||||
if (IsDictionary(kv.Value) && !CheckProperties(kv.Value as Dictionary<string, object>))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public static bool CheckProperties(List<object> properties)
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool ret = true;
|
||||
foreach (object value in properties)
|
||||
{
|
||||
if (!(value is string || value is DateTime || value is bool || IsNumeric(value) || IsDictionary(value)))
|
||||
{
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect property - property values in list must be one of: String, Numberic, Boolean, DateTime, Row");
|
||||
ret = false;
|
||||
}
|
||||
if (IsString(value) && !CheckProperties(value as string))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
if (IsNumeric(value)) {
|
||||
double number = Convert.ToDouble(value);
|
||||
if (!CheckProperties(number))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (IsDictionary(value) && !CheckProperties(value as Dictionary<string, object>))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public static bool CheckProperties(List<string> properties)
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
foreach(string value in properties)
|
||||
{
|
||||
if (!CheckProperties(value))
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public static bool CheckProperties(string properties)
|
||||
{
|
||||
if (properties is string && System.Text.Encoding.UTF8.GetBytes(Convert.ToString(properties)).Length > 2048) {
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect properties - the string is too long: " + (string)(object)properties);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static bool CheckProperties(double properties)
|
||||
{
|
||||
if (properties > 9999999999999.999 || properties < -9999999999999.999)
|
||||
{
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect properties - number value is invalid: " + properties + ", the data range is -9E15 to 9E15, with a maximum of 3 decimal places");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static bool CheckString(string eventName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(eventName))
|
||||
{
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect event name - the string is null");
|
||||
return false;
|
||||
}
|
||||
if (keyPattern.IsMatch(eventName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (propertyNameWhitelist.Contains(eventName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(TDLog.GetEnable()) TDLog.w("Incorrect event name - the string is invalid for TDAnalytics: " + eventName + ", event name and properties name rules: must be character string type, starting with a character and containing figures, characters, and an underline \"_\", with a maximum length of 50 characters");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static void MergeProperties(Dictionary<string, object> source, Dictionary<string, object> dest)
|
||||
{
|
||||
if (null == source) return;
|
||||
foreach (KeyValuePair<string, object> kv in source)
|
||||
{
|
||||
if (dest.ContainsKey(kv.Key))
|
||||
{
|
||||
dest[kv.Key] = kv.Value;
|
||||
} else
|
||||
{
|
||||
dest.Add(kv.Key, kv.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d887a818e74eb4316a839030f930036b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ThinkingData.Analytics.Utils
|
||||
{
|
||||
// Crosss Platform
|
||||
public enum TDThirdPartyType
|
||||
{
|
||||
NONE = 0,
|
||||
APPSFLYER = 1 << 0, // AppsFlyer
|
||||
IRONSOURCE = 1 << 1, // IronSource
|
||||
ADJUST = 1 << 2, // Adjust
|
||||
BRANCH = 1 << 3, // Branch
|
||||
TOPON = 1 << 4, // TopOn
|
||||
TRACKING = 1 << 5, // ReYun
|
||||
TRADPLUS = 1 << 6, // TradPlus
|
||||
};
|
||||
|
||||
// SSL
|
||||
public enum TDSSLPinningMode
|
||||
{
|
||||
NONE = 0, // Only allow certificates trusted by the system
|
||||
PUBLIC_KEY = 1 << 0, // Verify public key
|
||||
CERTIFICATE = 1 << 1 // Verify all contents
|
||||
}
|
||||
|
||||
public class TDPublicConfig
|
||||
{
|
||||
public static bool DisableCSharpException = false;
|
||||
public static List<string> DisPresetProperties = new List<string>();
|
||||
|
||||
public static readonly string LIB_VERSION = "3.0.7";
|
||||
|
||||
public static void GetPublicConfig()
|
||||
{
|
||||
TextAsset textAsset = Resources.Load<TextAsset>("ta_public_config");
|
||||
if (textAsset != null && !string.IsNullOrEmpty(textAsset.text))
|
||||
{
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(textAsset.text);
|
||||
XmlNode root = xmlDoc.SelectSingleNode("resources");
|
||||
for (int i=0; i<root.ChildNodes.Count; i++)
|
||||
{
|
||||
XmlNode x1 = root.ChildNodes[i];
|
||||
if (x1.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
XmlElement e1 = x1 as XmlElement;
|
||||
if (e1.HasAttributes)
|
||||
{
|
||||
string name = e1.GetAttribute("name");
|
||||
if (name == "TDDisPresetProperties" && e1.HasChildNodes)
|
||||
{
|
||||
for (int j=0; j<e1.ChildNodes.Count; j++)
|
||||
{
|
||||
XmlNode x2 = e1.ChildNodes[j];
|
||||
if (x2.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
DisPresetProperties.Add(x2.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (name == "DisableCSharpException")
|
||||
{
|
||||
DisableCSharpException = Convert.ToBoolean(e1.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31781ef5586fb47b68b6d0dbb4bc79a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user