/*
*
Copyright 2019, ThinkingData, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SDK VERSION:2.2.1
*/
#if !(UNITY_5_4_OR_NEWER)
#define DISABLE_TA
#warning "Your Unity version is not supported by us - ThinkingAnalyticsSDK disabled"
#endif
#if !(UNITY_EDITOR || UNITY_IOS || UNITY_ANDROID || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN)
#define DISABLE_TA
#warning "Your Unity Platfrom is not supported by us - ThinkingAnalyticsSDK disabled"
#endif
using System;
using System.Collections.Generic;
using System.Threading;
using ThinkingAnalytics.Utils;
using ThinkingAnalytics.Wrapper;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
//using UnityEditor.iOS.Xcode;
#endif
using System.IO;
using ThinkingAnalytics.TaException;
namespace ThinkingAnalytics
{
///
/// Dynamic super properties interfaces.
///
public interface IDynamicSuperProperties
{
Dictionary GetDynamicSuperProperties();
}
///
/// 内部使用的特殊事件类, 不要直接使用此类。
///
public class ThinkingAnalyticsEvent
{
public enum Type
{
FIRST,
UPDATABLE,
OVERWRITABLE
}
public ThinkingAnalyticsEvent(string eventName, Dictionary properties) {
EventName = eventName;
Properties = properties;
}
public Type? EventType { get; set; }
public string EventName { get; set; }
public Dictionary Properties { get; set; }
public DateTime EventTime { get; set; }
public string ExtraId { get; set; }
}
///
/// 首次(唯一)事件。默认情况下采集设备首次事件。请咨询数数客户成功获取支持。
///
public class TDFirstEvent : ThinkingAnalyticsEvent
{
public TDFirstEvent(string eventName, Dictionary properties) : base(eventName, properties)
{
EventType = Type.FIRST;
}
///
/// 设置用于检测是否首次的 ID,默认情况下会使用设备 ID
///
/// 用于首次事件检测的 ID
public void SetFirstCheckId(string firstCheckId)
{
ExtraId = firstCheckId;
}
}
///
/// 可被更新的事件。请咨询数数客户成功获取支持。
///
public class TDUpdatableEvent : ThinkingAnalyticsEvent
{
public TDUpdatableEvent(string eventName, Dictionary properties, string eventId) : base(eventName, properties)
{
EventType = Type.UPDATABLE;
ExtraId = eventId;
}
}
///
/// 可被重写的事件。请咨询数数客户成功获取支持。
///
public class TDOverWritableEvent : ThinkingAnalyticsEvent
{
public TDOverWritableEvent(string eventName, Dictionary properties, string eventId) : base(eventName, properties)
{
EventType = Type.OVERWRITABLE;
ExtraId = eventId;
}
}
///
/// 预置属性
///
public class TDPresetProperties
{
public static string BundleId
{
get {return (string)TDPresetProperties.PresetProperties["#bundle_id"];}
}
public static string Carrier
{
get {return (string)TDPresetProperties.PresetProperties["#carrier"];}
}
public static string DeviceId
{
get {return (string)TDPresetProperties.PresetProperties["#device_id"];}
}
public static string DeviceModel
{
get {return (string)TDPresetProperties.PresetProperties["#device_model"];}
}
public static string Manufacturer
{
get {return (string)TDPresetProperties.PresetProperties["#manufacturer"];}
}
public static string NetworkType
{
get {return (string)TDPresetProperties.PresetProperties["#network_type"];}
}
public static string OS
{
get {return (string)TDPresetProperties.PresetProperties["#os"];}
}
public static string OSVersion
{
get {return (string)TDPresetProperties.PresetProperties["#os_version"];}
}
public static int ScreenHeight
{
get {return (int)TDPresetProperties.PresetProperties["#screen_height"];}
}
public static int ScreenWidth
{
get {return (int)TDPresetProperties.PresetProperties["#screen_width"];}
}
public static string SystemLanguage
{
get {return (string)TDPresetProperties.PresetProperties["#system_language"];}
}
public static double ZoneOffset
{
get {return (double)TDPresetProperties.PresetProperties["#zone_offset"];}
}
public static Dictionary PresetProperties
{
get { return TDPresetProperties.ToEventPresetProperties();}
}
// 返回事件预置属性的Key以"#"开头,不建议直接作为事件的Property使用
public static Dictionary ToEventPresetProperties()
{
return ThinkingAnalyticsAPI.GetPresetProperties();
}
}
// 自动采集事件类型
[Flags]
public enum AUTO_TRACK_EVENTS
{
NONE = 0,
APP_START = 1 << 0, // 当应用进入前台的时候触发上报,对应 ta_app_start
APP_END = 1 << 1, // 当应用进入后台的时候触发上报,对应 ta_app_end
APP_CRASH = 1 << 4, // 当出现未捕获异常的时候触发上报,对应 ta_app_crash
APP_INSTALL = 1 << 5, // 应用安装后首次打开的时候触发上报,对应 ta_app_install
ALL = APP_START | APP_END | APP_INSTALL | APP_CRASH,
NO_CRASH = APP_START | APP_END | APP_INSTALL
}
public class ThinkingAnalyticsAPI : MonoBehaviour, TaExceptionHandler
{
#region settings
[System.Serializable]
public struct Token
{
public string appid;
public string serverUrl;
public TAMode mode;
public TATimeZone timeZone;
public string timeZoneId;
public Token(string appId, string serverUrl, TAMode mode, TATimeZone timeZone, string timeZoneId = null)
{
appid = appId;
this.serverUrl = serverUrl;
this.mode = mode;
this.timeZone = timeZone;
this.timeZoneId = timeZoneId;
}
public string getTimeZoneId()
{
switch (timeZone)
{
case TATimeZone.UTC:
return "UTC";
case TATimeZone.Asia_Shanghai:
return "Asia/Shanghai";
case TATimeZone.Asia_Tokyo:
return "Asia/Tokyo";
case TATimeZone.America_Los_Angeles:
return "America/Los_Angeles";
case TATimeZone.America_New_York:
return "America/New_York";
case TATimeZone.Other:
return timeZoneId;
default:
break;
}
return null;
}
}
public enum TATimeZone
{
Local,
UTC,
Asia_Shanghai,
Asia_Tokyo,
America_Los_Angeles,
America_New_York,
Other = 100
}
public enum TAMode
{
NORMAL = 0,
DEBUG = 1,
DEBUG_ONLY = 2
}
public enum NetworkType
{
DEFAULT = 1,
WIFI = 2,
ALL = 3
}
[Header("Configuration")]
[Tooltip("是否打开 Log")]
public bool enableLog = true;
[Tooltip("设置网络类型")]
public NetworkType networkType = NetworkType.DEFAULT;
[Header("Project")]
[Tooltip("项目相关配置, APP ID 会在项目申请时给出")]
[HideInInspector]
public Token[] tokens = new Token[1];
#endregion
public readonly string VERSION = "2.2.1";
private static ThinkingAnalyticsAPI taAPIInstance;
//配置Xcode选项
#if UNITY_EDITOR
//[PostProcessBuild]
[PostProcessBuildAttribute(88)]
public static void onPostProcessBuild(BuildTarget target, string targetPath)
{
// if (target != BuildTarget.iOS)
// {
// Debug.LogWarning("Target is not iPhone. XCodePostProcess will not run");
// return;
// }
// string projPath = Path.GetFullPath(targetPath) + "/Unity-iPhone.xcodeproj/project.pbxproj";
// UnityEditor.iOS.Xcode.PBXProject proj = new UnityEditor.iOS.Xcode.PBXProject();
// proj.ReadFromFile(projPath);
//#if UNITY_2019_3_OR_NEWER
// string targetGuid = proj.GetUnityFrameworkTargetGuid();
//#else
// string targetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
//#endif
// //Build Property
// proj.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");//BitCode NO
// proj.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");//Enable Objective-C Exceptions
// string[] headerSearchPathsToAdd = { "$(SRCROOT)/Libraries/Plugins/iOS/ThinkingSDK/Source/main", "$(SRCROOT)/Libraries/Plugins/iOS/ThinkingSDK/Source/common" };
// proj.UpdateBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", headerSearchPathsToAdd, null);// Header Search Paths
// //Add Frameworks
// proj.AddFrameworkToProject(targetGuid, "WebKit.framework", true);
// proj.AddFrameworkToProject(targetGuid, "CoreTelephony.framework", true);
// proj.AddFrameworkToProject(targetGuid, "SystemConfiguration.framework", true);
// proj.AddFrameworkToProject(targetGuid, "Security.framework", true);
// //Add Lib
// proj.AddFileToBuild(targetGuid, proj.AddFile("usr/lib/libsqlite3.tbd", "libsqlite3.tbd", PBXSourceTree.Sdk));
// proj.AddFileToBuild(targetGuid, proj.AddFile("usr/lib/libz.tbd", "libz.tbd", PBXSourceTree.Sdk));
// proj.WriteToFile(projPath);
}
#endif
///
/// 设置自定义访客 ID,用于替换系统生成的访客 ID
///
/// 访客 ID
/// 项目 ID(可选)
public static void Identify(string FIRSTId, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Identify(FIRSTId);
}
}
///
/// 返回当前的访客 ID.
///
/// 访客 ID
/// 项目 ID(可选)
public static string GetDistinctId(string appId = "")
{
if (tracking_enabled)
{
return getInstance(appId).GetDistinctId();
}
return null;
}
///
/// 设置账号 ID. 该方法不会上传用户登录事件.
///
/// 账号 ID
/// 项目 ID(可选)
public static void Login(string account, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Login(account);
}
}
///
/// 清空账号 ID. 该方法不会上传用户登出事件.
///
/// 项目 ID(可选)
public static void Logout(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Logout();
}
}
///
/// 主动触发上报缓存事件到服务器.
///
/// 项目 ID(可选)
public static void Flush(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Flush();
}
}
///
/// 开启自动采集功能.
///
/// 项目 ID(可选)
public static void EnableAutoTrack(AUTO_TRACK_EVENTS events, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).EnableAutoTrack(events);
//C#异常捕获提前,包含所有端
if ((events & AUTO_TRACK_EVENTS.APP_CRASH) != 0)
{
ThinkingSDKExceptionHandler eHandler = new ThinkingSDKExceptionHandler();
eHandler.SetTaExceptionHandler(taAPIInstance);
eHandler.RegisterTaExceptionHandler();
}
}
}
/// 异常捕获回调
public void InvokeTaExceptionHandler(string eventName, Dictionary properties)
{
Track(eventName, properties);
}
///
/// track 简单事件. 该事件会先缓存在本地,达到触发上报条件或者主动调用 Flush 时会上报到服务器.
///
/// 事件名称
/// 项目 ID(可选)
public static void Track(string eventName, string appId = "")
{
Track(eventName, null, appId);
}
///
/// track 事件及事件属性. 该事件会先缓存在本地,达到触发上报条件或者主动调用 Flush 时会上报到服务器.
///
/// 事件名称
/// Properties
/// 项目 ID(可选)
public static void Track(string eventName, Dictionary properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Track(eventName, properties);
}
}
///
/// track 事件及事件属性,并指定 #event_time 属性. 该事件会先缓存在本地,达到触发上报条件或者主动调用 Flush 时会上报到服务器. 从 v1.3.0 开始,会考虑 date 的时区信息。支持 UTC 和 local 时区.
///
/// 事件名称
/// 事件属性
/// 事件时间
/// 项目 ID(可选)
public static void Track(string eventName, Dictionary properties, DateTime date, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Track(eventName, properties, date);
}
}
public static void Track(ThinkingAnalyticsEvent analyticsEvent, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).Track(analyticsEvent);
}
}
///
/// 设置公共事件属性. 公共事件属性指的就是每个事件都会带有的属性.
///
/// 公共事件属性
/// 项目 ID(可选)
public static void SetSuperProperties(Dictionary superProperties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).SetSuperProperties(superProperties);
}
}
///
/// 删除某个公共事件属性.
///
/// 属性名称
/// 项目 ID(可选)
public static void UnsetSuperProperty(string property, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UnsetSuperProperty(property);
}
}
///
/// 返回当前公共事件属性.
///
/// 公共事件属性
/// 项目 ID(可选)
public static Dictionary GetSuperProperties(string appId = "")
{
if (tracking_enabled)
{
return getInstance(appId).GetSuperProperties();
}
return null;
}
///
/// 清空公共事件属性.
///
/// 项目 ID(可选)
public static void ClearSuperProperties(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).ClearSuperProperty();
}
}
///
/// 返回事件预置属性,Key以"#"开头,不建议直接作为事件的Property使用
///
/// 事件预置属性
/// 项目 ID(可选)
public static Dictionary GetPresetProperties(string appId = "")
{
if (tracking_enabled)
{
return getInstance(appId).GetPresetProperties();
}
return null;
}
///
/// 记录事件时长. 调用 TimeEvent 为某事件开始计时,当 track 传该事件时,SDK 会在在事件属性中加入 #duration 这一属性来表示事件时长,单位为秒.
///
/// 事件名称
/// 项目 ID(可选)
public static void TimeEvent(string eventName, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).TimeEvent(eventName);
}
}
///
/// 设置用户属性. 该接口上传的属性将会覆盖原有的属性值.
///
/// 用户属性
/// 项目 ID(可选)
public static void UserSet(Dictionary properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserSet(properties);
}
}
///
/// 设置用户属性. 该接口上传的属性将会覆盖原有的属性值.
///
/// 用户属性
/// 用户属性设置的时间
/// 项目 ID(可选)
public static void UserSet(Dictionary properties, DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserSet(properties, dateTime);
}
}
///
/// 重置一个用户属性.
///
/// 用户属性名称
/// 项目 ID(可选)
public static void UserUnset(string property, string appId = "")
{
List properties = new List();
properties.Add(property);
UserUnset(properties, appId);
}
///
/// 重置一组用户属性
///
/// 用户属性列表
/// 项目 ID(可选)
public static void UserUnset(List properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserUnset(properties);
}
}
///
/// 重置一组用户属性, 并指定操作时间
///
/// 用户属性列表
/// 操作时间
/// 项目 ID(可选)
public static void UserUnset(List properties, DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserUnset(properties, dateTime);
}
}
///
/// 设置用户属性. 当该属性之前已经有值的时候,将会忽略这条信息.
///
/// 用户属性
/// 项目 ID(可选)
public static void UserSetOnce(Dictionary properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserSetOnce(properties);
}
}
///
/// 设置用户属性. 当该属性之前已经有值的时候,将会忽略这条信息.
///
/// 用户属性
/// 操作时间
/// 项目 ID(可选)
public static void UserSetOnce(Dictionary properties, DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserSetOnce(properties, dateTime);
}
}
///
/// 对数值类用户属性进行累加. 如果该属性还未被设置,则会赋值 0 后再进行计算.
///
/// 属性名称
/// 数值
/// 项目 ID(可选)
public static void UserAdd(string property, object value, string appId = "")
{
Dictionary properties = new Dictionary()
{
{ property, value }
};
UserAdd(properties, appId);
}
///
/// 对数值类用户属性进行累加. 如果属性还未被设置,则会赋值 0 后再进行计算.
///
/// 用户属性
/// 项目 ID(可选)
public static void UserAdd(Dictionary properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserAdd(properties);
}
}
///
/// 对数值类用户属性进行累加. 如果属性还未被设置,则会赋值 0 后再进行计算.
///
/// 用户属性
/// 操作时间
/// 项目 ID(可选)
public static void UserAdd(Dictionary properties, DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserAdd(properties, dateTime);
}
}
///
/// 对 List 类型的用户属性进行追加.
///
/// 用户属性
/// 项目 ID(可选)
public static void UserAppend(Dictionary properties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserAppend(properties);
}
}
///
/// 对 List 类型的用户属性进行追加.
///
/// 用户属性
/// 操作时间
/// 项目 ID(可选)
public static void UserAppend(Dictionary properties, DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserAppend(properties, dateTime);
}
}
///
/// 删除用户数据. 之后再查询该名用户的用户属性,但该用户产生的事件仍然可以被查询到
///
/// 项目 ID(可选)
public static void UserDelete(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserDelete();
}
}
///
/// 删除用户数据并指定操作时间.
///
/// 项目 ID(可选)
public static void UserDelete(DateTime dateTime, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).UserDelete(dateTime);
}
}
///
/// 设置允许上报数据到服务器的网络类型.
///
/// 网络类型
/// 项目 ID(可选)
public static void SetNetworkType(NetworkType networkType, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).SetNetworkType(networkType);
}
}
///
/// Gets the device identifier.
///
/// The device identifier.
public static string GetDeviceId()
{
if (tracking_enabled)
{
return getInstance("").GetDeviceId();
}
return null;
}
///
/// Sets the dynamic super properties.
///
/// Dynamic super properties interface.
/// App ID (optional).
public static void SetDynamicSuperProperties(IDynamicSuperProperties dynamicSuperProperties, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).SetDynamicSuperProperties(dynamicSuperProperties);
}
}
///
/// 停止上报数据,并且清空本地缓存数据(未上报的数据、已设置的访客ID、账号ID、公共属性)
///
/// 项目ID
public static void OptOutTracking(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).OptOutTracking();
}
}
///
/// 停止上报数据,清空本地缓存数据,并且发送 user_del 到服务端.
///
/// 项目ID
public static void OptOutTrackingAndDeleteUser(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).OptOutTrackingAndDeleteUser();
}
}
///
/// 恢复上报数据
///
/// 项目ID
public static void OptInTracking(string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).OptInTracking();
}
}
///
/// 暂停/恢复上报数据,本地缓存不会被清空
///
/// 是否打开上报数据
/// 项目ID
public static void EnableTracking(bool enabled, string appId = "")
{
if (tracking_enabled)
{
getInstance(appId).EnableTracking(enabled);
}
}
///
/// 创建轻量级实例,轻量级实例与主实例共享项目ID. 访客ID、账号ID、公共属性不共享
///
/// 项目ID
/// 轻量级实例的 token
public static string CreateLightInstance(string appId = "") {
if (tracking_enabled)
{
ThinkingAnalyticsWrapper lightInstance = getInstance(appId).CreateLightInstance();
instance_lock.EnterWriteLock();
try
{
sInstances.Add(lightInstance.GetAppId(), lightInstance);
} finally
{
instance_lock.ExitWriteLock();
}
return lightInstance.GetAppId();
}
else
{
return null;
}
}
///
/// 传入时间戳校准 SDK 时间.
///
/// 当前 Unix timestamp, 单位 毫秒
public static void CalibrateTime(long timestamp)
{
ThinkingAnalyticsWrapper.CalibrateTime(timestamp);
}
///
/// 传入 NTP Server 地址校准 SDK 时间.
///
/// 您可以根据您用户所在地传入访问速度较快的 NTP Server 地址, 例如 time.asia.apple.com
/// SDK 默认情况下会等待 3 秒,去获取时间偏移数据,并用该偏移校准之后的数据.
/// 如果在 3 秒内未因网络原因未获得正确的时间偏移,本次应用运行期间将不会再校准时间.
///
/// 可用的 NTP 服务器地址
public static void CalibrateTimeWithNtp(string ntpServer)
{
ThinkingAnalyticsWrapper.CalibrateTimeWithNtp(ntpServer);
}
//多实例场景,设置默认的appid
public static void setDefaultAppid(string appid)
{
if (sInstances.Count > 0 && sInstances.ContainsKey(appid))
{
default_appid = appid;
}
}
#region internal
void Awake()
{
taAPIInstance = this;
#if DISABLE_TA
tracking_enabled = false;
#endif
TD_Log.EnableLog(enableLog);
ThinkingAnalyticsWrapper.SetVersionInfo(VERSION);
if (TA_instance == null)
{
DontDestroyOnLoad(gameObject);
TA_instance = this;
} else
{
Destroy(gameObject);
return;
}
if (tracking_enabled)
{
default_appid = tokens[0].appid;
instance_lock.EnterWriteLock();
try
{
ThinkingAnalyticsWrapper.EnableLog(enableLog);
foreach (Token token in tokens)
{
//Debug.Log("token:"+token.appid);
if (!string.IsNullOrEmpty(token.appid))
{
ThinkingAnalyticsWrapper wrapper = new ThinkingAnalyticsWrapper(token);
wrapper.SetNetworkType(networkType);
sInstances.Add(token.appid,wrapper);
}
}
}
finally
{
instance_lock.ExitWriteLock();
}
if (sInstances.Count == 0)
{
tracking_enabled = false;
}
//else
//{
// getInstance(default_appid).SetNetworkType(networkType);
//}
}
}
private static ThinkingAnalyticsAPI TA_instance;
private static string default_appid; // 如果用户调用接口时不指定项目 ID,默认使用第一个项目 ID
private static bool tracking_enabled = true;
private static ReaderWriterLockSlim instance_lock = new ReaderWriterLockSlim();
private static readonly Dictionary sInstances =
new Dictionary();
private static ThinkingAnalyticsWrapper getInstance(string appid)
{
instance_lock.EnterReadLock();
try
{
if (sInstances.Count > 0 && sInstances.ContainsKey(appid))
{
return sInstances[appid];
}
return sInstances[default_appid];
} finally
{
instance_lock.ExitReadLock();
}
}
#endregion
}
}